柚子快報邀請碼778899分享:MybatisPlus功能使用
版本與基礎(chǔ)依賴
MyBatisX插件安裝使用
在File->Settings->Plugins下載插件
點擊右側(cè)DataBase選擇對應(yīng)數(shù)據(jù)庫類型
按照數(shù)據(jù)庫信息填寫相關(guān)連接參數(shù),TestConnection測試連接
點擊 0 of 13
勾選數(shù)據(jù)庫
選擇對應(yīng)的表,右鍵生成
自定義語句
除了使用默認生成的BaseMapper之外,也可以向Mybatis一樣在xml添加自定義sql
Mapper.xml
mp默認生成是空的,可以調(diào)用BaseMapper來使用默認sql操作。
這里我們添加自定義查詢語句
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select code,deleted,name,
age,sex,job from pertwo
where code = #{code}
Mapper接口
在mapper.java中添加xml對應(yīng)的方法
import com.baomidou.entity.Pertwo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Component;
//注意將泛型換成實體類
public interface PertwoMapper extends BaseMapper
//自定義語句
Pertwo selectCode(String code);
}
業(yè)務(wù)接口
如果只用mapper的java接口進行調(diào)用,不需要用service,此處可以不寫,就用默認生成的就行
import com.baomidou.entity.Pertwo;
import com.baomidou.mybatisplus.extension.service.IService;
public interface PertwoService extends IService
Pertwo selectCode(String code);
}
業(yè)務(wù)接口實現(xiàn)類
如果只用mapper的java接口進行調(diào)用,不需要用service,此處可以不寫,就用默認生成的就行
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.entity.Pertwo;
import com.baomidou.service.PertwoService;
import com.baomidou.dao.PertwoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PertwoServiceImpl extends ServiceImpl
implements PertwoService{
@Autowired
private PertwoMapper pw;
@Override
public Pertwo selectCode(String code) {
return pw.selectCode(code);
}
}
測試調(diào)用
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private PertwoServiceImpl pertwoServiceImpl;
@RequestMapping("/qpt")
public Pertwo qst(@RequestParam String code){
Pertwo p = pertwoServiceImpl.selectCode(code);
return p;
}
}
主鍵生成策略詳解
NONE(無狀態(tài))
如果使用 IdType.NONE 策略,表示未設(shè)置主鍵類型,會使用默認雪花算法,如果手動賦值,則為手動賦予的值
@TableId(type=IdType.NONE)
@TableName(value ="person")
public class Person implements Serializable {
/**
*
*/
@TableId(type=IdType.NONE)
private String id;
/**
*
*/
private String name;
/**
*
*/
private Integer age;
}
service新增
@RequestMapping("/ins")
public String insUser(){
User user = new User();
//p.setId("lyh"); //如果此處放開,則id為lyh
user.setAge(2);
user.setName("阿比西尼亞");
user.setEmail("12222@qq.com");
iUserService.save(user);
return "success";
}
結(jié)果
自動:
手動設(shè)置:
ASSIGN_UUID(不含中劃線的UUID)
@TableId(type = IdType.ASSIGN_UUID)
@TableName(value ="person")
public class Person implements Serializable {
/**
*
*/
@TableId(type = IdType.ASSIGN_UUID)
private String code;
/**
*
*/
private String name;
/**
*
*/
private Integer age;
/**
*
*/
private String sex;
/**
*
*/
private String job;
}
service新增
如果手動設(shè)置,則結(jié)果為手動值,與none的一樣
@RequestMapping("/inp")
public String insPer(){
Person p = new Person();
//p.setCode("lyh");//此處放開則code為lyh
p.setSex("女");
p.setName("布偶");
p.setAge(3);
p.setJob("pet");
personService.save(p);
return "success";
}
結(jié)果
ASSIGN_ID(雪花算法)
@TableId(type = IdType.ASSIGN_ID)
@TableName(value ="person")
public class Person implements Serializable {
/**
*
*/
@TableId(type = IdType.ASSIGN_ID)
private String code;
/**
*
*/
private String name;
/**
*
*/
private Integer age;
/**
*
*/
private String sex;
/**
*
*/
private String job;
}
service新增
如果手動設(shè)置,則結(jié)果為手動值,與none的一樣
@RequestMapping("/inp")
public String insPer(){
Person p = new Person();
//p.setCode("lyh");//此處放開則code為lyh
p.setSex("男");
p.setName("bigworth");
p.setAge(25);
p.setJob("boss");
personService.save(p);
return "success";
}
結(jié)果
Input(自定義輸入策略)
@TableId(type = IdType.INPUT)
@TableName(value ="person")
public class Person implements Serializable {
/**
*
*/
@TableId(type = IdType.INPUT)
private String code;
/**
*
*/
private String name;
/**
*
*/
private Integer age;
/**
*
*/
private String sex;
/**
*
*/
private String job;
}
service新增
@RequestMapping("/inp")
public String insPer(){
Person p = new Person();
p.setCode("lyh");
p.setSex("女");
p.setName("gril");
p.setAge(18);
p.setJob("boss");
personService.save(p);
return "success";
}
結(jié)果
AUTO(自動增長策略)
@TableId(type = IdType.AUTO)
需要設(shè)置主鍵自增且為數(shù)字類型
public class User implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主鍵ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 姓名
*/
private String name;
/**
* 年齡
*/
private Integer age;
/**
* 郵箱
*/
private String email;
}
service新增
@RequestMapping("/ins")
public String insUser(){
User user = new User();
user.setAge(2);
user.setName("豹貓");
user.setEmail("66666@qq.com");
iUserService.save(user);
return "success";
}
結(jié)果
從21到23主鍵增加
注意
ASSIGN_ID,ASSIGN_UUID,NONE三種狀態(tài),如果代碼里手動設(shè)置了主鍵值,則以手動設(shè)置為主
邏輯刪除
添加一個字段用作刪除標記,比如1為已刪除,0為未刪除。
執(zhí)行刪除操作時,實際是更新,將標記字段變?yōu)?,而不是真正的刪除執(zhí)行查詢操作時,查標記為0的數(shù)據(jù)執(zhí)行更新時,更新標記為0的數(shù)據(jù)支持所有數(shù)據(jù)類型(推薦使用 Integer,Boolean,LocalDateTime)
配置
建表,注意新增數(shù)據(jù)操作,mp的邏輯刪除不會對標記字段做額外操作,需要自己設(shè)置默認值
drop table pertwo;
CREATE TABLE `pertwo` (
`deleted` int DEFAULT 0 COMMENT '是否被刪除 0 未刪除 1 已刪除', --設(shè)置默認值為0,代表未刪除
`name` varchar(255) DEFAULT NULL,
`age` int DEFAULT NULL,
`sex` varchar(255) DEFAULT NULL,
`code` varchar(255) NOT NULL,
`job` varchar(255) DEFAULT NULL,
PRIMARY KEY (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
yaml
#mybatis plus是獨立節(jié)點,需要單獨配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局邏輯刪除的實體字段名,也可實體類字段上加上@TableLogic注解
logic-not-delete-value: 0 # 邏輯未刪除值
logic-delete-value: 1 # 邏輯已刪除值
代碼測試
先新增一條數(shù)據(jù)
@RequestMapping("/inpt")
public String inst(){
Pertwo p = new Pertwo();
p.setCode("lyh");
p.setSex("女");
p.setName("gril");
p.setAge(18);
p.setJob("boss");
pertwoServiceImpl.save(p);
return "success";
}
執(zhí)行結(jié)果,因為建表時設(shè)置刪除標記字段默認值為0.新增后deleted值為0
再執(zhí)行更新操作,saveOrUpdate方法按主鍵進行更新
@RequestMapping("/uppt")
public String upst(){
Pertwo p = new Pertwo();
p.setCode("lyh");
p.setSex("女");
p.setName("gril");
p.setAge(18);
p.setJob("super boss");
pertwoServiceImpl.saveOrUpdate(p);
return "success";
}
操作結(jié)果,看見語句日志自動多了AND deleted=0,代表只更新未刪除數(shù)據(jù)
再執(zhí)行刪除操作
利用Wrapper構(gòu)造條件
@RequestMapping("/dept")
public String upst(){
pertwoServiceImpl.remove(new QueryWrapper
return "success";
}
操作結(jié)果,看見語句日志自動多了AND deleted=0,并且刪除改為update更新
最后再查詢
@RequestMapping("/qupt")
public Pertwo qust(){
Pertwo p = pertwoServiceImpl.getOne(new QueryWrapper
return p;
}
結(jié)果因為上面進行邏輯刪除了,deleted=1,所以查不到
通用枚舉
直接在代碼中使用枚舉常量,數(shù)據(jù)庫自動變?yōu)槠鋵?yīng)的枚舉值
配置
定義枚舉類
3.5.2 版本以前:
需要重寫getValue()方法來獲取值,@JsonValue用于前端正常返回json格式數(shù)據(jù)
import com.baomidou.mybatisplus.core.enums.IEnum;
import com.fasterxml.jackson.annotation.JsonValue;
public enum AgeEnum implements IEnum
ONE(1, "一歲"),
TWO(2, "二歲"),
THREE(3, "三歲");
private int value;
private String desc;
AgeEnum(int i, String age) {
this.value = i;
this.desc = age;
}
@Override
public Integer getValue() {
return this.value;
}
@JsonValue
public String getDesc(){
return this.desc;
}
}
從 3.5.2 版本開始:
不需要實現(xiàn)IEnum接口;
@EnumValue加在數(shù)值字段上,也就是需要存入數(shù)據(jù)庫的字段@JsonValue加在文字字段上,也就是用于展示,以及需要返回前端的json格式數(shù)據(jù)
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
public enum AgeEnum
{
ONE(1, "一歲"),
TWO(2, "二歲"),
THREE(3, "三歲");
/**
* 數(shù)據(jù)庫存放數(shù)值,前端展示漢字
* */
@JsonValue
private String desc;
@EnumValue
private int value;
/**
* get、set方法及構(gòu)造還要有
* */
AgeEnum(int i, String age) {
this.value = i;
this.desc = age;
}
public Integer getValue() {
return this.value;
}
public String getDesc(){
return this.desc;
}
}
實體類添加枚舉屬性
@TableName(value ="pertwo")
public class Pertwo implements Serializable {
@TableId
private String code;
private String name;
//省略其他屬性
.........
/**
* 枚舉屬性,包括get與set方法
*/
private AgeEnum age;
public AgeEnum getAge() {
return age;
}
public void setAge(AgeEnum age) {
this.age = age;
}
}
yaml配置
3.5.2 版本開始不需要配這個
配置枚舉類包掃描
#mybatis plus是獨立節(jié)點,需要單獨配置
mybatis-plus:
#掃描枚舉類所在包
type-enums-package: com.baomidou.Enum # 支持統(tǒng)配符 * 或者 ; 分割
代碼測試
新增
@RequestMapping("/inpt")
public String inst(){
Pertwo p = new Pertwo();
p.setCode("lyh");
p.setSex("女");
p.setName("gril");
p.setJob("boss");
p.setAge(AgeEnum.THREE);//直接賦值枚舉
pertwoServiceImpl.save(p);
return "success";
}
因為重寫了getValue()方法,數(shù)據(jù)庫顯示為枚舉的數(shù)字value
@JsonValue前端顯示
@RequestMapping("/qupt")
public Pertwo qust(){
Pertwo p = pertwoServiceImpl.getOne(new QueryWrapper
return p;
}
頁面查詢結(jié)果
如果不用@JsonValue,顯示為:
使用枚舉作為條件構(gòu)造查詢
@RequestMapping("/mupt")
public Pertwo must(){
Pertwo p = pertwoServiceImpl.getOne(new QueryWrapper
return p;
}
頁面顯示
字段類型處理器
類型處理器,用于 JavaType 與 JdbcType 之間的轉(zhuǎn)換,用于 PreparedStatement 設(shè)置參數(shù)值和從 ResultSet 或 CallableStatement 中取出一個值
配置類型轉(zhuǎn)換
實體類@TableName(autoResultMap = true)與@TableField(typeHandler = *.class)必須加
//實體類Entity
@TableName(autoResultMap = true)
public class Pertwo implements Serializable {
/**
* 要進行json類型轉(zhuǎn)換的字段:job
* 轉(zhuǎn)換器:FastjsonTypeHandler
*/
@TableField(typeHandler = FastjsonTypeHandler.class)
private Map
public Map
return job;
}
public void setJob(Map
this.job = job;
}
}
如果mapper.xml有resultMap且類型不對要去掉,或者添加typeHandler標簽
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
typeHandler="com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler"/> code,deleted,name, age,sex,job 測試代碼 @RequestMapping("/inpt") public String inst(){ Pertwo p = new Pertwo(); p.setCode("lyh"); p.setSex("女"); p.setName("gril"); //map變?yōu)閖son字符串存入數(shù)據(jù)庫 Map contact.put("phone", "010-1234567"); contact.put("tel", "13388889999"); contact.put("job", "boss"); p.setJob(contact); p.setAge(AgeEnum.THREE); pertwoServiceImpl.save(p); return "success"; } 執(zhí)行結(jié)果 自定義類型處理器 TypeHandler 自定義類型處理器需要實現(xiàn)TypeHandler TypeHandler擁有很多實現(xiàn) 代碼 這里實現(xiàn)一個數(shù)據(jù)庫vachar字符串轉(zhuǎn)實體屬性List 定義轉(zhuǎn)換器 import lombok.extern.java.Log; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import org.apache.ibatis.type.TypeHandler; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.List; @MappedJdbcTypes(JdbcType.VARCHAR) //數(shù)據(jù)庫類型 @MappedTypes({List.class}) //java數(shù)據(jù)類型 @Log public class ListTypeHandler implements TypeHandler @Override public void setParameter(PreparedStatement ps, int i, List throws SQLException { String hobbys = dealListToOneStr(parameter); ps.setString(i , hobbys); } /** * 集合拼接字符串 * @param parameter * @return */ private String dealListToOneStr(List if(parameter == null || parameter.size() <=0) { return null; } String res = ""; for (int i = 0 ;i < parameter.size(); i++) { if(i == parameter.size()-1){ res+=parameter.get(i); return res; } res+=parameter.get(i)+","; } return null; } @Override public List throws SQLException { log.info("method ====>>> getResult(ResultSet rs, String columnName)"); return Arrays.asList(rs.getString(columnName).split(",")); } @Override public List throws SQLException { log.info("method ====>>> getResult(ResultSet rs, int columnIndex)"); return Arrays.asList(rs.getString(columnIndex).split(",")); } @Override public List log.info("method ====>>> getResult(CallableStatement cs, int columnIndex)"); String hobbys = cs.getString(columnIndex); return Arrays.asList(hobbys.split(",")); } } 實體類添加typeHandler 將上面的typeHandler實現(xiàn)加入實體類注解 //實體類Entity @TableName(autoResultMap = true) public class Pertwo implements Serializable { /** * 轉(zhuǎn)換器:ListTypeHandler */ @TableField(typeHandler = ListTypeHandler.class) private Map public Map return job; } public void setJob(Map this.job = job; } } 注意,如果mapper.xml沒有使用默認的xml(MybatisPlus默認生成的xml里面是空的),而是自己添加了自定義語句,則需要進行映射配置,否則查詢結(jié)果該字段為null。 如下,實體類job屬性是List: 數(shù)據(jù)庫中的job字段值為: mapper.xml中添加了自定義查詢id=“selectCode”,這種是無法使用中的標簽進行拼接的,需要在結(jié)果映射中添加類型轉(zhuǎn)換: PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> select code,deleted,name, age,sex,job from pertwo where code = #{code} typeHandler="com.baomidou.typeHandler.ListTypeHandler"/> 測試 執(zhí)行新增操作 @RequestMapping("/inpt") public String inst(){ Pertwo p = new Pertwo(); p.setCode("lyh"); p.setSex("女"); p.setName("gril"); //list變?yōu)関archar字符串存入數(shù)據(jù)庫 List contact.add("一"); contact.add("個"); contact.add("boss"); p.setJob(contact); p.setAge(AgeEnum.THREE); pertwoServiceImpl.save(p); return "success"; } 數(shù)據(jù)庫顯示: 前端頁面顯示結(jié)果: {"code":"lyh","deleted":0,"name":"gril","age":"三歲","sex":"女","job":["一","個","boss"]} 新增后執(zhí)行查詢操作 // mp自帶默認的查詢方法 @RequestMapping("/mupt") public Pertwo must(){ //此處使用了上面的枚舉 Pertwo p = pertwoServiceImpl.getOne(new QueryWrapper return p; } // 使用xml中自定義的select查詢方法 @RequestMapping("/qpt") public Pertwo qst(@RequestParam String code){ Pertwo p = pertwoServiceImpl.selectCode(code); return p; } 結(jié)果: {"code":"lyh","deleted":0,"name":"gril","age":"三歲","sex":"女","job":["一","個","boss"]} 自動填充 在實際操作過程中,我們并不希望創(chuàng)建時間、修改時間這些來手動進行,而是希望通過自動化來完成,而mybatis-plus則也提供了自動填充功能來實現(xiàn)這一操作 配置 注解 @TableField(fill= FieldFill.*) 默認值是:FieldFill.DEFAULT 值描述DEFAULT默認不處理INSERT插入時填充字段UPDATE更新時填充字段INSERT_UPDATE插入和更新時填充字段 代碼 實體類注解添加 @TableField @TableName(value ="work_publish") public class WorkPublish implements Serializable { ......... @TableField(fill= FieldFill.INSERT_UPDATE) private Date time; .......... } 配置類 FieldFill.INSERT_UPDATE即新增修改都要配置 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import lombok.extern.java.Log; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.util.Date; /** * @Component 表示就是要把處理器 丟到IOC容器中 這一點千萬不能忘記 ! */ @Component @Log public class MyMetaObjectHandler implements MetaObjectHandler { /** * 插入時候的填充策略 * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill"); //注解標記在了time字段上,這里就要給time做處理,為其自動填充當前時間 this.setFieldValByName("time",new Date(),metaObject); //可以寫很多字段的自動填充 //this.setFieldValByName("createTime",new Date(),metaObject); //this.setFieldValByName("updateTime",new Date(),metaObject); } /** * 更新時候的填充策略,與上面類似 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { log.info("start update fill"); this.setFieldValByName("time",new Date(),metaObject); } } 測試 保存實體類時不手動保存時間字段 @RequestMapping("/work2") public String mp2(){ WorkPublish wp = new WorkPublish(); wp.setId("A11112"); wp.setType("2"); wp.setPhone("13230707234"); wp.setDescribe1("填充測試"); wp.setPhoto("/profile/upload/2023/05/02/d434b20437c17ac316f9c29e8e839e4_20230502113157A001.png"); wp.setSeatmap("/profile/baiduMap/陜西省渭南市華陰市華山風景區(qū)玉泉廣場北50米路東.jpg"); wp.setTitle("測試"); workPublishServiceImpl.save(wp); return "success"; } 結(jié)果 時間自動填充 SQL注入器 相當于擴展BaseMapper,添加它沒有的方法 編寫通用mapper接口 findAll可能會有紅線,但不影響使用,不需要加@Component注解 import com.baomidou.entity.Pertwo; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import java.util.List; /** * 通用mapper接口,以后創(chuàng)建其他mapper接口時,不再繼承BaseMapper,而是繼承MyBaseMapper */ public interface MyBaseMapper //查詢所有用戶 public List } 已有mapper繼承通用mapper 不需要加@Component注解 /** * 繼承MyBaseMapper, 自定義通用mapper接口 */ public interface PertwoMapper extends MyBaseMapper } 自定義sql方法類 寫一個自定義sql方法類,此類分版本 3.5.3.1以前的版本: import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; public class FindAll extends AbstractMethod { @Override public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) { String sql = "select * from " + tableInfo.getTableName(); SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); //添加自定義查詢sql return this.addSelectMappedStatementForOther(mapperClass, "findAll", sqlSource, modelClass); } } 3.5.3.1及其以后 import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; public class FindAll extends AbstractMethod { //多了構(gòu)造方法 public FindAll(String methodName) { super(methodName); } @Override public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) { String sql = "select * from " + tableInfo.getTableName(); SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); return this.addSelectMappedStatementForOther(mapperClass, "findAll", sqlSource, modelClass); } } 除了addSelectMappedStatementForOther添加查詢sql外,還有許多其他方法 自定義sql注入器 寫一個sql注入器,加入上面自定義sql方法類 3.5.3.1以前的版本: import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import java.util.List; public class MySqlInjector extends DefaultSqlInjector { @Override public List //獲取框架中已有的注入方法 List //添加自己自定義方法 methodList.add(new FindAll()); return methodList; } } 3.5.3.1及其以后的版本: import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import com.baomidou.mybatisplus.core.metadata.TableInfo; import java.util.List; /** * 自定義sql注入器 */ public class MySqlInjector extends DefaultSqlInjector { @Override public List //獲取框架中已有的注入方法 List //添加自己自定義方法,參數(shù)為自定義方法類中this.addSelectMappedStatementForOther中傳入的方法名 methodList.add(new FindAll("findAll")); return methodList; } } 注入SQL注入器 為spring添加注入器Bean import com.baomidou.injectors.MySqlInjector; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @MapperScan("com.baomidou.dao") //此處可以不加掃描,但需要添加在主啟動類上 public class mpConfig { /*** 自定義SQL注入器 */ @Bean public MySqlInjector mySqlInjector() { return new MySqlInjector(); } } 測試 Controller中注入mapper,可能會有紅線,不影響使用 @Autowired private PertwoMapper mapper; @RequestMapping("/qupt") public void qust(){ List for (Pertwo user : users) { System.out.println("findall: "+user); } } 執(zhí)行SQL分析打印 注意! driver-class-name 為 p6spy 提供的驅(qū)動類url 前綴為 jdbc:p6spy 跟著冒號為對應(yīng)數(shù)據(jù)庫連接地址打印出 sql 為 null,在 excludecategories 增加 commit批量操作不打印 sql,去除 excludecategories 中的 batch批量操作打印重復(fù)的問題請使用 MybatisPlusLogFactory (3.2.1 新增)該插件有性能損耗,不建議生產(chǎn)環(huán)境使用。 步驟 pom依賴引入 yml 配置(示例數(shù)據(jù)庫為mysql) 主要是url:jdbc:p6spy:mysql,以及驅(qū)動:com.p6spy.engine.spy.P6SpyDriver spring: datasource: url: jdbc:p6spy:mysql://localhost:3306/mybatis-plus?nullCatalogMeansCurrent=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: wangziyu123 driver-class-name: com.p6spy.engine.spy.P6SpyDriver #com.mysql.cj.jdbc.Driver spy.properties #3.2.1以上使用 modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory #3.2.1以下使用或者不配置 #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定義日志打印 logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger #日志輸出到控制臺 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 使用日志系統(tǒng)記錄 sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger # 設(shè)置 p6spy driver 代理 deregisterdrivers=true # 取消JDBC URL前綴 useprefix=true #配置記錄 Log 例外,可去掉的結(jié)果集有error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 實際驅(qū)動可多個 #driverlist=org.h2.Driver # 是否開啟慢SQL記錄 outagedetection=true # 慢SQL記錄標準 2 秒 outagedetectioninterval=2 最后執(zhí)行語句測試 效果: 查詢消耗5毫秒 數(shù)據(jù)安全保護 該功能為了保護數(shù)據(jù)庫配置及數(shù)據(jù)安全,在一定的程度上控制開發(fā)人員流動導(dǎo)致敏感信息泄露。 pom引入 版本要大于等于3.3.2 使用自帶AES工具類加密連接參數(shù) 一定要記住并妥善保管密鑰,后續(xù)用于加解密!! import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; //MybatisPlus自帶的AES加密工具 import com.baomidou.mybatisplus.core.toolkit.AES; @SpringBootTest public class SampleTest { @Test public void testSelect() { String url = "jdbc:mysql://localhost:3306/mybatis-plus?nullCatalogMeansCurrent=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"; String username = "root"; String password = "wangziyu123"; // 生成16位密鑰 String randomKey = AES.generateRandomKey(); //密鑰:3b2dc937dc3030a3 System.out.println("密鑰:" + randomKey); // 密鑰加密 String urlRes = AES.encrypt(url, randomKey); //B9NhJxO3oceWf1s76AnubjtGTxD4ZO7yrPs9Pm2yEWe7xXAX15VsHO1Vfuva79dWNHh System.out.println("url加密后:" + urlRes); String unRes = AES.encrypt(username, randomKey); //9MTrYDAx6cCZGsTmo15ZhQ== System.out.println("username加密后:" + unRes); String pasRes = AES.encrypt(password, randomKey); //RM1UQWDB7qzlArC84n232A== System.out.println("password加密后:" + pasRes); } } 將加密后的信息寫入yaml 加密字符串前面要加 mpw: spring: datasource: #加密后的地址 url: mpw:B9NhJxO3oceWf1s76AnubjtGTxD4ZO7yrPs9Pm2yEWe7xXAX15VsHO1Vfuva79dWNHhw3QmVBpGq6cHFRXhos3TaSr+fxq+S2C8+RL8lf+wrsjmQcgA2CxgTfNMEOhA7m1gGxMlPJ0rD11p5t3Bd5L/Eh2w0dNJm/v4l8omS9ozGSKjXBrY6Qy3IHA8LnbqY19bZkdYu06PFCNOvd//MD7fZHfXldSgMbtH2Z/ZG+cwJzh8kXYUPwoxp9pGnW4Sx #加密后的用戶名 username: mpw:9MTrYDAx6cCZGsTmo15ZhQ== #加密后的密碼 password: mpw:RM1UQWDB7qzlArC84n232A== 在springboot的tomcat配置添加啟動參數(shù) --mpw.key=你的密鑰 多數(shù)據(jù)源 pom yaml配置多數(shù)據(jù)源 spring: datasource: dynamic: primary: master #設(shè)置默認的數(shù)據(jù)源或者數(shù)據(jù)源組,默認值即為master strict: false #嚴格匹配數(shù)據(jù)源,默認false. true未匹配到指定數(shù)據(jù)源時拋異常,false使用默認數(shù)據(jù)源 datasource: master: url: jdbc:mysql://localhost:3306/mybatis-plus?nullCatalogMeansCurrent=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: wangziyu123 driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0開始支持SPI可省略此配置 slave_1: url: jdbc:mysql://localhost:3306/mybatis-plus-slave?nullCatalogMeansCurrent=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: wangziyu123 driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0開始支持SPI可省略此配置 #......省略 #以上會配置一個默認庫master,一個子庫slave_1 使用注解@DS(“數(shù)據(jù)源名”) @DS可以加載類上或方法上,可以加在mapper接口,或service接口 import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.entity.Pertwo; import java.util.List; /** * 繼承MyBaseMapper, 自定義通用mapper接口 */ //@Sharding("mysql") public interface PertwoMapper extends MyBaseMapper //查的是yaml中master數(shù)據(jù)庫的數(shù) @DS("master") Pertwo selectCode(String code); //查的是yaml中slave_1數(shù)據(jù)庫的數(shù) @DS("slave_1") List } MP整體插件 MybatisPlusInterceptor 該插件是核心插件,目前代理了 Executor#query 和 Executor#update 和 StatementHandler#prepare 方法 屬性 private List InnerInterceptor 我們提供的插件都將基于此接口來實現(xiàn)功能 目前已有的功能: 自動分頁: PaginationInnerInterceptor多租戶: TenantLineInnerInterceptor動態(tài)表名: DynamicTableNameInnerInterceptor樂觀鎖: OptimisticLockerInnerInterceptorsql 性能規(guī)范: IllegalSQLInnerInterceptor防止全表更新與刪除: BlockAttackInnerInterceptor 注意: 使用多個功能需要注意順序關(guān)系,建議使用如下順序 多租戶,動態(tài)表名分頁,樂觀鎖sql 性能規(guī)范,防止全表更新與刪除 總結(jié): 對 sql 進行單次改造的優(yōu)先放入,不對 sql 進行改造的最后放入 攔截忽略注解 @InterceptorIgnor 屬性名是@InterceptorIgnor中的參數(shù),分別屏蔽不同的插件功能 如:@InterceptorIgnor(tenantLine = “true”) 屬性名類型默認值描述tenantLineString“”行級租戶dynamicTableNameString“”動態(tài)表名blockAttackString“”攻擊 SQL 阻斷解析器,防止全表更新與刪除illegalSqlString“”垃圾 SQL 攔截 分頁插件 注意 版本要求:3.4.0 版本以上 1.分頁配置 import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @MapperScan("com.baomidou.dao")//主啟動類配了這里就可以不配包掃描 public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } } 2.代碼配置 Mapper 使用自動生成的即可 public interface UserMapper extends BaseMapper } Service 在mybatisx自動生成的service實現(xiàn)中添加分頁查詢方法,用在Controller中調(diào)用 @Service public class UserServiceImpl extends ServiceImpl @Autowired private UserMapper userMapper; public List //1為當前頁數(shù),3為每頁顯示條數(shù) Page Page List return records; } } Controller @RestController @RequestMapping("/user") public class UserController { @Autowired private UserServiceImpl ui; //localhost:8080/user/page3 @RequestMapping("/page3") public List return ui.page3(); } } 3.代碼執(zhí)行 建表語句 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '姓名', `age` int NULL DEFAULT NULL COMMENT '年齡', `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '郵箱', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1697138078167941125 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; INSERT INTO `user` VALUES (1, 'Jone', 18, 'test1@baomidou.com'); INSERT INTO `user` VALUES (2, 'Jack', 20, 'test2@baomidou.com'); INSERT INTO `user` VALUES (3, 'Tom', 28, 'test3@baomidou.com'); INSERT INTO `user` VALUES (4, 'Sandy', 21, 'test4@baomidou.com'); INSERT INTO `user` VALUES (5, 'Billie', 24, 'test5@baomidou.com'); INSERT INTO `user` VALUES (1696794424765054978, '貝斯特', 16, '12345@qq.com'); INSERT INTO `user` VALUES (1697138078167941121, '阿比西尼亞', 3, '12222@qq.com'); INSERT INTO `user` VALUES (1697138078167941122, '布偶', 3, '33333@qq.com'); INSERT INTO `user` VALUES (1697138078167941123, '豹貓', 2, '66666@qq.com'); INSERT INTO `user` VALUES (1697138078167941124, '暹羅', 1, '88888@qq.com'); SET FOREIGN_KEY_CHECKS = 1; controller執(zhí)行結(jié)果 http://localhost:8080/user/page3 [{"id":1,"name":"Jone","age":18,"email":"test1@baomidou.com"},{"id":2,"name":"Jack","age":20,"email":"test2@baomidou.com"},{"id":3,"name":"Tom","age":28,"email":"test3@baomidou.com"}] 4.分頁類屬性解析 public class Page private static final long serialVersionUID = 8545996863226528798L; //用于存儲分頁查詢結(jié)果的數(shù)據(jù)列表 protected List //用于表示查詢結(jié)果的總記錄數(shù) protected long total; //用于表示分頁要查詢的 每頁記錄數(shù) protected long size; //表示分頁查詢中的當前頁碼,提供了關(guān)于正在訪問的特定結(jié)果頁的信息 protected long current; //用于存儲排序規(guī)則,這樣可以在查詢時指定多個排序條件,以便按照特定順序?qū)Y(jié)果進行排序。 protected List //mybatisplus分頁Page對象的optimizeCountSql屬性是用來優(yōu)化查詢總記錄數(shù)的功能。默認情況下,當我們使用Page對象進行分頁查詢時,會先執(zhí)行一條查詢總記錄數(shù)的SQL語句,然后再執(zhí)行實際的分頁查詢SQL語句。通過設(shè)置optimizeCountSql屬性為true,可以使得在執(zhí)行查詢總記錄數(shù)時,自動去除掉不必要的order by語句,從而提高查詢總記錄數(shù)的性能。 //需要注意的是,設(shè)置optimizeCountSql屬性為true后,可能會導(dǎo)致查詢總記錄數(shù)的結(jié)果不準確。因為去除了order by語句,可能會導(dǎo)致查詢結(jié)果的排序與實際分頁查詢結(jié)果的排序不一致。所以在使用該屬性時,需要根據(jù)實際情況進行權(quán)衡和選擇。 protected boolean optimizeCountSql; //當searchCount為true時,會在查詢數(shù)據(jù)之前先查詢總記錄數(shù),然后將總記錄數(shù)設(shè)置到Page對象中; //當searchCount為false時,不會進行總記錄數(shù)的查詢,只查詢當前頁的數(shù)據(jù)。 //使用searchCount屬性可以控制是否需要查詢總記錄數(shù),當數(shù)據(jù)量較大時,查詢總記錄數(shù)可能會影響性能,因此可以通過設(shè)置searchCount為false來提高查詢效率。但需要注意的是,當searchCount為false時,Page對象中的total屬性將會是0,即無法獲取到總記錄數(shù)。 protected boolean searchCount; //用來優(yōu)化查詢總記錄數(shù)的屬性 //通常情況下,獲取總記錄數(shù)的SQL語句會包含多個表的關(guān)聯(lián)查詢,這樣的查詢可能會導(dǎo)致性能下降。而通過設(shè)置optimizeJoinOfCountSql屬性為true,MyBatis Plus會將原始的查詢SQL作為子查詢,然后再對子查詢進行count操作,從而避免了多表關(guān)聯(lián)查詢的性能問題。 //需要注意的是,使用該屬性進行優(yōu)化時,需要確保查詢語句中的表關(guān)聯(lián)條件是正確的,否則可能會導(dǎo)致查詢結(jié)果不準確。 protected boolean optimizeJoinOfCountSql; //用來限制每頁查詢的最大記錄數(shù)的 //當設(shè)置了maxLimit屬性后,如果查詢結(jié)果超過了maxLimit指定的值,mybatisplus會自動截取前maxLimit條記錄作為當前頁的數(shù)據(jù)返回,而不會返回超過maxLimit的記錄 //這個屬性的作用是為了防止一次查詢返回過多的數(shù)據(jù),導(dǎo)致內(nèi)存占用過大或者網(wǎng)絡(luò)傳輸壓力過大。通過限制每頁查詢的最大記錄數(shù),可以有效控制查詢結(jié)果的大小,提高系統(tǒng)的性能和穩(wěn)定性。 //需要注意的是,maxLimit屬性只對單次查詢有效,如果需要查詢更多的記錄,可以通過翻頁操作來獲取后續(xù)的數(shù)據(jù)。 protected Long maxLimit; //用來指定統(tǒng)計總記錄數(shù)的SQL語句的id。在進行分頁查詢時,除了需要獲取當前頁的數(shù)據(jù),還需要知道總記錄數(shù)以便于計算總頁數(shù)等信息。countId屬性就是用來指定執(zhí)行統(tǒng)計總記錄數(shù)的SQL語句的id。 //在使用mybatisplus進行分頁查詢時,我們可以通過設(shè)置countId屬性來指定執(zhí)行統(tǒng)計總記錄數(shù)的SQL語句的id。mybatisplus會根據(jù)這個id去執(zhí)行對應(yīng)的SQL語句,并將結(jié)果作為總記錄數(shù)返回。 protected String countId; } 樂觀鎖插件 1.概念 樂觀鎖和悲觀鎖都是用于解決并發(fā)場景下的數(shù)據(jù)競爭問題,但是卻是兩種完全不同的思想。它們的使用非常廣泛,也不局限于某種編程語言或數(shù)據(jù)庫。 樂觀鎖的概念: 指的是在操作數(shù)據(jù)的時候樂觀地認為別人不會同時修改數(shù)據(jù),因此默認不上鎖,只有在執(zhí)行更新的時候才會去判斷在此期間別人是否修改了數(shù)據(jù),如果別人修改了數(shù)據(jù)則放棄操作,否則執(zhí)行操作。 ? 沖突比較少的時候, 使用樂觀鎖(沒有悲觀鎖那樣耗時的開銷) ,由于樂觀鎖的不上鎖特性,所以在性能方面要比悲觀鎖好,比較適合用在DB的讀大于寫的業(yè)務(wù)場景 悲觀鎖的概念: 指的是在操作數(shù)據(jù)的時候悲觀地認為別人一定會同時修改數(shù)據(jù),因此直接把數(shù)據(jù)上鎖,直到操作完成之后才會釋放鎖,在上鎖期間其他人不能操作數(shù)據(jù)。 ? 沖突比較多的時候, 使用悲觀鎖(沒有樂觀鎖那么多次的嘗試),對于每一次數(shù)據(jù)修改都要上鎖,如果在DB讀取需要比較大的情況,有線程執(zhí)行了修改操作,會導(dǎo)致上鎖讀不出來,等修改線程釋放了鎖才能讀到數(shù)據(jù),體驗極差。所以比較適合用在DB寫大于讀的情況 總結(jié): 讀取頻繁使用樂觀鎖,寫入頻繁使用悲觀鎖。 樂觀鎖插件: 當要更新一條記錄的時候,希望這條記錄沒有被別人更新 樂觀鎖實現(xiàn)方式: 取出記錄時,獲取當前 version更新時,帶上這個 version執(zhí)行更新時, set version = newVersion where version = oldVersion如果 version 不對,就更新失敗 2.配置 配置類 import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //分頁 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //樂觀鎖 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } } 實體類 數(shù)據(jù)庫加version字段 實體類加version字段與@Version注解 import com.baomidou.mybatisplus.annotation.*; /** * 樂觀鎖版本號 */ @Version private Long version; 支持的數(shù)據(jù)類型只有: int,Integer,long,Long,Date,Timestamp,LocalDateTime整數(shù)類型下 newVersion = oldVersion + 1,也就是version字段自增1 3.執(zhí)行代碼 Controller 必須先進行查詢操作,獲取實體類得到version,然后修改,才可以讓version自增。不是mp從數(shù)據(jù)庫操作中拿出來的實體類,不會自增僅支持 updateById(id) 與 update(entity, wrapper) 方法在 update(entity, wrapper) 方法下, wrapper 不能復(fù)用!!! /** * 將user表中,name='貝斯特'的,按實體類u進行更新,此處實體類賦值了email,那就更新email字段 * 樂觀鎖操作看下面注釋 * */ @RestController @RequestMapping("/user") public class UserController { @Autowired private UserMapper userMapper; @Autowired private UserServiceImpl userService; //localhost:8080/user/uu @RequestMapping("/uu") public void uu(){ //1.必須先從數(shù)據(jù)庫取出user,也就是必須先查 User u = userMapper.selectById(1696794424765054978l); //User u = new User(); 這種寫法的version雖然能查出來,但是不會自增改變 u.setEmail("152301@qq.com"); //2.然后修改,才可以讓version自增,不是mp從數(shù)據(jù)庫拿出來得實體類,不會自增 //3.以下3個方法都可以使version生效 userMapper.updateById(u); userMapper.update(u,new UpdateWrapper userService.update(u,new UpdateWrapper } } 驗證 第一步,打斷點執(zhí)行過查詢,看到查出來版本是1,此時暫停代碼執(zhí)行,斷點在update前,不進行sql修改 第二步,將該條數(shù)據(jù)在數(shù)據(jù)庫中的版本由1改為2,模擬代碼執(zhí)行過程中數(shù)據(jù)被他人修改的過程 最后,版本號被中途修改,前后對應(yīng)不上,所以樂觀鎖生效,沒有執(zhí)行數(shù)據(jù)更改,Updates: 0 多租戶插件 Saas多租戶 多租戶技術(shù)(英語:multi-tenancy technology)或稱多重租賃技術(shù),是一種軟件架構(gòu)技術(shù),它是在探討與實現(xiàn)如何于多用戶的環(huán)境下共用相同的系統(tǒng)或程序組件,并且仍可確保各用戶間數(shù)據(jù)的隔離性。 Mybatis-Plus中,多租戶的實現(xiàn),實際就是將租戶id拼接到了所操作sql的條件中 1.數(shù)據(jù)表 建表 tenant_id -- 租戶ID 租戶ID可以是整數(shù)或字符串 CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', `name` varchar(30) DEFAULT NULL COMMENT '姓名', `age` int DEFAULT NULL COMMENT '年齡', `email` varchar(50) DEFAULT NULL COMMENT '郵箱', `version` bigint DEFAULT NULL, `tenant_id` bigint NOT NULL COMMENT '租戶ID', --也可以是varchar類型 PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1697138078167941125 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 插入數(shù)據(jù) 上半部分租戶id為1,下半部分租戶id為2 INSERT INTO `user` VALUES (1, 'Jone', 18, 'test1@baomidou.com', NULL, 1); INSERT INTO `user` VALUES (2, 'Jack', 20, 'test2@baomidou.com', NULL, 1); INSERT INTO `user` VALUES (3, 'Tom', 28, 'test3@baomidou.com', NULL, 1); INSERT INTO `user` VALUES (4, 'Sandy', 21, 'test4@baomidou.com', NULL, 1); INSERT INTO `user` VALUES (5, 'Billie', 24, 'test5@baomidou.com', NULL, 1); --------------------------------------------------------------------------------------- INSERT INTO `user` VALUES (1696794424765054978, '貝斯特', 16, '122222@qq.com', 2, 2); INSERT INTO `user` VALUES (1697138078167941121, '阿比西尼亞', 3, '12222@qq.com', NULL, 2); INSERT INTO `user` VALUES (1697138078167941122, '布偶', 3, '33333@qq.com', NULL, 2); INSERT INTO `user` VALUES (1697138078167941123, '豹貓', 2, '66666@qq.com', NULL, 2); INSERT INTO `user` VALUES (1697138078167941124, '暹羅', 1, '88888@qq.com', NULL, 2); 2.代碼配置 配置插件 注意插件順序,先多租戶,在分頁、然后樂觀鎖 主要進行了三項配置: 獲取租戶ID,會將此ID值拼接到所有sql的條件中獲取租戶表字段 默認為tenant_id表過濾,哪些表需要進行租戶過濾。返回true,表示當前表不進行租戶過濾,false則需要過濾 import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import net.sf.jsqlparser.expression.StringValue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //多租戶插件 interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() { /** * 獲取租戶ID 實際應(yīng)該從用戶信息中獲取,此處為了測試隨便寫了,就寫上面建表的1或2 * */ @Override public Expression getTenantId() { //new StringValue("0001");字符串的 return new LongValue(2); } /** * 獲取租戶表字段 默認為tenant_id * * @return */ @Override public String getTenantIdColumn() { return "tenant_id"; } /** * 表過濾,返回true,表示當前表不進行租戶過濾 * * 默認返回 false 表示所有表都需要拼多租戶條件 * */ @Override public boolean ignoreTable(String tableName) { //排除a表 //return "a".equalsIgnoreCase(tableName); return false; } })); //分頁 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //樂觀鎖 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } /** * 設(shè)置 MybatisConfiguration#useDeprecatedExecutor = false 避免緩存萬一出現(xiàn)問題 * 新版本、大概3.4之后不需要這個 * */ // @Bean // public ConfigurationCustomizer configurationCustomizer() { // return configuration -> configuration.setUseDeprecatedExecutor(false); // } } 實體類 租戶id字段,最好就用tenantId @TableName(value ="user") public class User implements Serializable { /** * 租戶 ID */ private Long tenantId; } 多租戶屏蔽 如果配置了多租戶,又希望某些語句的執(zhí)行不進行租戶過濾,則需要: 在xxMapper.java中的方法上添加@InterceptorIgnore(tenantLine = "true") //注意將泛型換為實體類 public interface UserMapper extends MyUserMapper //不希望此方法進行多租戶過濾 @InterceptorIgnore(tenantLine = "true") User selectCode(String id); } 注意點: 注解要加在xxxMapper.java類的方法上 訪問修飾符default會使注解失效 如果不是自定義語句,而是MybatisPlus自帶的增刪改查方法,要繼承BaseMapper來添加注解,如下: /** * 注意將泛型換為實體類 * */ public interface UserMapper extends BaseMapper //selectById是Mybatis-Plus中BaseMapper @InterceptorIgnore(tenantLine = "true") User selectById(Serializable id); //這個是自己寫的 @InterceptorIgnore(tenantLine = "true") User selectCode(String id); } 3.代碼執(zhí)行 Controller @RestController @RequestMapping("/user") public class UserController { @Autowired private UserServiceImpl userService; //localhost:8080/user/zu @RequestMapping("/zu") public User zu(){ return userService.getOne(new QueryWrapper } } 結(jié)果 因為配置類里獲取的租戶id為2,所以自動拼接了tenant_id = 2 使用了@InterceptorIgnore(tenantLine = "true"),則沒有拼接tenant_id = 2,實現(xiàn)了屏蔽 防全表更新刪除插件 針對 update 和 delete 語句 作用: 阻止惡意的全表更新刪除 其實就是阻止不帶where條件的修改或刪除語句執(zhí)行,執(zhí)行了全表更新刪除會報錯: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation 1.數(shù)據(jù)表 建表語句 CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', `name` varchar(30) DEFAULT NULL COMMENT '姓名', `age` int DEFAULT NULL COMMENT '年齡', `email` varchar(50) DEFAULT NULL COMMENT '郵箱', `version` bigint DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1697138078167941125 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 插入數(shù)據(jù) 此處沒有沿用上面示例的多租戶,因為多租戶會拼接sql,帶租戶id的where條件,不會使此插件生效 INSERT INTO `user` VALUES (1696794424765054978, '貝斯特', 16, '123', 2); INSERT INTO `user` VALUES (1697138078167941121, '阿比西尼亞', 3, '15230114461@163.com', NULL); INSERT INTO `user` VALUES (1697138078167941122, '布偶', 3, '457', NULL); INSERT INTO `user` VALUES (1697138078167941123, '豹貓', 2, '789', NULL); INSERT INTO `user` VALUES (1697138078167941124, '暹羅', 1, 'sdf', NULL); 2.代碼配置 config 注意插件順序! import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //分頁 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //樂觀鎖 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); //防止全表更新與刪除 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; } } 屏蔽全表操作插件 如果希望某些方法可以進行全表更新刪除: 在xxMapper.java中的方法上添加@InterceptorIgnore(blockAttack = "true") import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; import java.io.Serializable; public interface UserMapper extends BaseMapper //這個是自己寫的 @InterceptorIgnore(blockAttack = "true") User selectCode(String id); //這個是BaseMapper自帶的,直接從BaseMapper里粘貼出來加注解即可,注意把泛型T改為對應(yīng)entity @InterceptorIgnore(blockAttack = "true") int update(@Param("et") User entity, @Param("ew") Wrapper } 3.代碼執(zhí)行 Controller @RestController @RequestMapping("/user") public class UserController { @Autowired private UserMapper userMapper; //localhost:8080/user/dela @RequestMapping("/dela") public void dela(){ User u = new User(); u.setEmail("qq.com"); userMapper.update(u,null); } //localhost:8080/user/delb @RequestMapping("/delb") public void delb(){ User u = new User(); u.setEmail("15230114461@163.com"); userMapper.update(u,new UpdateWrapper } } 結(jié)果 localhost:8080/user/dela 會報錯: 而 localhost:8080/user/delb 執(zhí)行成功! 動態(tài)表名插件 1.概念 描述: Sql執(zhí)行時,動態(tài)的修改表名 簡單業(yè)務(wù)場景: 日志或者其他數(shù)據(jù)量大的表,通過日期進行了水平分表,需要通過日期參數(shù),動態(tài)的查詢數(shù)據(jù)。分開來存儲,表中的列名都是一樣的,只是表名不同。 **比如:**log_201907、log_201908等等之類的 2.數(shù)據(jù)表 建表語句 建立一個user表,以年月結(jié)尾 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for user202308 -- ---------------------------- DROP TABLE IF EXISTS `user202308`; CREATE TABLE `user202308` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '姓名', `age` int NULL DEFAULT NULL COMMENT '年齡', `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '郵箱', `version` bigint NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1697138078167941125 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of user202308 -- ---------------------------- INSERT INTO `user202308` VALUES (1696794424765054978, '貝斯特', 16, '123', 2); INSERT INTO `user202308` VALUES (1697138078167941121, '阿比西尼亞', 3, '15230114461@163.com', NULL); INSERT INTO `user202308` VALUES (1697138078167941122, '布偶', 3, '457', NULL); INSERT INTO `user202308` VALUES (1697138078167941123, '豹貓', 2, '789', NULL); INSERT INTO `user202308` VALUES (1697138078167941124, '暹羅', 1, 'sdf', NULL); SET FOREIGN_KEY_CHECKS = 1; 改上面語句表名加一個月,生成兩張年月結(jié)尾的user表 3.代碼配置 實體類、mapper等不需要修改,生成后不動 動態(tài)表名轉(zhuǎn)換器 根據(jù)當前年月生成表名后綴進行拼接,返回邏輯表名+年月,比如 ‘user202309’ import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; public class ITableNameHandler implements TableNameHandler { private String handlerTableName; ITableNameHandler(String handlerTableName){ this.handlerTableName = handlerTableName; } @Override public String dynamicTableName(String sql, String tableName) { /** * 根據(jù)當前年月生成表名后綴進行拼接,返回表名+年月,比如 'user202309' * */ Date da = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMM"); String end = simpleDateFormat.format(da); return handlerTableName + end; } } 配置類 yaml配置動態(tài)表的原始表 DynamicTableName: tableName: user 將動態(tài)表名轉(zhuǎn)換器配置到動態(tài)表插件 import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.Version; import com.baomidou.mybatisplus.extension.plugins.inner.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import java.text.SimpleDateFormat; import java.util.Date; @Configuration public class MybatisPlusConfig { @Value("${DynamicTableName.tableName}") private String handlerTableName; @Bean public MybatisPlusInterceptor mybatisPInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //動態(tài)表名插件 DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor(); dynamicTableNameInnerInterceptor.setTableNameHandler(new ITableNameHandler(handlerTableName)); interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor); // 3.4.3.2 作廢該方式 // dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map); //分頁 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //樂觀鎖 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); //防止全表更新與刪除 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; } } 屏蔽動態(tài)表名 如果某些操作不希望使用動態(tài)表名 在xxMapper.java中的方法上添加@InterceptorIgnore(dynamicTableName= "true") import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.baomidou.mybatisplus.core.mapper.BaseMapper; public interface UserMapper extends BaseMapper @InterceptorIgnore(dynamicTableName = "true") List } 這樣,selectByMap方法就不會使用轉(zhuǎn)換的表名了 4.代碼執(zhí)行 Controller @RestController @RequestMapping("/user") public class UserController { @Autowired private UserServiceImpl userService; @RequestMapping("/zu") public User zu(){ return userService.getOne(new QueryWrapper } } 結(jié)果 使用了轉(zhuǎn)換的表名 數(shù)據(jù)權(quán)限插件 Mybatis-Plus的數(shù)據(jù)權(quán)限插件,其實就是攔截sql,在其末尾添加where條件。 比如部門、用戶id等,實現(xiàn)操作本部門、本用戶的數(shù)據(jù) 1.建表 主要添加部門及用戶id字段 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '姓名', `age` int NULL DEFAULT NULL COMMENT '年齡', `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '郵箱', `version` bigint NULL DEFAULT NULL, `deptid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '部門', `userid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用戶id', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1697138078167941125 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, 'Jone', 18, 'qq.com', NULL, 'A', 'Jone'); INSERT INTO `user` VALUES (2, 'Jack', 20, 'qq.com', NULL, 'A', 'Jack'); INSERT INTO `user` VALUES (3, 'Tom', 28, 'qq.com', NULL, 'A', 'Tom'); INSERT INTO `user` VALUES (4, 'Sandy', 21, 'qq.com', NULL, 'A', 'Sandy'); INSERT INTO `user` VALUES (5, 'Billie', 24, 'qq.com', NULL, 'A', 'Billie'); INSERT INTO `user` VALUES (1696794424765054978, '貝斯特', 16, 'qq.com', 2, 'B', '貝斯特'); INSERT INTO `user` VALUES (1697138078167941121, '阿比西尼亞', 3, 'qq.com', NULL, 'B', '阿比西尼亞'); INSERT INTO `user` VALUES (1697138078167941122, '布偶', 3, 'qq.com', NULL, 'B', '布偶'); INSERT INTO `user` VALUES (1697138078167941123, '豹貓', 2, 'qq.com', NULL, 'B', '豹貓'); INSERT INTO `user` VALUES (1697138078167941124, '暹羅', 1, 'qq.com', NULL, 'B', '暹羅'); SET FOREIGN_KEY_CHECKS = 1; 2.代碼配置 2.1 權(quán)限工具類 寫一個保存權(quán)限信息的工具類 這里權(quán)限就用簡單的數(shù)字代替了 public class GetRole { private static ThreadLocal public static Integer getRole(){ return role.get(); } public static void setRole(Integer i){ role.set(i); } } 2.2 攔截器 寫一個攔截器實現(xiàn)類 role==2 為部門權(quán),限查本部門 role==3 為本人權(quán)限,只能查本人數(shù)據(jù) 由于只是演示,部門寫死為"B",用戶寫死為"貝斯特" import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.StringValue; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.schema.Column; /** * Mybatis-Plus的數(shù)據(jù)權(quán)限插件,其實就是攔截sql,在其末尾添加where條件。 * 比如部門、用戶id等,實現(xiàn)操作本部門、本用戶的數(shù)據(jù) * */ public class MyDataPermissionHandler implements DataPermissionHandler { /** * 獲取數(shù)據(jù)權(quán)限 SQL 片段 * * Params: * where – 待執(zhí)行sql的Where條件 * mappedStatementId – 待執(zhí)行sql所對應(yīng)的Mapper方法, * * Returns: * JSqlParser 條件表達式,返回的條件表達式會覆蓋原有的條件表達式 * */ @Override public Expression getSqlSegment(Expression where, String mappedStatementId) { //一般用戶登陸后信息保存在ThreadLocal中,這里模擬這個過程,從ThreadLocal獲取權(quán)限字符,2或3 Integer role = GetRole.getRole(); Expression sqlSegmentExpression = null; try { /** * 如果要執(zhí)行的sql沒有where條件, CCJSqlParserUtil.parseCondExpression("A=A") 會自動加where條件 * 比如執(zhí)行的是 SELECT id, deptid FROM user,最后會變?yōu)?SELECT id, deptid FROM user where A=A * */ if(where == null){ if(role==2){ return CCJSqlParserUtil.parseCondExpression("deptid = 'B' "); } else if (role==3){ return CCJSqlParserUtil.parseCondExpression("userid = '貝斯特' "); } }else{ if(role==2){ sqlSegmentExpression = CCJSqlParserUtil.parseCondExpression("deptid = 'B' "); } else if (role==3){ sqlSegmentExpression = CCJSqlParserUtil.parseCondExpression("userid = '貝斯特' "); } } } catch (JSQLParserException e) { e.printStackTrace(); return null; } //返回and....的語句 //參數(shù)where放到and的左邊,參數(shù)sqlSegmentExpression放and右邊,where and sqlSegmentExpression return new AndExpression(where,sqlSegmentExpression); } } 2.3 配置類 將攔截器實現(xiàn)類配置到插件主體中 import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.inner.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //數(shù)據(jù)權(quán)限 interceptor.addInnerInterceptor(new DataPermissionInterceptor(new MyDataPermissionHandler())); //分頁 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //樂觀鎖 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); //防止全表更新與刪除 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; } } 3.執(zhí)行測試 測試調(diào)用 模擬傳入用戶權(quán)限為2,傳入ThreadLocal,并在攔截器獲取用于拼接sql判斷 @RestController @RequestMapping("/user") public class UserController { @Autowired private UserMapper userMapper; //localhost:8080/user/ro?r=2 @RequestMapping("/ro") public List GetRole.setRole(r); List return ul; } } 最后拼接了部門條件 數(shù)據(jù)變動記錄插件 必須使用邏輯刪除,只針對新增和修改(因為用了邏輯刪除,就沒有真正的刪除了,刪除也是修改) 在執(zhí)行增改操作后,控制臺日志會生成json串 1.數(shù)據(jù)表 要有邏輯刪除字段 CREATE TABLE `pertwo0` ( `deleted` int DEFAULT '0' COMMENT '是否被刪除 0 未刪除 1 已刪除', `name` varchar(255) DEFAULT NULL, `age` int DEFAULT NULL, `sex` varchar(255) DEFAULT NULL, `code` varchar(255) NOT NULL, `job` varchar(255) DEFAULT NULL, PRIMARY KEY (`code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 2.實體類及yaml 實體類 邏輯刪除字段 @TableName(value ="pertwo",autoResultMap = true) public class Pertwo implements Serializable { /** * 是否被刪除 0 未刪除 1 已刪除 */ private Integer deleted; } yaml 配置邏輯刪除 mybatis-plus: global-config: db-config: logic-delete-field: deleted # 全局邏輯刪除的實體字段名,也可實體類字段上加上@TableLogic注解 logic-not-delete-value: 0 # 邏輯未刪除值 logic-delete-value: 1 # 邏輯已刪除值 3.插件配置 @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //數(shù)據(jù)變動記錄插件 interceptor.addInnerInterceptor(new DataChangeRecorderInnerInterceptor()); return interceptor; } } 4.執(zhí)行測試 Controller // localhost:8080/user/inpt @RequestMapping("/inpt") public String inst(){ Pertwo p = new Pertwo(); p.setCode("cat"); p.setSex("女"); p.setName("gril"); //list變?yōu)関archar字符串存入數(shù)據(jù)庫 List contact.add("一"); contact.add("個"); contact.add("cat"); p.setJob(contact); p.setAge(AgeEnum.TWO); pertwoServiceImpl.save(p); return "success"; } 結(jié)果 { "tableName": "pertwo", "operation": "insert", "recordStatus": "true", "changedData": [{ "CODE": "null->cat", "JOB": "null->[一, 個, cat]", "NAME": "null->gril", "AGE": "null->TWO", "SEX": "null->女" }], "cost(ms)": 22 } 結(jié)合Sharding分庫分表 MyBatis-Plus 和 Sharding-JDBC 的版本對應(yīng)關(guān)系如下: MyBatis-Plus 版本Sharding-JDBC 版本3.0.0及以上4.0.0及以上3.0.0以下3.0.0以下 因此,如果使用 MyBatis-Plus 3.0.0及以上版本,應(yīng)該使用 Sharding-JDBC 4.0.0及以上版本。如果使用 MyBatis-Plus 3.0.0以下版本,應(yīng)該使用 Sharding-JDBC 3.0.0以下版本。 需要注意的是,不同版本的 MyBatis-Plus 和 Sharding-JDBC 可能存在兼容性問題。為了避免出現(xiàn)這種問題,建議在使用時使用相應(yīng)版本的 MyBatis-Plus 和 Sharding-JDBC。 準備工作: 建表 在兩個庫中分別執(zhí)行下面語句來建表,deleted為邏輯刪除字段、age為通用枚舉,沿用了上面的示例 先建立pertwo,利用MybatisX工具生成實體類后,在建pertwo0與pertwo1 CREATE TABLE `pertwo` ( `deleted` int DEFAULT '0' COMMENT '是否被刪除 0 未刪除 1 已刪除', `name` varchar(255) DEFAULT NULL, `age` int DEFAULT NULL, `sex` varchar(255) DEFAULT NULL, `code` varchar(255) NOT NULL, `job` varchar(255) DEFAULT NULL, PRIMARY KEY (`code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE `pertwo0` ( `deleted` int DEFAULT '0' COMMENT '是否被刪除 0 未刪除 1 已刪除', `name` varchar(255) DEFAULT NULL, `age` int DEFAULT NULL, `sex` varchar(255) DEFAULT NULL, `code` varchar(255) NOT NULL, `job` varchar(255) DEFAULT NULL, PRIMARY KEY (`code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE `pertwo1` ( `deleted` int DEFAULT '0' COMMENT '是否被刪除 0 未刪除 1 已刪除', `name` varchar(255) DEFAULT NULL, `age` int DEFAULT NULL, `sex` varchar(255) DEFAULT NULL, `code` varchar(255) NOT NULL, `job` varchar(255) DEFAULT NULL, PRIMARY KEY (`code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; pom依賴 注意,連接池要用druid而不是druid-spring-boot-starter 如果使用下面這個: 會報錯:Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required 實體類 遇到的問題: Sharding-JDBC與Myabtis-Plus并非完全兼容,在二者整合情況下。MP的通用枚舉功能不適用,所以需要自己手動對二者的轉(zhuǎn)換進行處理,其他不適配功能暫時沒發(fā)現(xiàn)。 import com.baomidou.Enum.AgeEnum; import com.baomidou.mybatisplus.annotation.*; import com.baomidou.typeHandler.GenericEnumTypeHandler; import com.baomidou.typeHandler.ListTypeHandler; import java.io.Serializable; import java.util.List; /** * @TableName pertwo */ @TableName(value ="pertwo",autoResultMap = true) public class Pertwo implements Serializable { @TableId private String code; /** * 是否被刪除 0 未刪除 1 已刪除 */ private Integer deleted; private String name; //配置數(shù)據(jù)庫int類型與java枚舉的互相轉(zhuǎn)換 //Sharding-JDBC與Myabtis-Plus并非完全兼容,在二者整合情況下。MP的通用枚舉功能不適用,所以需要自己手動對二者的轉(zhuǎn)換進行處理 @TableField(typeHandler = GenericEnumTypeHandler.class) private AgeEnum age; public AgeEnum getAge() { return age; } public void setAge(AgeEnum age) { this.age = age; } private String sex; /** * 配置數(shù)據(jù)庫varchar類型與json類型轉(zhuǎn)換,字段類型處理器功能二者目前適配 */ @TableField(typeHandler = ListTypeHandler.class) private List public List return job; } public void setJob(List this.job = job; } @TableField(exist = false)//不作為表中字段,排除出去 private static final long serialVersionUID = 1L; public String getCode() { return code; } public void setCode(String code) { this.code = code; } /** * 是否被刪除 0 未刪除 1 已刪除 */ public Integer getDeleted() { return deleted; } /** * 是否被刪除 0 未刪除 1 已刪除 */ public void setDeleted(Integer deleted) { this.deleted = deleted; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } } 枚舉 在真實開發(fā)中,枚舉可能會對應(yīng)下拉框小代碼業(yè)務(wù)。而且不可能只有一種枚舉類。 這里定義了共有接口,并在具體枚舉中添加靜態(tài)方法,用于在枚舉類型處理器獲取所有枚舉類型 public interface EnumCommonMethod { Integer getId(); String getDesc(); } import com.baomidou.mybatisplus.annotation.EnumValue; import com.fasterxml.jackson.annotation.JsonValue; public enum AgeEnum implements EnumCommonMethod { ONE(1, "一歲"), TWO(2, "二歲"), THREE(3, "三歲"); /** * 數(shù)據(jù)庫存放數(shù)值,前端展示漢字 * */ @JsonValue private String desc; //@EnumValue private int id; AgeEnum(int i, String age) { this.id = i; this.desc = age; } public static AgeEnum getEnumById(int id){ for(AgeEnum ageEnum : AgeEnum.values()){ if(ageEnum.getId() == id){ return ageEnum; } } return null; } @Override public Integer getId() { return this.id; } @Override public String getDesc(){ return this.desc; } } 枚舉類型處理器 Sharding-JDBC與Myabtis-Plus并非完全兼容,在二者整合情況下。MP的通用枚舉功能不適用,所以需要自己手動對二者的轉(zhuǎn)換進行處理 java反射中,Method的invoke如果不傳入目標對象實例,則執(zhí)行的方法必須是靜態(tài)方法,此處對應(yīng)上面枚舉類中的靜態(tài)方法 import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import java.lang.reflect.Method; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class GenericEnumTypeHandler private Class private Method getId; private Method getEnumById; public GenericEnumTypeHandler(Class if (type == null) { throw new IllegalArgumentException("Type argument cannot be null"); } this.type = type; try { getId = type.getDeclaredMethod("getId"); getEnumById = type.getDeclaredMethod("getEnumById", int.class); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("Can not found getId or getEnumById method in " + type.getName(), e); } } @Override public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException { try { ps.setInt(i, (Integer)getId.invoke(parameter)); } catch (Exception e) { throw new SQLException(e); } } @Override public E getNullableResult(ResultSet rs, String columnName) throws SQLException { try { int id = rs.getInt(columnName); this.type.getName(); //invoke如果不傳入目標示例,則執(zhí)行的方法必須是靜態(tài)方法,對應(yīng)枚舉類中的靜態(tài)方法 return (E)getEnumById.invoke(null, id); } catch (Exception e) { throw new SQLException(e); } } @Override public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { try { int id = rs.getInt(columnIndex); //invoke如果不傳入目標示例,則執(zhí)行的方法必須是靜態(tài)方法,對應(yīng)枚舉類中的靜態(tài)方法 return (E)getEnumById.invoke(null, id); } catch (Exception e) { throw new SQLException(e); } } @Override public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { try { int id = cs.getInt(columnIndex); //invoke如果不傳入目標示例,則執(zhí)行的方法必須是靜態(tài)方法,對應(yīng)枚舉類中的靜態(tài)方法 return (E)getEnumById.invoke(null, id); } catch (Exception e) { throw new SQLException(e); } } } 字段類型處理器 此處對應(yīng)實體類job字段的字符串轉(zhuǎn)集合轉(zhuǎn)換處理 import lombok.extern.java.Log; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import org.apache.ibatis.type.TypeHandler; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.List; @MappedJdbcTypes(JdbcType.VARCHAR) //數(shù)據(jù)庫類型 @MappedTypes({List.class}) //java數(shù)據(jù)類型 @Log public class ListTypeHandler implements TypeHandler @Override public void setParameter(PreparedStatement ps, int i, List throws SQLException { String hobbys = dealListToOneStr(parameter); ps.setString(i , hobbys); } /** * 集合拼接字符串 * @param parameter * @return */ private String dealListToOneStr(List if(parameter == null || parameter.size() <=0) { return null; } String res = ""; for (int i = 0 ;i < parameter.size(); i++) { if(i == parameter.size()-1){ res+=parameter.get(i); return res; } res+=parameter.get(i)+","; } return null; } @Override public List throws SQLException { log.info("method ====>>> getResult(ResultSet rs, String columnName)"); return Arrays.asList(rs.getString(columnName).split(",")); } @Override public List throws SQLException { log.info("method ====>>> getResult(ResultSet rs, int columnIndex)"); return Arrays.asList(rs.getString(columnIndex).split(",")); } @Override public List log.info("method ====>>> getResult(CallableStatement cs, int columnIndex)"); String hobbys = cs.getString(columnIndex); return Arrays.asList(hobbys.split(",")); } } yaml配置 server: port: 8080 spring: #可以不用配置 #datasource: #url: #username: #password: main: allow-bean-definition-overriding: true shardingsphere: # 參數(shù)配置,顯示sql props: sql: show: true # 配置數(shù)據(jù)源 datasource: # 給每個數(shù)據(jù)源取別名,下面的ds1,ds1任意取名字 names: ds0,ds1 # 給master-ds1每個數(shù)據(jù)源配置數(shù)據(jù)庫連接信息 ds0: # 配置druid數(shù)據(jù)源 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis-plus?nullCatalogMeansCurrent=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: wangziyu123 maxPoolSize: 100 minPoolSize: 5 # 配置ds1-slave ds1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis-plus-slave?nullCatalogMeansCurrent=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: wangziyu123 maxPoolSize: 100 minPoolSize: 5 # 配置默認數(shù)據(jù)源ds0 sharding: # 默認數(shù)據(jù)源,如果配置了數(shù)據(jù)庫的讀寫分離就需要此配置,主要用于寫。 #default-data-source-name: ds0 # 配置分表的規(guī)則 tables: #邏輯表名,去掉數(shù)字后綴的表名 pertwo: # 數(shù)據(jù)節(jié)點:數(shù)據(jù)源$->{0..N}.邏輯表名$->{0..N} actual-data-nodes: ds$->{0..1}.pertwo$->{0..1} # 拆分庫策略,也就是什么樣子的數(shù)據(jù)放入放到哪個數(shù)據(jù)庫中。 database-strategy: # 精確分片策略 standard: sharding-column: sex # 分片字段(分片鍵) #自定義分片策略 precise-algorithm-class-name: com.sharding.config.DatabasePreciseShardingAlgorithm # 拆分表策略,也就是什么樣子的數(shù)據(jù)放入放到哪個數(shù)據(jù)表中。 table-strategy: inline: sharding-column: age # 分片字段(分片鍵) #取模策略,如果age字段除2余數(shù)為0,就放入表pertwo0,余數(shù)為1就放入pertwo1.(余數(shù)永遠小于除數(shù)) algorithm-expression: pertwo$->{age % 2} # 分片算法表達式 ########################################Mybatis-Plus部分################################################## #mybatis plus是獨立節(jié)點,需要單獨配置 mybatis-plus: global-config: db-config: logic-delete-field: deleted # 全局邏輯刪除的實體字段名,也可實體類字段上加上@TableLogic注解 logic-not-delete-value: 0 # 邏輯未刪除值 logic-delete-value: 1 # 邏輯已刪除值 configuration: # 控制臺打印sql語句方便調(diào)試sql語句執(zhí)行錯誤 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #org.apache.ibatis.logging.log4j2.Log4j2Impl 這個不在控制臺打印查詢結(jié)果,但是在log4j中打印 #org.apache.ibatis.logging.nologging.NoLoggingImpl 在生產(chǎn)環(huán)境,不打印SQL日志 # log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl # xml掃描,多個目錄用逗號或者分號分隔(告訴 Mapper 所對應(yīng)的 XML 文件位置) # mapper-locations: classpath*:mapper/**/*Mapper.xml mapper-locations: classpath*:mapper/*.xml,classpath*:mapper/**/*Mapper.xml # 實體掃描,多個package用逗號或者分號分隔 type-aliases-package: com.sharding.entity 連接池問題 整合sharding,在配置數(shù)據(jù)源datasource時,除了url、username、password等,type也必須配,否則會報錯 Spring框架中,spring.datasource.type屬性用于指定數(shù)據(jù)源的類型。它可以配置以下幾種類型: com.zaxxer.hikari.HikariDataSource:使用HikariCP連接池作為數(shù)據(jù)源。org.apache.tomcat.jdbc.pool.DataSource:使用Tomcat JDBC連接池作為數(shù)據(jù)源。org.apache.commons.dbcp2.BasicDataSource:使用Apache Commons DBCP連接池作為數(shù)據(jù)源。org.springframework.jdbc.datasource.SimpleDriverDataSource:簡單的數(shù)據(jù)源實現(xiàn),直接使用提供的JDBC驅(qū)動程序。 除了上述幾種類型,還可以根據(jù)需要自定義數(shù)據(jù)源類型。使用不同的數(shù)據(jù)源類型可以根據(jù)具體的需求選擇適合的連接池或驅(qū)動程序。 此處我們的type配置druid連接池: type: com.alibaba.druid.pool.DruidDataSource 如果多個數(shù)據(jù)源的連接池參數(shù)一致,可采取如下: 不再為每個數(shù)據(jù)源單獨配置連接池參數(shù),而是使用一份配置: spring: shardingsphere: #省略 #....... datasource: names: ds0,ds1 ds0: type: com.alibaba.druid.pool.DruidDataSource # 配置druid數(shù)據(jù)源 driver-class-name: com.mysql.cj.jdbc.Driver url: username: root password: wangziyu123 #不再為每個數(shù)據(jù)源單獨配置連接池參數(shù) #maxPoolSize: 100 #minPoolSize: 5 ds1: type: com.alibaba.druid.pool.DruidDataSource # 配置druid數(shù)據(jù)源 driver-class-name: com.mysql.cj.jdbc.Driver url: username: password: #不再為每個數(shù)據(jù)源單獨配置連接池參數(shù) #maxPoolSize: 100 #minPoolSize: 5 #而是使用一份配置 #此處的連接池配置會被所有數(shù)據(jù)源統(tǒng)一使用 druid: # 初始連接數(shù) initialSize: 5 # 最小連接池數(shù)量 minIdle: 10 # 最大連接池數(shù)量 maxActive: 20 # 配置獲取連接等待超時的時間 maxWait: 60000 # 配置連接超時時間 connectTimeout: 30000 # 配置網(wǎng)絡(luò)超時時間 socketTimeout: 60000 druid監(jiān)控頁面問題 整合sharding連接池要用druid,而不是druid-spring-boot-starter,所以無法使用yaml配置監(jiān)控頁面 使用代碼進行配置 注意只是配置監(jiān)控頁面,其他連接池參數(shù)還是使用yaml配置 import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @Configuration public class DruidConfig { /** * Druid監(jiān)控 */ @Bean public ServletRegistrationBean statViewServlet(){ ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); Map initParams.put("loginUsername","root"); initParams.put("loginPassword","root"); //白名單,不配置默認就是允許所有訪問 //initParams.put("allow",""); //黑名單IP //initParams.put("deny","192.168.15.21"); bean.setInitParameters(initParams); return bean; } /** * web監(jiān)控的filter */ @Bean public FilterRegistrationBean webStatFilter(){ WebStatFilter ws = new WebStatFilter(); //開啟session統(tǒng)計功能,默認1000個 ws.setSessionStatEnable(true); FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(ws); Map //過濾掉需要監(jiān)控的文件 initParams.put("exclusions","/static/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); bean.setInitParameters(initParams); bean.setUrlPatterns(Arrays.asList("/*")); return bean; } } 去掉監(jiān)控頁面的連接池yaml配置 spring: shardingsphere: #......具體配置去上面示例粘貼 sharding: #......具體配置去上面示例粘貼 druid: # 初始連接數(shù) initialSize: 5 # 最小連接池數(shù)量 minIdle: 10 # 最大連接池數(shù)量 maxActive: 20 # 配置獲取連接等待超時的時間 maxWait: 60000 # 配置連接超時時間 connectTimeout: 30000 # 配置網(wǎng)絡(luò)超時時間 socketTimeout: 60000 # 配置間隔多久才進行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一個連接在池中最小生存的時間,單位是毫秒 minEvictableIdleTimeMillis: 300000 # 配置一個連接在池中最大生存的時間,單位是毫秒 maxEvictableIdleTimeMillis: 900000 # 配置檢測連接是否有效 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false filter: stat: enabled: true # 慢SQL記錄 log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true filters: stat,wall,log4j2 監(jiān)控頁面預(yù)覽 訪問地址: http://localhost:8080/druid 可以看到是兩個數(shù)據(jù)源 分片策略配置 對應(yīng)yaml中的precise-algorithm-class-name import lombok.extern.slf4j.Slf4j; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; import java.util.Collection; @Slf4j public class DatabasePreciseShardingAlgorithm implements PreciseShardingAlgorithm public DatabasePreciseShardingAlgorithm() { } /** * 以拼接邏輯表名末尾數(shù)字的方式實現(xiàn)分表: * 性別為'女',就將'0'拼接到邏輯表名末尾返回 * 性別為'男',就將'1'拼接到邏輯表名末尾返回 * * @param dataSourceCollection 數(shù)據(jù)源列表 * @param preciseShardingValue 分片信息,包括分片字段,邏輯表等 * */ @Override public String doSharding(Collection // 獲取分片鍵的值,也就是分片字段的值 String shardingValue = preciseShardingValue.getValue(); // 獲取邏輯表 String logicTableName = preciseShardingValue.getLogicTableName(); log.info("分片鍵的值:{},邏輯表:{}", shardingValue, logicTableName); String endMath = ""; if("女".equals(shardingValue)){ endMath = "0"; }else if("男".equals(shardingValue)){ endMath = "1"; }else{ log.info("字段:"+preciseShardingValue.getColumnName()+"的實際值為: "+shardingValue+",不符合規(guī)定值"); } // 遍歷數(shù)據(jù)源 for (String databaseSource : dataSourceCollection) { // 判斷數(shù)據(jù)源是否存在 if (databaseSource.endsWith(endMath)) { return databaseSource; } } // 不存在則拋出異常 throw new UnsupportedOperationException(); } } 測試 mapper 在自動生成基礎(chǔ)添加 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> select code,deleted,name, age,sex,job from pertwo where code = #{code} mapper接口 import com.baomidou.entity.Pertwo; import java.util.List; /** * 繼承MyBaseMapper, 自定義通用mapper接口 */ public interface PertwoMapper extends BaseMapper Pertwo selectCode(String code); } service 接口 import com.baomidou.entity.Pertwo; import com.baomidou.mybatisplus.extension.service.IService; public interface PertwoService extends IService Pertwo selectCode(String code); } 實現(xiàn)類 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.entity.Pertwo; import com.baomidou.service.PertwoService; import com.baomidou.dao.PertwoMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class PertwoServiceImpl extends ServiceImpl implements PertwoService{ @Autowired private PertwoMapper pw; @Override public Pertwo selectCode(String code) { return pw.selectCode(code); } } Controller @RestController @RequestMapping("/user") public class UserController { @Autowired private PertwoServiceImpl pertwoServiceImpl; //localhost:8080/user/mupt @RequestMapping("/mupt") public Pertwo must(){ //Pertwo p1 = pertwoServiceImpl.getOne(new QueryWrapper Pertwo p = pertwoServiceImpl.getOne(new QueryWrapper return p; } //localhost:8080/user/qpt?code=lyh @RequestMapping("/qpt") public Pertwo qst(@RequestParam String code){ Pertwo p = pertwoServiceImpl.selectCode(code); return p; } //localhost:8080/user/inpt @RequestMapping("/inpt") public String inst(){ Pertwo p = new Pertwo(); p.setCode("cat"); p.setSex("女"); p.setName("gril"); //list變?yōu)関archar字符串存入數(shù)據(jù)庫 List contact.add("一"); contact.add("個"); contact.add("cat"); p.setJob(contact); p.setAge(AgeEnum.TWO); pertwoServiceImpl.save(p); return "success"; } //localhost:8080/user/inpt1 @RequestMapping("/inpt1") public String inst1(){ Pertwo p = new Pertwo(); p.setCode("dog"); p.setSex("男"); p.setName("boy"); //list變?yōu)関archar字符串存入數(shù)據(jù)庫 List contact.add("一"); contact.add("個"); contact.add("dog"); p.setJob(contact); p.setAge(AgeEnum.THREE); pertwoServiceImpl.save(p); return "success"; } } 結(jié)果: localhost:8080/user/inpt: cat 主0 localhost:8080/user/inpt1 dog slave 1 分庫分表策略 分庫分表的策略,有5種 package org.apache.shardingsphere.core.yaml.config.sharding; public final class YamlShardingStrategyConfiguration implements YamlConfiguration { //單分片鍵的標準分片策略,支持精確查詢和范圍查詢。提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持。 private YamlStandardShardingStrategyConfiguration standard; //復(fù)合分片策略,支持多分片鍵組合。提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持 private YamlComplexShardingStrategyConfiguration complex; //通過Hint而非SQL解析的方式分片的策略。簡單來理解就是說,他的分片鍵不再跟SQL語句相關(guān)聯(lián),而是用程序另行指定。 private YamlHintShardingStrategyConfiguration hint; //行表達式分片策略 private YamlInlineShardingStrategyConfiguration inline; //不分片的策略 private YamlNoneShardingStrategyConfiguration none; } yaml種對應(yīng)的配置 分庫與分表是通用的 分庫的: 分表的: 標準分片策略 只支持單分片鍵,也就是一個數(shù)據(jù)庫字段的分片。 提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持。相當于等于一個值,或在幾個值范圍中 public final class YamlStandardShardingStrategyConfiguration implements YamlBaseShardingStrategyConfiguration { //指定分片鍵。也就是數(shù)據(jù)庫的列,字段名 private String shardingColumn; //指定一個精確查詢的實現(xiàn)類,值為全類名。用于處理:分片鍵=某個具體值 private String preciseAlgorithmClassName; //指定一個范圍查詢的實現(xiàn)類,值為全類名。用于處理:分片鍵in或BETWEEN AND某些值,即范圍處理 private String rangeAlgorithmClassName; } 對應(yīng)下面的database-strategy與table-strategy的yaml配置: spring: shardingsphere: datasource: names: ds0,ds1 ds0: #.... ds1: #.... sharding: tables: pertwo: actual-data-nodes: ds$->{0..1}.pertwo$->{0..1} #分庫 database-strategy: # 精確分片策略 standard: # 分片字段(分片鍵) sharding-column: sex # 自定義精確分片策略實現(xiàn)類 precise-algorithm-class-name: com.sharding.config.DatabasePreciseShardingAlgorithm #分表 table-strategy: # 精確分片策略 standard: # 分片字段(分片鍵) sharding-column: age # 自定義精確分片策略實現(xiàn)類 precise-algorithm-class-name: com.sharding.config.DataTablePreciseShardingAlgorithm 分庫實現(xiàn)類代碼示例: 精確查詢實現(xiàn)接口PreciseShardingAlgorithm 范圍查詢實現(xiàn)接口RangeShardingAlgorithm 下面示例為精確查詢: public class DatabasePreciseShardingAlgorithm implements PreciseShardingAlgorithm public DatabasePreciseShardingAlgorithm() { } /** * 以拼接邏輯表名末尾數(shù)字的方式實現(xiàn)分表: * 性別為'女',就將'0'拼接到邏輯表名末尾返回,也就是sex為女的使用pertwo0表 * 性別為'男',就將'1'拼接到邏輯表名末尾返回,也就是sex為男的使用pertwo1表 * * @param dataSourceCollection 數(shù)據(jù)源列表 * @param preciseShardingValue 分片信息,包括分片字段,邏輯表等 * */ @Override public String doSharding(Collection // 獲取分片鍵的值,也就是分片字段的值 String shardingValue = preciseShardingValue.getValue(); // 獲取邏輯表 String logicTableName = preciseShardingValue.getLogicTableName(); log.info("分片鍵的值:{},邏輯表:{}", shardingValue, logicTableName); String endMath = ""; if("女".equals(shardingValue)){ endMath = "0"; }else if("男".equals(shardingValue)){ endMath = "1"; }else{ log.info("字段:"+preciseShardingValue.getColumnName()+"的實際值為: "+shardingValue+",不符合規(guī)定值"); } // 遍歷數(shù)據(jù)源 for (String databaseSource : dataSourceCollection) { // 判斷數(shù)據(jù)源是否存在 if (databaseSource.endsWith(endMath)) { return databaseSource; } } // 不存在則拋出異常 throw new UnsupportedOperationException(); } } 復(fù)合分片策略 提供多個數(shù)據(jù)庫字段對SQL語句中的=, IN和BETWEEN AND的分片操作支持 @Getter @Setter public final class YamlComplexShardingStrategyConfiguration implements YamlBaseShardingStrategyConfiguration { //分片鍵(多個) private String shardingColumns; //實現(xiàn)類的全類名 private String algorithmClassName; } yaml配置 spring: shardingsphere: sharding: default-table-strategy: #復(fù)合分片 complex: #分片鍵(多個) sharding-columns: #實現(xiàn)類的全類名 algorithm-class-name: 實現(xiàn)類示例 待補充… Hint分片策略 package org.apache.shardingsphere.core.yaml.config.sharding.strategy; @Getter @Setter public final class YamlHintShardingStrategyConfiguration implements YamlBaseShardingStrategyConfiguration { #實現(xiàn)類全類名 private String algorithmClassName; } yaml配置 spring: shardingsphere: sharding: default-table-strategy: #Hint分片策略 hint: #實現(xiàn)類全類名 algorithm-class-name: 實現(xiàn)類示例 待補充… 表達式分片策略 只支持單分片鍵,對于簡單的分片算法,可以通過簡單的配置使用,從而避免繁瑣的Java代碼開發(fā)。 如: tuser${user_id % 8} 表示t_user表按照user_id按8取模分成8個表,表名稱為t_user_0到t_user_7。 package org.apache.shardingsphere.core.yaml.config.sharding.strategy; @Getter @Setter public final class YamlInlineShardingStrategyConfiguration implements YamlBaseShardingStrategyConfiguration { //分片鍵,單個 private String shardingColumn; //表達式 private String algorithmExpression; } yaml配置 spring: shardingsphere: sharding: tables: pertwo: table-strategy: #表達式策略 inline: # 分片字段(分片鍵) sharding-column: age #取模策略,如果age字段除2余數(shù)為0,就放入表pertwo0,余數(shù)為1就放入pertwo1.(余數(shù)永遠小于除數(shù)) algorithm-expression: pertwo$->{age % 2} # 分片算法表達式 代碼示例 表達式策略無需寫代碼配置 none分片策略 不分片的策略。什么都沒有 package org.apache.shardingsphere.core.yaml.config.sharding.strategy; public final class YamlNoneShardingStrategyConfiguration implements YamlBaseShardingStrategyConfiguration { } 柚子快報邀請碼778899分享:MybatisPlus功能使用 參考閱讀> {
> {
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。