- 1. 简单使用以及配置 - 带分页配置
- 2. 用法
- 2.0 Wrapper属性
- 2.1 @TableId - 自定义主键生成策略
- 2.2 @TableField - 自定义字段值填充
- 2.3 锁 - 乐观锁、悲观锁
- 2.3.1 @Version - 乐观锁
- 2.4 DML、DQL操作
- 2.4.1 DML - 增删改 - 物理
- 2.4.2 DML - 逻辑删除 - @TableLogic - 仅对注入MyBatisPlus注入的SQL生效,自定义SQL不生效
- 2.4.3 DQL - 查
- 2.4.4 条件构造器
- 2.5 性能分析、SQL打印插件(p6spy) - 支持SQL格式化
- 2.6 代码生成器
- 2.7 自定义主键值策略填充(IdType.INPUT)
- 2.8 动态表名-根据入参决定执行哪个表-会影响手写的SQL语句且手写SQL记得写表别名 - 手写SQL中所有的表名都会经过这层
- 内置SQL
- 手写SQL - 必须起表别名因为动态表单插件仅改from表名,where属性的表名不会改
- 2.9 多租户 - 手写SQL中所有的表名都会经过这层 - 主表的租户字段加在where上,连表的租户字段加在on上
- 2.10 枚举@EnumValue
- 2.11 类型处理器BaseTypeHandler - 必须开启TableName.autoResultMap=true
- 2.12 连表自定义SQL - 配合wrapper使用
pom.xml - 依赖
org.springframework.boot spring-boot-starter-web mysql mysql-connector-java runtime org.projectlombok lombok com.baomidou mybatis-plus-boot-starter 3.3.2 org.springframework.boot spring-boot-devtools org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine
application.yml
spring:
application:
name: mybatisplusStudy
datasource:
data-password: root
password: root
username: 123456
url: jdbc:mysql://localhost:3306/interview?serverTimezone=UTC&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
server:
servlet:
context-path: /mybatisplus
port: 80
mybatis-plus:
# mapper文件的位置
mapper-locations: classpath:/mappers
@Override
public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) {
//格式化SQL
String sqlContent = sql.replaceAll("[\s]+", " ");
sqlContent = SqlUtil.formatSql(sqlContent);
return StringUtils.isNotBlank(sql) ? " Consume Time:" + elapsed + " ms " + now +
"n Execute SQL:n" + sqlContent + "n" : "";
}
}
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 # 自定义日志打印 # 官网的SQL没有格式化 #logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger # SQL格式化输出 logMessageFormat=work.linruchang.mybatisplussamplestest.config.mybatisplus.EnhaceP6SpyLogger #日志输出到控制台 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 使用日志系统记录 sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger # 设置 p6spy driver 代理 deregisterdrivers=true # 取消JDBC URL前缀 useprefix=true # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 实际驱动可多个 #driverlist=org.h2.Driver # 是否开启慢SQL记录 outagedetection=true # 慢SQL记录标准 2 秒 outagedetectioninterval=2
测试
@Test
public void test10() {
HashMap map = new HashMap();
map.put("sport", "泡妞");
List students = studentDao.selectByMap(map);
students.forEach(System.out::println);
}
格式化前-配置官网的com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
格式化后:配置增强的EnhaceP6SpyLogger
org.springframework.boot spring-boot-starter-web mysql mysql-connector-java runtime com.baomidou mybatis-plus-boot-starter 3.2.0 com.baomidou mybatis-plus-generator 3.3.2 io.springfox springfox-swagger2 2.9.2 io.springfox springfox-swagger-ui 2.9.2 org.apache.velocity velocity-engine-core 2.0 org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine
@Test
public void test16() {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 1. 全局配置
GlobalConfig gc = new GlobalConfig();
//生成文件的输入目录
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("LinRuChang");
//生成文件后不打开文件管理器级【我的电脑】
gc.setOpen(false);
//添加swagger注解在生成文件中
gc.setSwagger2(true);
//设置文件生成日期注释所使用的日期类
gc.setDateType(DateType.ONLY_DATE);
//设置插入记录时,ID主键生成的值
gc.setIdType(IdType.ASSIGN_UUID);
//生成的文件不覆盖已有的文件
gc.setFileOverride(false);
//service类,mapper类即dao类,mapper的xml文件的名字 %s表示表名
gc.setServiceName("%sService");
gc.setMapperName("%sDao");
gc.setXmlName("%sMapper");
mpg.setGlobalConfig(gc);
// 2. 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/book?serverTimezone=UTC&useSSL=false&characterEncoding=utf8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 3. 包配置
PackageConfig pc = new PackageConfig();
//将该模块的文件放在哪个包里
pc.setParent("top.linruchang.springbootmybatisautogenerator");
//模块名 - 可以不配 -
pc.setModuleName("");
//控制层、实体类层、service层,dao层的包名设置
pc.setController("controller");
pc.setEntity("dto");
pc.setService("service");
pc.setMapper("dao");
mpg.setPackageInfo(pc);
// 3. 策略配置 - 实体类、controller类生成规则
StrategyConfig strategy = new StrategyConfig();
//实体类名的设置 - 表名转驼峰命名
strategy.setNaming(NamingStrategy.underline_to_camel);
//实体类的字段名的设置 - 字段名名转驼峰命名
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//是否使用lombok注解在实体类中
strategy.setEntityLombokModel(true);
//控制器中的含有@RestController注解 - 常用于前后端分离
strategy.setRestControllerStyle(true);
// 写于父类中的公共字段 - 实体类中的公共字段
strategy.setSuperEntityColumns("id", "version", "deleted");
//注意这个类需要自己写哦 - 即写上面定义的字段
strategy.setSuperEntityClass(BaseDTO.class);
//TODO 表名 - 准备映射的表
strategy.setInclude("class_type", "student");
strategy.setControllerMappingHyphenStyle(true);
//逻辑删除字段
strategy.setLogicDeleteFieldName("deleted");
//乐观锁字段
strategy.setVersionFieldName("version");
//字段值填充
TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
strategy.setTableFillList(Arrays.asList(createTime, updateTime));
mpg.setStrategy(strategy);
//执行代码生成
mpg.execute();
}
注意: 一旦设置了keyGenerator,如果没有全局指定ID的生成策略或在基类中的@TableId中指定主键的生成策略,则默认为IdType.INPUT,则全部的主键值都由keyGenerator进行生成
ArticleTag.java
@Data
@ToString(callSuper=true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("article_tag")
//用于com.baomidou.mybatisplus.core.incrementer.IKeyGenerator#executeSql的入参
@KeySequence("ArticleTag")
public class ArticleTag extends BaseDB implements Serializable {
@TableId(type= IdType.INPUT)
private String id;
private static final long serialVersionUID = -8898734581249698571L;
private String tagName;
private String userId;
private Integer isViolation;
}
MyBatisPlusConfig.java
@Configuration
public class MyBatisPlusConfig {
@Bean
public IKeyGenerator keyGenerator() {
return new IKeyGenerator(){
@Override
public String executeSql(String incrementerName) {
incrementerName = StrUtil.nullToEmpty(StrUtil.trim(incrementerName));
return StrUtil.format("select CONCAt('{}',SUBSTr(REPLACE(uuid(),'-',''),1,32-LENGTH('{}')))",incrementerName,incrementerName );
}
@Override
public DbType dbType() {
return DbType.MYSQL;
}
};
}
}
ArticleTagDao.java
@Mapper public interface ArticleTagDao extends BaseMapper{ }
ArticleTagService.java
@Service public class ArticleTagService extends ServiceImpl{ }
ArticleTagServiceTest.java
@SpringBootTest
class ArticleTagServiceTest {
@Autowired
ArticleTagService articleTagService;
@Test
public void keySequenceTest1() {
ArticleTag articleTag = new ArticleTag();
articleTag.setTagName("你好");
boolean insert = articleTag.insert();
Console.log("插入情况:{}", insert);
Console.log("实体情况:{}", articleTag);
}
}
MyBatisPlusConfig.java
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//防全表更新与删除插件
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setDbType(DbType.MYSQL);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
//乐观锁,字段添加version注解
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//动态表单
interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor(() -> {
Console.log("动态表单执行成功");
}, (sql, tableName) -> {
Console.log("当前执行的SQL:{}n 表名:{}", sql,tableName);
Map requestData = RequestDataHelper.getRequestData();
String tableNum = null;
if(CollUtil.isNotEmpty(requestData)) {
String mapValueContent = requestData.values()
.stream()
.map(Object::toString)
.reduce(StrUtil.EMPTY, (t1, t2) -> {
return StrUtil.join(StrUtil.DOT, t1, t2);
});
int mapHashValue = HashUtil.fnvHash(mapValueContent);
int modulus = mapHashValue % 3;
tableNum = String.valueOf(modulus);
Console.log("当前参数的Hash值:{}: 取模后:{}",mapHashValue,modulus);
}
String resultTableName = tableName;
if(StrUtil.isNotBlank(tableNum)) {
resultTableName = StrUtil.format("{}_{}", tableName, tableNum);
}
RequestDataHelper.setRequestData(null);
return resultTableName;
}));
return interceptor;
}
}
SysUser.java
@Data
@ToString(callSuper=true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("sys_user")
public class SysUser extends BaseDB implements Serializable {
private static final long serialVersionUID = 2095940921263481761L;
private String nickName;
private String headUrl;
private String loginName;
private String phoneNumber;
private String email;
private Integer gender;
@TableField("gender")
private GenderEnum genderEnum;
private String personalMotto;
//@ApiModelProperty("我的分类专栏,逗号分隔 - article_category表的id")
//private String myArticleCategory;
private String lastLoginTime;
private Integer loginStatus;
private Integer disabledStatus;
private String password;
}
SysUserDao.java
@Mapper public interface SysUserDao extends MyBaseMapper{ }
SysUserService.java
@Service public class SysUserService extends ServiceImpl{ }
SysUserServiceTest.java
@SpringBootTest
class SysUserServiceTest {
@Test
public void test12() {
List nicknames = null;
Console.log("n========sys_user=========n");
RequestDataHelper.setRequestData(null);
List sysUsers = sysUserService.listByIds(Arrays.asList("c08d391e02bc11eb9416b42e99ea3e69", "dd9918601ef8071877cd75b6f1a21ad2"));
nicknames = sysUsers.stream()
.map(SysUser::getNickName)
.collect(Collectors.toList());
Console.log(sysUsers);
Console.log(nicknames);
Console.log("n========sys_user_0=========n");
RequestDataHelper.setRequestData(new Dict()
.set("id","4"));
sysUsers = sysUserService.listByIds(Arrays.asList("c08d391e02bc11eb9416b42e99ea3e69", "dd9918601ef8071877cd75b6f1a21ad2"));
nicknames = sysUsers.stream()
.map(SysUser::getNickName)
.collect(Collectors.toList());
Console.log(sysUsers);
Console.log(nicknames);
Console.log("n========sys_user_1=========n");
RequestDataHelper.setRequestData(new Dict()
.set("id","8"));
sysUsers = sysUserService.listByIds(Arrays.asList("c08d391e02bc11eb9416b42e99ea3e69", "dd9918601ef8071877cd75b6f1a21ad2"));
nicknames = sysUsers.stream()
.map(SysUser::getNickName)
.collect(Collectors.toList());
Console.log(sysUsers);
Console.log(nicknames);
Console.log("n========sys_user_2=========n");
RequestDataHelper.setRequestData(new Dict()
.set("id","5"));
sysUsers = sysUserService.listByIds(Arrays.asList("c08d391e02bc11eb9416b42e99ea3e69", "dd9918601ef8071877cd75b6f1a21ad2"));
nicknames = sysUsers.stream()
.map(SysUser::getNickName)
.collect(Collectors.toList());
Console.log(sysUsers);
Console.log(nicknames);
}
}
执行结果
MyBatisPlusConfig.java
@Configuration
public class MyBatisPlusConfig {
@Bean
public IKeyGenerator keyGenerator() {
return new IKeyGenerator() {
@Override
public String executeSql(String incrementerName) {
incrementerName = StrUtil.nullToEmpty(StrUtil.trim(incrementerName));
return StrUtil.format("select CONCAt('{}',SUBSTr(REPLACE(uuid(),'-',''),1,32-LENGTH('{}')))", incrementerName, incrementerName);
}
@Override
public DbType dbType() {
return DbType.MYSQL;
}
};
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//动态表单
interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor(() -> {
Console.log("动态表单执行成功");
}, (sql, tableName) -> {
Console.log("当前执行的SQL:{}n 表名:{}", sql, tableName);
Map requestData = RequestDataHelper.getRequestData();
String tableNum = null;
if (CollUtil.isNotEmpty(requestData)) {
String mapValueContent = requestData.values()
.stream()
.map(Object::toString)
.reduce(StrUtil.EMPTY, (t1, t2) -> {
return StrUtil.join(StrUtil.DOT, t1, t2);
});
int mapHashValue = HashUtil.fnvHash(mapValueContent);
int modulus = mapHashValue % 3;
tableNum = String.valueOf(modulus);
Console.log("当前参数的Hash值:{}: 取模后:{}", mapHashValue, modulus);
}
String resultTableName = tableName;
if (StrUtil.isNotBlank(tableNum)) {
resultTableName = StrUtil.format("{}_{}", tableName, tableNum);
RequestDataHelper2.setRequestData(resultTableName, TableInfoHelper.getTableInfo(tableName));
}
return resultTableName;
}));
//乐观锁,字段添加version注解
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//防全表更新与删除插件
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
//租户模式
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
Object tenant = RequestDataHelper.getRequestData("tenant");
return tenant == null ? null : new StringValue(tenant.toString());
}
@Override
public String getTenantIdColumn() {
return "tenant";
}
@Override
public boolean ignoreTable(String tableName) {
//无租户值则直接忽略拼接租户字段
Object tenant = RequestDataHelper.getRequestData("tenant");
boolean tenantValueFlag = ObjectUtil.isNotEmpty(tenant);
//根据实体是否定义tenant属性来评判是否需要租户条件 == 经过动态表单的表名可能非com.baomidou.mybatisplus.annotation.TableName.value定义的表名
TableInfo tableInfo = ObjectUtil.defaultIfNull(TableInfoHelper.getTableInfo(tableName),(TableInfo)RequestDataHelper2.getRequestData(tableName));
boolean tanantEntityFlag = tableInfo.getFieldList().stream()
.anyMatch(tableFieldInfo -> {
return StrUtil.equals(tableFieldInfo.getColumn(), "tenant");
});
//需要租户的表
return !tanantEntityFlag && (!tenantValueFlag || !Arrays.asList(SysUser.class, FriendlyLink.class).stream()
.map(TableInfoHelper::getTableInfo)
.map(TableInfo::getTableName)
.anyMatch(t -> StrUtil.contains(tableName,t)));
}
}));
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setDbType(DbType.MYSQL);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
@Bean
public ISqlInjector enhanceISqlInjector() {
return new DefaultSqlInjector() {
@Override
public List getMethodList(Class> mapperClass, TableInfo tableInfo) {
List methodList = super.getMethodList(mapperClass, tableInfo);
methodList.add(new DeleteByIdPhysical());
methodList.add(new DeleteByIdLogic());
return methodList;
}
};
}
}
SysUser.java
@Data
@ToString(callSuper=true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("sys_user")
public class SysUser extends BaseDB implements Serializable {
private static final long serialVersionUID = 2095940921263481761L;
private String nickName;
private String headUrl;
private String loginName;
private String phoneNumber;
private String email;
private Integer gender;
@TableField("gender")
private GenderEnum genderEnum;
private String personalMotto;
//@ApiModelProperty("我的分类专栏,逗号分隔 - article_category表的id")
//private String myArticleCategory;
private String lastLoginTime;
private Integer loginStatus;
private Integer disabledStatus;
private String password;
private String tenant;
}
FriendlyLink.java
@Data
@ToString(callSuper=true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("friendly_link")
public class FriendlyLink extends BaseDB{
private static final long serialVersionUID = -4714261187453073302L;
private String userId;
private String name;
private String url;
private Integer sort;
private String type;
private String jumpFlag;
private String params;
private String tenant;
}
SysUserDao.java
public interface SysUserDao extends MyBaseMapper{ SysUser findOneById(String id); Map findOneByIdAndFriendLink(String id); Map findOneByIdAndFriendLink2(String id); }
SysUserMapper.xml
SysUserServiceTest.java
@SpringBootTest
class SysUserServiceTest {
@Test
public void test15() {
Map sysUser = sysUserDao.findOneByIdAndFriendLink("d32478155b6530951cf6b3da56848d5c");
Console.log(sysUser);
RequestDataHelper.setRequestData(new Dict().set("tenant", 1));
sysUser = sysUserDao.findOneByIdAndFriendLink("d32478155b6530951cf6b3da56848d5c");
Console.log(sysUser);
}
@Test
public void test16() {
Map sysUser = sysUserDao.findOneByIdAndFriendLink2("d32478155b6530951cf6b3da56848d5c");
Console.log(sysUser);
RequestDataHelper.setRequestData(new Dict().set("tenant", 1));
sysUser = sysUserDao.findOneByIdAndFriendLink2("d32478155b6530951cf6b3da56848d5c");
Console.log(sysUser);
}
}
上面是:findOneByIdAndFriendLink,下面是:findOneByIdAndFriendLink2。避免Bug请手写SQL的时候起表别名
查看动态表名-手写SQL的代码
MyBatisPlusConfig.java
@Configuration
public class MyBatisPlusConfig {
@Bean
public IKeyGenerator keyGenerator() {
return new IKeyGenerator() {
@Override
public String executeSql(String incrementerName) {
incrementerName = StrUtil.nullToEmpty(StrUtil.trim(incrementerName));
return StrUtil.format("select CONCAt('{}',SUBSTr(REPLACE(uuid(),'-',''),1,32-LENGTH('{}')))", incrementerName, incrementerName);
}
@Override
public DbType dbType() {
return DbType.MYSQL;
}
};
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//动态表单
interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor(() -> {
Console.log("动态表单执行成功");
}, (sql, tableName) -> {
Console.log("当前执行的SQL:{}n 表名:{}", sql, tableName);
boolean needDynamicFlag = Arrays.asList(SysUser.class).stream()
.map(TableInfoHelper::getTableInfo)
.map(TableInfo::getTableName)
.anyMatch(t -> StrUtil.equals(t, tableName));
if(!needDynamicFlag) {return tableName;}
Map requestData = RequestDataHelper.getRequestData();
String tableNum = null;
if (CollUtil.isNotEmpty(requestData)) {
String mapValueContent = requestData.values()
.stream()
.map(Object::toString)
.reduce(StrUtil.EMPTY, (t1, t2) -> {
return StrUtil.join(StrUtil.DOT, t1, t2);
});
int mapHashValue = HashUtil.fnvHash(mapValueContent);
int modulus = mapHashValue % 3;
tableNum = String.valueOf(modulus);
Console.log("当前参数的Hash值:{}: 取模后:{}", mapHashValue, modulus);
}
String resultTableName = tableName;
if (StrUtil.isNotBlank(tableNum)) {
resultTableName = StrUtil.format("{}_{}", tableName, tableNum);
RequestDataHelper2.setRequestData(resultTableName, TableInfoHelper.getTableInfo(tableName));
}
return resultTableName;
}));
//乐观锁,字段添加version注解
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//防全表更新与删除插件
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
//租户模式
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
Object tenant = RequestDataHelper.getRequestData("tenant");
return tenant == null ? null : new StringValue(tenant.toString());
}
@Override
public String getTenantIdColumn() {
return "tenant";
}
@Override
public boolean ignoreTable(String tableName) {
//无租户值则直接忽略拼接租户字段
Object tenant = RequestDataHelper.getRequestData("tenant");
boolean tenantValueFlag = ObjectUtil.isNotEmpty(tenant);
//根据实体是否定义tenant属性来评判是否需要租户条件 == 经过动态表单的表名可能非com.baomidou.mybatisplus.annotation.TableName.value定义的表名
TableInfo tableInfo = ObjectUtil.defaultIfNull(TableInfoHelper.getTableInfo(tableName),(TableInfo)RequestDataHelper2.getRequestData(tableName));
boolean tanantEntityFlag = tableInfo.getFieldList().stream()
.anyMatch(tableFieldInfo -> {
return StrUtil.equals(tableFieldInfo.getColumn(), "tenant");
});
//需要租户的表
return !tanantEntityFlag || !tenantValueFlag || !Arrays.asList(SysUser.class, FriendlyLink.class).stream()
.map(TableInfoHelper::getTableInfo)
.map(TableInfo::getTableName)
.anyMatch(t -> StrUtil.contains(tableName,t));
}
}));
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setDbType(DbType.MYSQL);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
@Bean
public ISqlInjector enhanceISqlInjector() {
return new DefaultSqlInjector() {
@Override
public List getMethodList(Class> mapperClass, TableInfo tableInfo) {
List methodList = super.getMethodList(mapperClass, tableInfo);
methodList.add(new DeleteByIdPhysical());
methodList.add(new DeleteByIdLogic());
return methodList;
}
};
}
}
GenderEnum.java
@Getter
@AllArgsConstructor
@ToString
public enum GenderEnum {
NO_DELETE(null, "未知"),
MALE(0, "男"),
FEMALE(1, "女");
@EnumValue
Integer dbValue;
String desc;
}
IsDelEnum.java
@Getter
@AllArgsConstructor
@ToString
public enum IsDelEnum {
NO_DELETE(0, "未删除"),
DELETED(1, "已删除");
@EnumValue
Integer dbValue;
String desc;
}
SysUser.java
@Data
@ToString(callSuper=true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("sys_user")
public class SysUser extends BaseDB implements Serializable {
private static final long serialVersionUID = 2095940921263481761L;
private String nickName;
private String headUrl;
private String loginName;
private String phoneNumber;
private String email;
private Integer gender;
@TableField("gender")
private GenderEnum genderEnum;
private String personalMotto;
//@ApiModelProperty("我的分类专栏,逗号分隔 - article_category表的id")
//private String myArticleCategory;
private String lastLoginTime;
private Integer loginStatus;
private Integer disabledStatus;
private String password;
}
SysUserDao.java
@Mapper public interface SysUserDao extends MyBaseMapper{ }
SysUserServiceTest.java
@SpringBootTest
class SysUserServiceTest {
@Test
public void test11() {
List sysUsers = sysUserService.listByIds(Arrays.asList("c08d391e02bc11eb9416b42e99ea3e69", "dd9918601ef8071877cd75b6f1a21ad2"));
Console.log(CollectionUtil.size(sysUsers));
Console.log(sysUsers);
}
}
SysUser.java
@Data @ToString(callSuper=true) @Builder @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @TableName(value = "sys_user", autoResultMap = true) public class SysUser extends BaseDBimplements Serializable { private static final long serialVersionUID = 2095940921263481761L; private String nickName; private String headUrl; private String loginName; private String phoneNumber; private String email; private Integer gender; @TableField("gender") private GenderEnum genderEnum; private String personalMotto; //@ApiModelProperty("我的分类专栏,逗号分隔 - article_category表的id") //private String myArticleCategory; private String lastLoginTime; private Integer loginStatus; private Integer disabledStatus; private String password; private String tenant; @TableField(typeHandler = WalletListTypeHandler.class) private List wallets; @TableField(typeHandler = HutooljsonTypeHandler.class) private OtherInfo otherInfo; }
Wallet.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Wallet {
private String name;
private List currencyList;
}
OtherInfo.java
@Data
public class OtherInfo {
private String sex;
private String city;
}
Currency.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Currency {
private String type;
private Double amount;
}
HutooljsonTypeHandler.java
@Slf4j
@MappedTypes({Object.class})
@MappedJdbcTypes(JdbcType.VARCHAR)
public class HutooljsonTypeHandler extends AbstractJsonTypeHandler
WalletListTypeHandler.java
@Slf4j
@MappedTypes({Object.class})
@MappedJdbcTypes(JdbcType.VARCHAR)
public class WalletListTypeHandler extends HutooljsonTypeHandler {
public WalletListTypeHandler(Class> type) {
super(type);
}
@Override
protected Object parse(String json) {
return JSONUtil.toBean(json, new TypeReference>() {
},true);
}
}
SysUserDao.java
@Mapper public interface SysUserDao extends MyBaseMapper{ }
SysUserServiceTest.java
@SpringBootTest
class SysUserServiceTest {
@Test
public void test17() {
SysUser sysUser = sysUserDao.selectById("6d72c93aa292cf2ca2e789919a5e7bdc");
Console.log(sysUser);
sysUser = sysUserDao.selectById("c08d391e02bc11eb9416b42e99ea3e69");
Console.log(sysUser);
}
}
SysUser.java
@Data @ToString(callSuper=true) @Builder @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @TableName(value = "sys_user", autoResultMap = true) public class SysUser extends BaseDBimplements Serializable { private static final long serialVersionUID = 2095940921263481761L; private String nickName; private String headUrl; private String loginName; private String phoneNumber; private String email; private Integer gender; @TableField("gender") private GenderEnum genderEnum; private String personalMotto; //@ApiModelProperty("我的分类专栏,逗号分隔 - article_category表的id") //private String myArticleCategory; private String lastLoginTime; private Integer loginStatus; private Integer disabledStatus; private String password; private String tenant; @TableField(typeHandler = WalletListTypeHandler.class) private List wallets; @TableField(typeHandler = HutooljsonTypeHandler.class) private OtherInfo otherInfo; }
FriendlyLink.java
@Data
@ToString(callSuper=true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("friendly_link")
public class FriendlyLink extends BaseDB{
private static final long serialVersionUID = -4714261187453073302L;
private String userId;
private String name;
private String url;
private Integer sort;
private String type;
private String jumpFlag;
private String params;
private String tenant;
}
MyBatisPlusConfig.java
@Configuration
public class MyBatisPlusConfig {
@Bean
public IKeyGenerator keyGenerator() {
return new IKeyGenerator() {
@Override
public String executeSql(String incrementerName) {
incrementerName = StrUtil.nullToEmpty(StrUtil.trim(incrementerName));
return StrUtil.format("select CONCAt('{}',SUBSTr(REPLACE(uuid(),'-',''),1,32-LENGTH('{}')))", incrementerName, incrementerName);
}
@Override
public DbType dbType() {
return DbType.MYSQL;
}
};
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//动态表单
interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor(() -> {
Console.log("动态表单执行成功");
}, (sql, tableName) -> {
Console.log("当前执行的SQL:{}n 表名:{}", sql, tableName);
boolean needDynamicFlag = Arrays.asList(SysUser.class).stream()
.map(TableInfoHelper::getTableInfo)
.map(TableInfo::getTableName)
.anyMatch(t -> StrUtil.equals(t, tableName));
if(!needDynamicFlag) {return tableName;}
Map requestData = RequestDataHelper.getRequestData();
String tableNum = null;
if (CollUtil.isNotEmpty(requestData)) {
String mapValueContent = requestData.values()
.stream()
.map(Object::toString)
.reduce(StrUtil.EMPTY, (t1, t2) -> {
return StrUtil.join(StrUtil.DOT, t1, t2);
});
int mapHashValue = HashUtil.fnvHash(mapValueContent);
int modulus = mapHashValue % 3;
tableNum = String.valueOf(modulus);
Console.log("当前参数的Hash值:{}: 取模后:{}", mapHashValue, modulus);
}
String resultTableName = tableName;
if (StrUtil.isNotBlank(tableNum)) {
resultTableName = StrUtil.format("{}_{}", tableName, tableNum);
RequestDataHelper2.setRequestData(resultTableName, TableInfoHelper.getTableInfo(tableName));
}
return resultTableName;
}));
//乐观锁,字段添加version注解
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//防全表更新与删除插件
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
//租户模式
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
Object tenant = RequestDataHelper.getRequestData("tenant");
return tenant == null ? null : new StringValue(tenant.toString());
}
@Override
public String getTenantIdColumn() {
return "tenant";
}
@Override
public boolean ignoreTable(String tableName) {
//无租户值则直接忽略拼接租户字段
Object tenant = RequestDataHelper.getRequestData("tenant");
boolean tenantValueFlag = ObjectUtil.isNotEmpty(tenant);
//根据实体是否定义tenant属性来评判是否需要租户条件 == 经过动态表单的表名可能非com.baomidou.mybatisplus.annotation.TableName.value定义的表名
TableInfo tableInfo = ObjectUtil.defaultIfNull(TableInfoHelper.getTableInfo(tableName),(TableInfo)RequestDataHelper2.getRequestData(tableName));
boolean tanantEntityFlag = tableInfo.getFieldList().stream()
.anyMatch(tableFieldInfo -> {
return StrUtil.equals(tableFieldInfo.getColumn(), "tenant");
});
//需要租户的表
return !tanantEntityFlag || !tenantValueFlag || !Arrays.asList(SysUser.class, FriendlyLink.class).stream()
.map(TableInfoHelper::getTableInfo)
.map(TableInfo::getTableName)
.anyMatch(t -> StrUtil.contains(tableName,t));
}
}));
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setDbType(DbType.MYSQL);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
@Bean
public ISqlInjector enhanceISqlInjector() {
return new DefaultSqlInjector() {
@Override
public List getMethodList(Class> mapperClass, TableInfo tableInfo) {
List methodList = super.getMethodList(mapperClass, tableInfo);
methodList.add(new DeleteByIdPhysical());
methodList.add(new DeleteByIdLogic());
return methodList;
}
};
}
}
SysUserDao.java
@Mapper public interface SysUserDao extends MyBaseMapper{ IPage
SysUserMapper.xml
select * from sys_user as sys_user left join friendly_link as friendly_link on friendly_link.user_id = sys_user.id ${ew.sqlSegment}
SysUserServiceTest.java
@SpringBootTest
class SysUserServiceTest {
@Test
public void test18() {
RequestDataHelper.setRequestData(new Dict().set("tenant", 1));
Page pageInfo = new Page<>(2, 1);
LambdaQueryWrapper wrapper = Wrappers.query()
.like("sys_user.id", "1536530866313883649")
.eq("friendly_link.name", "Gitee")
.or().lambda()
.like(SysUser::getNickName, "嗯嗯**");
IPage dataPage = sysUserDao.findOneByIdAndFriendLink2Page(pageInfo, wrapper);
dataPage.getRecords().stream().forEach(Console::log);
}
}



