本文共 12955 字,大约阅读时间需要 43 分钟。
MybatisPlus基础篇请看:
基本实体类:
@Builder@AllArgsConstructor@NoArgsConstructor@Datapublic class Employee { // id private Long id; // 名称 private String name; // 年龄 @TableField(fill = FieldFill.UPDATE) private Integer age; // 邮箱 private String email; // 上级id private Long managerId; /** 设置自动填充 */ // 创建时间 @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; // 更新时间 @TableField(fill = FieldFill.UPDATE) private LocalDateTime updateTime; // 版本号 @Version private Integer version; // 逻辑删除的 局部设置 @TableField(select = false) // 查询的时候,不显示这个参数 @TableLogic private Integer deleted; // 子级 @TableField(exist = false) private Listchildren = new ArrayList<>();}
springboot中配置:
# 配置逻辑删除的值 在这里配置的是 删除的时候为1 未删除的时候为0mybatis-plus: global-config: db-config: logic-not-delete-value: 0 logic-delete-value: 1
需要在实体类的字段上加上逻辑删除的注解:
// 删除的时候为1 未删除的时候为0 @TableLogic private Integer deleted;
测试逻辑删除:
// update employee set deleted = 1 where id = ? and deleted = 0boolean b = employeeService.removeById(1237753494614700033L);// 查询输出后将不会显示刚刚被删除的那条 select * from employee where deleted = 1Listlist = employeeService.list(null);// 并且只能更新未删除的int update = employeeMapper.update(null, lambdaUpdateWrapper);
逻辑删除注意事项:
【注意: 自定义的查询方法,系统 不会 自动加上 逻辑删除条件,需要自己手动加上】 在mapper中自定义的方法,需要自己手动加上逻辑删除限制条件
设置自动填充需要在字段上加上注解:
/** 设置自动填充 */ // 创建时间 @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; // 更新时间 @TableField(fill = FieldFill.UPDATE) private LocalDateTime updateTime;
配置字段自动填充:
@Componentpublic class MyMetaObjectHandler implements MetaObjectHandler { /** * 插入的时候自动填充 * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { // fieldName 是实体中的属性名称 // 检查是否有这个属性 boolean createTime = metaObject.hasSetter("createTime"); if (createTime) { this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); } } /** * 修改的时候自动填充 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { /** * 先检查时候已经传入值了,若传入值了,则以传入值为准,就不再自动填充 */ Object updateTime = getFieldValByName("updateTime", metaObject); // 若没有传入值才自动填充 if (updateTime == null) { this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } }}
测试自动填充以及注意事项:
Employee employee = new Employee();// 此时添加员工数据的时候会自动赋值上创建时间int rows = employeeMapper.insert(employee);// 这里修改会自动填充修改时间int rows = employeeMapper.updateById(employee);// 注意这种情况下【不会】自动填充修改时间 int update = employeeMapper.update(null, lambdaUpdateWrapper);// 有实体传入的情况下,会自动填充修改时间int update = employeeMapper.update(employee, lambdaUpdateWrapper);
配置乐观锁插件:
@Slf4j@Configurationpublic class MybatisPlusConfig { /** * 乐观锁插件 * @return */ @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }}
实体类中字段上加上乐观锁注解:
// 版本号 @Version private Integer version;
乐观锁测试:
boolean b = employeeService.updateById(employee);// 第二个参数为,【查询】 重点boolean update = employeeService.update(employee,lambdaQueryWrapper);
注意事项:
特别说明:
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
整数类型下 newVersion = oldVersion + 1
newVersion
会回写到 entity
中
仅支持 updateById(entity)
与 update(entity, wrapper)
方法
在 update(entity, wrapper)
方法下, wrapper
不能复用!!!
引入依赖:
p6spy p6spy 3.8.2
更改数据库配置:
spring: datasource: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql://127.0.0.1:3306/mybatisplus?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8 username: root password: root
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# 使用日志系统记录 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# 将日志输出到指定的位置#logfile=log.log
注意事项:
driver-class-name 为 p6spy 提供的驱动类 url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址 打印出sql为null,在excludecategories增加commit 批量操作不打印sql,去除excludecategories中的batch 批量操作打印重复的问题请使用MybatisPlusLogFactory (3.2.1新增) 该插件有性能损耗,不建议生产环境使用
需要在分页配置类中配置:
@Slf4j@Configurationpublic class MybatisPlusConfig { /** * 分页配置中,设置多租户信息 */ @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); ArrayListsqlParsers = new ArrayList<>(); // 租户解析器 TenantSqlParser tenantSqlParser = new TenantSqlParser(); tenantSqlParser.setTenantHandler(new TenantHandler() { @Override public Expression getTenantId(boolean where) { // 租户的id,这里测试固定写死 return new LongValue(1088248166370832385L); } // 多租户字段是什么 @Override public String getTenantIdColumn() { // 这是表中的字段名,即 用于区分租户的字段 return "manager_id"; } // 是否需要排除某些表,不加租户字段 @Override public boolean doTableFilter(String tableName) { // 为 角色 表的时候,不增加租户信息 if("role".equals(tableName)){ // 不增加租户信息 return true; } return false; } }); sqlParsers.add(tenantSqlParser); paginationInterceptor.setSqlParserList(sqlParsers); / 【特定sql 过滤】,可用注解替换 @SqlParser(filter = true) 为true表示,不在此方法上增加租户信息 / paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() { @Override public boolean doFilter(MetaObject metaObject) { MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject); if ("cn.mesmile.mybatisplusdemo.mapper.EmployeeMapper.selectAll" .equals(ms.getId())) { // 同时这个方法对 动态表明解析也会产生作用,也会过滤掉动态表名,只显示原来的表名 // 不增加租户信息 return true; } return false; } }); return paginationInterceptor; }}
过滤得到不使用多租户用户的方法:
@Componentpublic interface EmployeeMapper extends MyMapper{ /** * 这是自定义的查询方法 * @param wrapper */// @SqlParser(filter = true) 该注解为true 设置该方法,不需要要加入租户信息 @Select("select * from employee ${ew.customSqlSegment}") List selectAll(@Param(Constants.WRAPPER)Wrapper wrapper);}
配置动态表名解析器:
@Slf4j@Configurationpublic class MybatisPlusConfig { public static ThreadLocalmyTableName = new ThreadLocal<>(); /** * mybatis-plus 的分页配置里面配置 动态表名解析器 * @return */ @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); ArrayList sqlParsers = new ArrayList<>(); // 动态表名解析器 DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser(); HashMap tableNameHandlerHashMap = new HashMap<>(); // 第一参数是需要 动态替换的表名 tableNameHandlerHashMap.put("employee", new ITableNameHandler() { @Override public String dynamicTableName(MetaObject metaObject, String sql, String tableName) { log.info("metaObject: {}", metaObject); log.info("原表执行的 sql: {}", sql); log.info("原表的名称 tableName: {}", tableName); // 【这里返回动态的表名】 // 如果返回的值为空,则不进行替换 这里返回的事 动态表名 return myTableName.get(); } }); dynamicTableNameParser.setTableNameHandlerMap(tableNameHandlerHashMap); sqlParsers.add(dynamicTableNameParser); paginationInterceptor.setSqlParserList(sqlParsers); return paginationInterceptor; }}
注意:【在多租户配置这里 , 过滤的方法, 动态表名不会生效】
/ 【特定sql 过滤】,可用注解替换 @SqlParser(filter = true) 为true表示,不在此方法上增加租户信息 / paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() { @Override public boolean doFilter(MetaObject metaObject) { MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject); if ("cn.mesmile.mybatisplusdemo.mapper.EmployeeMapper.selectAll" .equals(ms.getId())) { // 同时这个方法对 动态表明解析也会产生作用,也会过滤掉动态表名,只显示原来的表名 // 不增加租户信息 return true; } return false; } });
例:
@Componentpublic interface EmployeeMapper extends BaseMapper{ /** * 这是自定义的查询方法 * @param wrapper * @return */ //该注解为true 设置该方法,不需要要加入租户信息 //@SqlParser(filter = true) @Select("select * from employee ${ew.customSqlSegment}") List selectAll(@Param(Constants.WRAPPER)Wrapper wrapper);}
测试:
MybatisPlusConfig.myTableName.set("employee_2020");LambdaQueryWrapperemployeeLambdaQueryWrapper = Wrappers.lambdaQuery();// 当自定义查询方法[有] @SqlParser(filter = true) 时候:select * from employee 动态表名不生效// 当自定义查询方法[没有] @SqlParser(filter = true) 时候:select * from employee_2020 动态表名生效List employees = employeeMapper.selectAll(employeeLambdaQueryWrapper);
public class DeleteAllMethod extends AbstractMethod { @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { // 执行的sql String sql = "delete from "+ tableInfo.getTableName(); // mapper 接口方法名 String method = "deleteAll"; SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); return addDeleteMappedStatement(mapperClass, method , sqlSource); }}
@Componentpublic class MySqlInjector extends DefaultSqlInjector { @Override public ListgetMethodList(Class mapperClass) { // 获取到父类已有的抽象方法 List methodList = super.getMethodList(mapperClass); // 将自定义的类方法 加入到父类的集合中 methodList.add(new DeleteAllMethod()); return methodList; }}
在mapper类中写入方法:
@Componentpublic interface EmployeeMapper extends BaseMapper{ /** * 自定义的sql注入方法,删除所有 * @return 返回受影响行数 */ int deleteAll();}
选装件:InsertBatchSomeColumn 、 LogicDeleteByIdWithFill 、AlwaysUpdateSomeColumnById
加入选装件:
@Componentpublic class MySqlInjector extends DefaultSqlInjector { @Override public ListgetMethodList(Class mapperClass) { List methodList = super.getMethodList(mapperClass); // 将自定义的类方法 加入到父类的集合中 methodList.add(new DeleteAllMethod()); // 【选装件】 插入的时候,不插入【逻辑删除】 和 【age年龄】 methodList.add(new InsertBatchSomeColumn(t -> !t.isLogicDelete() && !t.getColumn().equals("age"))); // 【选装件】 逻辑删除的时候同时再修改值,但是修改值得字段上要加上注解 @TableField(fill = FieldFill.UPDATE) methodList.add(new LogicDeleteByIdWithFill()); // 【选装件】 根据id更新固定的几个字段(但是不包括逻辑删除) 这里 不更新 name 字段 methodList.add(new AlwaysUpdateSomeColumnById(t -> !t.getColumn().equals("name"))); return methodList; }}
在mapper接口中写选装件方法:
public interface MyMapperextends BaseMapper { /** * 自定义的sql注入方法,删除所有 * @return 返回受影响行数 */ int deleteAll(); /** * mybatis-plus 中的【选装件】 , 自定义批量插入 * @param list * @return */ int insertBatchSomeColumn(List list); /** * mybatis-plus 中的额【选装件】 , 自定义删除 * * 在删除的同时,填充值修改值,需要在 修改的字段上 加注解 * @TableField(fill = FieldFill.UPDATE) * private Integer age; * @param entity * @return */ int deleteByIdWithFill(T entity); /** * mybatis-plus 中的额【选装件】 * 指定更新某些字段 * @param entity * @return */ int alwaysUpdateSomeColumnById(@Param(Constants.ENTITY) T entity);}
转载地址:http://bhxxi.baihongyu.com/