欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:MybatisPlus功能使用

柚子快報邀請碼778899分享:MybatisPlus功能使用

http://yzkb.51969.com/

版本與基礎(chǔ)依賴

com.baomidou

mybatis-plus-boot-starter

3.5.3.1

com.baomidou

mybatis-plus-generator

3.5.3.1

mysql

mysql-connector-java

8.0.21

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

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().eq("code","lyh"));

return "success";

}

操作結(jié)果,看見語句日志自動多了AND deleted=0,并且刪除改為update更新

最后再查詢

@RequestMapping("/qupt")

public Pertwo qust(){

Pertwo p = pertwoServiceImpl.getOne(new QueryWrapper().eq("code","lyh"));

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().eq("code","lyh"));

return p;

}

頁面查詢結(jié)果

如果不用@JsonValue,顯示為:

使用枚舉作為條件構(gòu)造查詢

@RequestMapping("/mupt")

public Pertwo must(){

Pertwo p = pertwoServiceImpl.getOne(new QueryWrapper().eq("age",AgeEnum.THREE));

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 job;

public Map getJob() {

return job;

}

public void setJob(Map job) {

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 = new HashMap<>();

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 parameter, JdbcType jdbcType)

throws SQLException {

String hobbys = dealListToOneStr(parameter);

ps.setString(i , hobbys);

}

/**

* 集合拼接字符串

* @param parameter

* @return

*/

private String dealListToOneStr(List parameter){

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 getResult(ResultSet rs, String columnName)

throws SQLException {

log.info("method ====>>> getResult(ResultSet rs, String columnName)");

return Arrays.asList(rs.getString(columnName).split(","));

}

@Override

public List getResult(ResultSet rs, int columnIndex)

throws SQLException {

log.info("method ====>>> getResult(ResultSet rs, int columnIndex)");

return Arrays.asList(rs.getString(columnIndex).split(","));

}

@Override

public List getResult(CallableStatement cs, int columnIndex) throws SQLException{

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 job;

public Map getJob() {

return job;

}

public void setJob(Map job) {

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

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 = new ArrayList<>();

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().eq("age",AgeEnum.THREE));

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 extends BaseMapper {

//查詢所有用戶

public List findAll();

}

已有mapper繼承通用mapper

不需要加@Component注解

/**

* 繼承MyBaseMapper, 自定義通用mapper接口

*/

public interface PertwoMapper extends MyBaseMapper {

}

自定義sql方法類

寫一個自定義sql方法類,此類分版本

com.baomidou

mybatis-plus-boot-starter

3.5.3.1

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 getMethodList(Class mapperClass) {

//獲取框架中已有的注入方法

List methodList = super.getMethodList(mapperClass);

//添加自己自定義方法

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 getMethodList(Class mapperClass, TableInfo tableInfo) {

//獲取框架中已有的注入方法

List methodList = super.getMethodList(mapperClass, tableInfo);

//添加自己自定義方法,參數(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 users = mapper.findAll();

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依賴引入

p6spy

p6spy

3.9.1

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

com.baomidou

mybatis-plus-boot-starter

3.3.2

com.baomidou

mybatis-plus-generator

3.5.3.1

使用自帶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

com.baomidou

dynamic-datasource-spring-boot-starter

3.6.1

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 findAll();

}

MP整體插件

MybatisPlusInterceptor

該插件是核心插件,目前代理了 Executor#query 和 Executor#update 和 StatementHandler#prepare 方法

屬性

private List interceptors = new ArrayList<>();

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 implements IUserService {

@Autowired

private UserMapper userMapper;

public List page3(){

//1為當前頁數(shù),3為每頁顯示條數(shù)

Page p = new Page<>(1,3);

Page userPage = userMapper.selectPage(p, null);

List records = userPage.getRecords();

return records;

}

}

Controller

@RestController

@RequestMapping("/user")

public class UserController {

@Autowired

private UserServiceImpl ui;

//localhost:8080/user/page3

@RequestMapping("/page3")

public List page3(){

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 implements IPage {

private static final long serialVersionUID = 8545996863226528798L;

//用于存儲分頁查詢結(jié)果的數(shù)據(jù)列表

protected List records;

//用于表示查詢結(jié)果的總記錄數(shù)

protected long total;

//用于表示分頁要查詢的 每頁記錄數(shù)

protected long size;

//表示分頁查詢中的當前頁碼,提供了關(guān)于正在訪問的特定結(jié)果頁的信息

protected long current;

//用于存儲排序規(guī)則,這樣可以在查詢時指定多個排序條件,以便按照特定順序?qū)Y(jié)果進行排序。

protected List orders;

//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().eq("name","貝斯特"));//將name是貝斯特的,按實體類u進行更新

userService.update(u,new UpdateWrapper().eq("name","貝斯特"));//將name是貝斯特的,按實體類u進行更新

}

}

驗證

第一步,打斷點執(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().eq("name","貝斯特"));

}

}

結(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 updateWrapper);

}

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().eq("name","阿比西尼亞"));

}

}

結(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(@Param("cm") Map columnMap);

}

這樣,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().eq("name","貝斯特"));

}

}

結(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 role = new 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 ro(@RequestParam Integer r){

GetRole.setRole(r);

List ul = userMapper.selectList(new QueryWrapper().eq("email","qq.com"));

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 = new ArrayList<>();

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依賴

org.springframework.boot

spring-boot-starter-parent

2.7.2

org.springframework.boot

spring-boot-starter

org.springframework.boot

spring-boot-starter-test

test

org.springframework.boot

spring-boot-starter-web

com.baomidou

mybatis-plus-boot-starter

3.5.3.1

com.baomidou

mybatis-plus-generator

3.5.3.1

mysql

mysql-connector-java

8.0.21

com.alibaba

druid

1.2.16

org.apache.shardingsphere

sharding-jdbc-spring-boot-starter

4.1.1

org.apache.shardingsphere

sharding-core-common

4.1.1

org.projectlombok

lombok

注意,連接池要用druid而不是druid-spring-boot-starter

如果使用下面這個:

com.alibaba

druid-spring-boot-starter

1.2.16

會報錯: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 job;

public List getJob() {

return job;

}

public void setJob(List job) {

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> extends BaseTypeHandler {

private Class type;

private Method getId;

private Method getEnumById;

public GenericEnumTypeHandler(Class type) {

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 parameter, JdbcType jdbcType)

throws SQLException {

String hobbys = dealListToOneStr(parameter);

ps.setString(i , hobbys);

}

/**

* 集合拼接字符串

* @param parameter

* @return

*/

private String dealListToOneStr(List parameter){

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 getResult(ResultSet rs, String columnName)

throws SQLException {

log.info("method ====>>> getResult(ResultSet rs, String columnName)");

return Arrays.asList(rs.getString(columnName).split(","));

}

@Override

public List getResult(ResultSet rs, int columnIndex)

throws SQLException {

log.info("method ====>>> getResult(ResultSet rs, int columnIndex)");

return Arrays.asList(rs.getString(columnIndex).split(","));

}

@Override

public List getResult(CallableStatement cs, int columnIndex) throws SQLException{

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 = new HashMap<>();//這是配置的druid監(jiān)控的登錄密碼

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 initParams = new HashMap<>();

//過濾掉需要監(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 dataSourceCollection, PreciseShardingValue preciseShardingValue) {

// 獲取分片鍵的值,也就是分片字段的值

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

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().eq("age",AgeEnum.THREE));

Pertwo p = pertwoServiceImpl.getOne(new QueryWrapper().eq("sex","男"));

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 = new ArrayList<>();

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 = new ArrayList<>();

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 dataSourceCollection, PreciseShardingValue preciseShardingValue) {

// 獲取分片鍵的值,也就是分片字段的值

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功能使用

http://yzkb.51969.com/

參考閱讀

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19016243.html

發(fā)布評論

您暫未設(shè)置收款碼

請在主題配置——文章設(shè)置里上傳

掃描二維碼手機訪問

文章目錄