Mybatis-Plus进阶之扩展插件
文章目录
- Mybatis-Plus进阶之扩展插件
- 1、逻辑删除
- 2、自动填充
- 3、乐观锁
- 4、执行sql分析打印
- 5、多租户实现
- 6、动态表名解析
- 7、sql注入器 和 选装件
Mybatis-Plus进阶之扩展插件
MybatisPlus基础篇请看:Mbatis-Plus整合springboot详细学习笔记
基本实体类:
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Employee {// idprivate Long id;// 名称private String name;// 年龄@TableField(fill = FieldFill.UPDATE)private Integer age;// 邮箱private String email;// 上级idprivate Long managerId;/** 设置自动填充 */// 创建时间@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;// 更新时间@TableField(fill = FieldFill.UPDATE)private LocalDateTime updateTime;// 版本号@Versionprivate Integer version;// 逻辑删除的 局部设置@TableField(select = false) // 查询的时候,不显示这个参数@TableLogicprivate Integer deleted;// 子级@TableField(exist = false)private List<Employee> children = new ArrayList<>();}
1、逻辑删除
springboot中配置:
# 配置逻辑删除的值 在这里配置的是 删除的时候为1 未删除的时候为0
mybatis-plus:global-config:db-config:logic-not-delete-value: 0logic-delete-value: 1
需要在实体类的字段上加上逻辑删除的注解:
// 删除的时候为1 未删除的时候为0@TableLogicprivate Integer deleted;
测试逻辑删除:
// update employee set deleted = 1 where id = ? and deleted = 0
boolean b = employeeService.removeById(1237753494614700033L);// 查询输出后将不会显示刚刚被删除的那条 select * from employee where deleted = 1
List<Employee> list = employeeService.list(null);// 并且只能更新未删除的
int update = employeeMapper.update(null, lambdaUpdateWrapper);
逻辑删除注意事项:
【注意: 自定义的查询方法,系统 不会 自动加上 逻辑删除条件,需要自己手动加上】在mapper中自定义的方法,需要自己手动加上逻辑删除限制条件
2、自动填充
设置自动填充需要在字段上加上注解:
/** 设置自动填充 */// 创建时间@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;// 更新时间@TableField(fill = FieldFill.UPDATE)private LocalDateTime updateTime;
配置字段自动填充:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {/*** 插入的时候自动填充* @param metaObject*/@Overridepublic void insertFill(MetaObject metaObject) {// fieldName 是实体中的属性名称// 检查是否有这个属性boolean createTime = metaObject.hasSetter("createTime");if (createTime) {this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());}}/*** 修改的时候自动填充* @param metaObject*/@Overridepublic 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);
3、乐观锁
配置乐观锁插件:
@Slf4j
@Configuration
public class MybatisPlusConfig {/*** 乐观锁插件* @return*/@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor() {return new OptimisticLockerInterceptor();}
}
实体类中字段上加上乐观锁注解:
// 版本号@Versionprivate 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
不能复用!!!
4、执行sql分析打印
引入依赖:
<!-- 执行sql 分析打印 依赖--><dependency><groupId>p6spy</groupId><artifactId>p6spy</artifactId><version>3.8.2</version></dependency>
更改数据库配置:
spring:datasource:driver-class-name: com.p6spy.engine.spy.P6SpyDriverurl: jdbc:p6spy:mysql://127.0.0.1:3306/mybatisplus?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8username: rootpassword: 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新增)该插件有性能损耗,不建议生产环境使用
5、多租户实现
需要在分页配置类中配置:
@Slf4j
@Configuration
public class MybatisPlusConfig {/*** 分页配置中,设置多租户信息*/@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();ArrayList<ISqlParser> sqlParsers = new ArrayList<>();// 租户解析器TenantSqlParser tenantSqlParser = new TenantSqlParser();tenantSqlParser.setTenantHandler(new TenantHandler() {@Overridepublic Expression getTenantId(boolean where) {// 租户的id,这里测试固定写死return new LongValue(1088248166370832385L);}// 多租户字段是什么@Overridepublic String getTenantIdColumn() {// 这是表中的字段名,即 用于区分租户的字段return "manager_id";}// 是否需要排除某些表,不加租户字段@Overridepublic 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() {@Overridepublic 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;}
}
过滤得到不使用多租户用户的方法:
@Component
public interface EmployeeMapper extends MyMapper<Employee> {/*** 这是自定义的查询方法* @param wrapper*/
// @SqlParser(filter = true) 该注解为true 设置该方法,不需要要加入租户信息@Select("select * from employee ${ew.customSqlSegment}")List<Employee> selectAll(@Param(Constants.WRAPPER)Wrapper<Employee> wrapper);}
6、动态表名解析
配置动态表名解析器:
@Slf4j
@Configuration
public class MybatisPlusConfig {public static ThreadLocal<String> myTableName = new ThreadLocal<>();/*** mybatis-plus 的分页配置里面配置 动态表名解析器* @return*/@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();ArrayList<ISqlParser> sqlParsers = new ArrayList<>();// 动态表名解析器DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();HashMap<String, ITableNameHandler> tableNameHandlerHashMap = new HashMap<>();// 第一参数是需要 动态替换的表名tableNameHandlerHashMap.put("employee", new ITableNameHandler() {@Overridepublic 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() {@Overridepublic boolean doFilter(MetaObject metaObject) {MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);if ("cn.mesmile.mybatisplusdemo.mapper.EmployeeMapper.selectAll".equals(ms.getId())) {// 同时这个方法对 动态表明解析也会产生作用,也会过滤掉动态表名,只显示原来的表名// 不增加租户信息return true;}return false;}});
例:
@Component
public interface EmployeeMapper extends BaseMapper<Employee> {/*** 这是自定义的查询方法* @param wrapper* @return*///该注解为true 设置该方法,不需要要加入租户信息//@SqlParser(filter = true) @Select("select * from employee ${ew.customSqlSegment}")List<Employee> selectAll(@Param(Constants.WRAPPER)Wrapper<Employee> wrapper);}
测试:
MybatisPlusConfig.myTableName.set("employee_2020");LambdaQueryWrapper<Employee> employeeLambdaQueryWrapper = Wrappers.lambdaQuery();// 当自定义查询方法[有] @SqlParser(filter = true) 时候:select * from employee 动态表名不生效
// 当自定义查询方法[没有] @SqlParser(filter = true) 时候:select * from employee_2020 动态表名生效
List<Employee> employees = employeeMapper.selectAll(employeeLambdaQueryWrapper);
7、sql注入器 和 选装件
public class DeleteAllMethod extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {// 执行的sqlString sql = "delete from "+ tableInfo.getTableName();// mapper 接口方法名String method = "deleteAll";SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);return addDeleteMappedStatement(mapperClass, method , sqlSource);}
}
@Component
public class MySqlInjector extends DefaultSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {// 获取到父类已有的抽象方法List<AbstractMethod> methodList = super.getMethodList(mapperClass);// 将自定义的类方法 加入到父类的集合中methodList.add(new DeleteAllMethod());return methodList;}
}
在mapper类中写入方法:
@Component
public interface EmployeeMapper extends BaseMapper<Employee> {/*** 自定义的sql注入方法,删除所有* @return 返回受影响行数*/int deleteAll();
}
选装件:InsertBatchSomeColumn 、 LogicDeleteByIdWithFill 、AlwaysUpdateSomeColumnById
加入选装件:
@Component
public class MySqlInjector extends DefaultSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {List<AbstractMethod> 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 MyMapper<T> extends BaseMapper<T> {/*** 自定义的sql注入方法,删除所有* @return 返回受影响行数*/int deleteAll();/*** mybatis-plus 中的【选装件】 , 自定义批量插入* @param list* @return*/int insertBatchSomeColumn(List<T> 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);
}
Mybatis-Plus进阶之扩展插件相关推荐
- Mybatis分库分表扩展插件
参考:http://fangjialong.iteye.com/blog/2240880
- springboot进阶,分页插件 pageHelper,Swagger整合,日志
文章目录 1,课程回顾 2,本章重点 3,具体内容 3.1 整合连接池 3.2 springboot日志配置: 3.3 springboot整合shiro 3.4 mybatis分页插件 pageHe ...
- VS Code 安装插件、自定义模板、自定义配置参数、自定义主题、配置参数说明、常用的扩展插件
1. 下载和官网教程 下载地址:https://code.visualstudio.com/ 官方教程:https://code.visualstudio.com/docs 2. 安装插件 安装扩展插 ...
- jupyter扩展插件Nbextensions的安装、使用
jupyter扩展插件Nbextensions的安装.使用 # 使用pypi里面包进行安装 pip install jupyter_contrib_nbextensions # 也可以通过如下方式进行 ...
- javascript扩展插件alook_使用 Kotlin 编写你的第一个 Firefox WebExtension 扩展
Kotlin 是我最喜爱的编程语言.我们已经知道 Kotlin 编译成 Java 字节码可以快速被安卓和服务端采用.事实上,Kotlin 还支持编译成 JavaScript,因此该语言也开始在 Web ...
- vscode 结束_8 个给前端的顶级 VS Code 扩展插件
翻译:京程一灯 原文:https://1stwebdesigner.com/top-free-extensions-for-vs-code/ 微软的 VS (Visual Studio) Code 是 ...
- 【Visual Studio 2019】上传代码到 GitHub ( 16.9.2 版本 | 安装 GitHub 扩展插件 | 创建 Git 仓库 | 推送到远程仓库 )
文章目录 一.安装 GitHub 扩展插件 二.创建 Git 仓库 三.推送到远程仓库 今天将 Visual Studio 2019 从 16.3.6 版本升级到了 16.9.2 版本 , 发现相关操 ...
- java代码里的JSON格式怎么写好看_谁会不爱让代码骚里骚气的VSCode扩展插件呢?...
点击上方 "Python人工智能技术" 关注,星标或者置顶22点24分准时推送,第一时间送达 来自:公众号 读芯术 | 编辑:真经君 码农真经(ID:coder_experienc ...
- reactjs redux chrome扩展插件
reactjs redux chrome扩展插件 React Developer ToolsRedux DevTools
最新文章
- 【全网之最】用JavaScript写一个最简短的语句实现从A数组中去除B数组中相同元素
- 导出Windows服务器下的Oracle数据库并导入到Linux服务器下的Oracle数据库中
- 中小学计算机教室设备维修记录,多媒体电教室的管理与设备的维护
- 完全备份指的是对整个计算机系统,网络安全管理实践题库:在备份技术中,差分备份就是对整个系统所有文件进行完全备份,包括所有系统和数据。()...
- WebLogic8.1 配置SSL/HTTPS单向认证
- aspen共沸精馏如何模拟_9月1011号Aspen plus:精馏精品培训!线上线下同时开展!另有惊喜活动等你参与!...
- 设置图片圆角 或者圆形
- [vue] 第一次加载页面时会触发哪几个钩子?
- WEB服务器技术名词
- Linaro GCC 交叉编译工具链 国内源下载列表 (持续更新)
- 【版本更新】CAD组件Aspose.CAD 9月新版V17.9发布 | 支持IFC格式
- dicom格式怎么转换_qsv怎么转换mp4格式?qsv转mp4的首选工具
- matlab 读取同一文件中所有图像_matlab 批量读取文件夹内所有图片的几种方法
- 高等数学18讲(19版)反常积分的计算与敛散性判别
- SVN教程代码比较(图文教程)
- 模电学习1. 三极管基础知识及常用电路
- 自己写的txt分割器
- 高含盐废水处理资源化——双极膜电渗析
- 做自己喜欢的事情,是假的,改变世界也是假的
- Clion 调教记录
热门文章
- K8s中的CNI网络模型
- 数据分析---pandas(一)
- 002 Ubuntu系统设置之调整界面大小
- word2003下的神秘咒语——灵活的棕色狐狸跳过懒狗
- Movement Disorders脑电格兰杰因果分析:运动皮质在帕金森病复发性震颤中的作用
- 《UNIX网络编程》配置unp.h头文件
- LED照明各国认证及标准发展趋势
- 解决Hyperledger Fabric通道重复创建问题( readset expected key [Group] /Channel/Application at version 0, but )
- 关于对接芝麻 GO 的几点问题
- MVC中的URL路由(一)