博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis-Plus进阶之扩展插件
阅读量:4165 次
发布时间:2019-05-26

本文共 12955 字,大约阅读时间需要 43 分钟。

文章目录

Mybatis-Plus进阶之扩展插件

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

1、逻辑删除

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 = 1List
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;

配置字段自动填充:

@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);

3、乐观锁

配置乐观锁插件:

@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 不能复用!!!

4、执行sql分析打印

引入依赖:

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新增)    该插件有性能损耗,不建议生产环境使用

5、多租户实现

需要在分页配置类中配置:

@Slf4j@Configurationpublic class MybatisPlusConfig {
/** * 分页配置中,设置多租户信息 */ @Bean public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); ArrayList
sqlParsers = 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);}

6、动态表名解析

配置动态表名解析器:

@Slf4j@Configurationpublic class MybatisPlusConfig {
public static ThreadLocal
myTableName = 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");LambdaQueryWrapper
employeeLambdaQueryWrapper = Wrappers.lambdaQuery();// 当自定义查询方法[有] @SqlParser(filter = true) 时候:select * from employee 动态表名不生效// 当自定义查询方法[没有] @SqlParser(filter = true) 时候:select * from employee_2020 动态表名生效List
employees = employeeMapper.selectAll(employeeLambdaQueryWrapper);

7、sql注入器 和 选装件

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 List
getMethodList(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 List
getMethodList(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 MyMapper
extends 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/

你可能感兴趣的文章
C++设计技巧(一)之两个类互相拥有对方的对象指针
查看>>
TCP/UDP的接收缓冲区和发送缓冲区
查看>>
C++类里面定义一个指向自己的静态成员指针变量(实现单例)
查看>>
LTE基本认识
查看>>
MQTT的实际应用
查看>>
TinyXml2解析xml用法例子
查看>>
Linux 虚拟内存和物理内存
查看>>
产品和技术,你选对了吗?
查看>>
使用libcurl遇到的坑
查看>>
UDP客户端发送结构体数据,调用recvfrom函数阻塞问题
查看>>
压抑的西安互联网环境,你有纠结辞职吗?
查看>>
使用-section等参数实现对库的裁剪,从而减小程序体积
查看>>
使用nmcli network-manager管理wifi网络
查看>>
JWT之token生成和解析鉴权验证[C++]
查看>>
C++如何获取服务器内网IP地址[Windows+Linux]
查看>>
Linux中获取CPU数目确定多线程数
查看>>
C/C++ windows 获取CPU数
查看>>
最好用的Hash
查看>>
srs提供的rtmp客户端srs-librtmp
查看>>
使用c-ares进行DNS域名解析
查看>>