MyBatis-plus拓展之字段类型处理器、自动填充和乐观锁等(完结)

张开发
2026/4/5 15:55:37 15 分钟阅读

分享文章

MyBatis-plus拓展之字段类型处理器、自动填充和乐观锁等(完结)
MyBatis-plus拓展逻辑删除逻辑删除就是增加一个字段表示这个数据的状态通过状态来显示数据或隐藏数据而不是真正的删除。MyBatis-plus使用TableLogic注解来标注逻辑删除字段public class User extends ModelUser { TableId private Long id; private String name; private Integer age; private String email; // 配置当前字段为逻辑删除字段 // 默认值是1删除状态的值是0 TableLogic(value 1,delval 0) private Integer status; }此时如果调用Mapper的删除方法实际对应的sql语句是更新操作。将逻辑删除字段的值更新为0而不是真正的删除。而且此时Mapper的查询方法不会查出这条数据。生成的sql语句会自动拼接where条件status 1逻辑删除也可以在配置文件中进行全局配置mybatis-plus: global-config: banner: false db-config: id-type: assign_id # 逻辑删除字段为status logic-delete-field: status # 删除状态的值 logic-delete-value: 0 # 未删除状态的值 logic-not-delete-value: 1使用全局配置后就不用使用TableLogic注解了。通用枚举假如要表示性别只有男和女两个值我们就可以使用枚举来描述。数据库表中使用gender int 类型表示性别0表示女性1表示男性。使用EnumValue来标注将哪个变量的值插入到数据库。先创建枚举类public enum GenderEnum { MAN(1,男),WOMAN(0,女); EnumValue // 表示将这个变量的值插入到数据库 private Integer gender; private String genderName; GenderEnum(Integer gender, String genderName) { this.gender gender; this.genderName genderName; } }给Pojo类添加枚举属性public class User extends ModelUser { TableId private Long id; private String name; private Integer age; private String email; // 配置当前字段为逻辑删除字段 // 默认值是1删除状态的值是0 TableLogic(value 1,delval 0) private Integer status; private GenderEnum gender; }调用正常的插入方法即可实现。字段类型处理器某些场景下实体类中使用map集合作为属性接收前端传来的数据但是把这些输出存到数据库时使用json格式的字符串存储。那怎么把map类型转换成字符串类型呢这里就需要使用字段类型处理器。需要TableName注解和TableField注解配合使用。实体类代码如下// autoResultMap true 表示查询时自动将contact字段json格式的字符串转换为Map类型 TableName(autoResultMap true) public class User extends ModelUser { TableId private Long id; private String name; private Integer age; private String email; // 配置当前字段为逻辑删除字段 // 默认值是1删除状态的值是0 TableLogic(value 1,delval 0) private Integer status; private GenderEnum gender; // 指定类型处理器在新增或更新时自动将Map类型转换成json格式的字符串 // 这个处理器依赖于Fastjson所以要在pom文件中引入Fastjson依赖 TableField(typeHandler FastjsonTypeHandler.class) private MapString, String contact; // 联系方式 }自动填充在实际应用中有一些属性其实不需要我们每次都手动填充可以设置为自动填充比如创建时间、更新时间等可以设置为自动填充。注意时区的设置mysql数据库设置时区set global time_zone 8:00查看时区对不对执行select now() 看时间能不能对上。项目中的时区设置在数据库连接Url中设置。url: jdbc:mysql://localhost:3306/spring6?useSSLfalseserverTimezoneUTCcharacterEncodingutf8useUnicodetrue把serverTimezoneUTC改为serverTimezoneAsia/Shanghai。使用TableField注解设置填充时机// autoResultMap true 表示查询时自动将contact字段json格式的字符串转换为Map类型 TableName(autoResultMap true) public class User extends ModelUser { TableId private Long id; private String name; private Integer age; private String email; // 配置当前字段为逻辑删除字段 // 默认值是1删除状态的值是0 TableLogic(value 1,delval 0) private Integer status; private GenderEnum gender; // 指定类型处理器在新增或更新时自动将Map类型转换成json格式的字符串 // 这个处理器依赖于Fastjson所以要在pom文件中引入Fastjson依赖 TableField(typeHandler FastjsonTypeHandler.class) private MapString, String contact; // 联系方式 // 指定插入时填充 TableField(fill FieldFill.INSERT) private Data creatTime; // 指定插入或更新时填充 TableField(fill FieldFill.INSERT_UPDATE) private Data modifyTime; }编写自定义处理器设置填充策略//自定义Handler设置填充策略,这里需要实现MetaObjectHandler接口 Component public class MyMetaHandler implements MetaObjectHandler { // 插入时的填充策略 Override public void insertFill(MetaObject metaObject) { setFieldValByName(creatTime,new Date(),metaObject); setFieldValByName(modifyTime,new Date(),metaObject); } // 更新时的填充策略 Override public void updateFill(MetaObject metaObject) { setFieldValByName(modifyTime,new Date(),metaObject); } }此时在新增或更新时无需手动设置创建时间更新时间系统会自动填充时间到数据库表中。防止全表更新与删除插件配置拦截规则插件默认拦截没有指定条件的update和delete语句。当触发拦截时会抛出MyBatisPlusException异常。通过在MybatisPlusConfig 这个配置类中加入对应的拦截器来阻止全表更新与删除Configuration MapperScan(com.ali.mapper) public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 防止全表更新与删除插件 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加 // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType return interceptor; } }MyBatisX快速开发插件MyBatisX是一款idea提供的插件目的是为了简化MyBatis和MyBatis-plus框架。MyBatisX先安装插件File -- Settings --Plugins -- 搜索 MyBatisX然后安装MyBatisX插件的快速定位这个插件把Mapper接口的方法和映射文件的sql进行了一一对应。可以从Mapper接口方法快速跳到对应的映射文件对应的sql。同样也可以在映射文件点击小鸟快速跳转到对应的Mapper接口MyBatisX插件的逆向工程通过数据库表快速生成以下项目文件实体类Mapper接口Mapper映射文件Service接口Service实现类首先使用idea连接数据库2. 选择对应的数据库表然后右键选择MyBatisX-generate最后一步设置这样就生成了对应的项目文件。注意这里的Mapper接口文件要手动加上Mapper注解。乐观锁并发请求就是在同一时刻有多个请求去请求同一个服务器资源。如果是获取信息不会出现问题但是如果做修改操作就会出现并发问题。比如三个人去买同样的商品商品剩余一件。购买时一般先查询库存再购买后数量减一并发请求就是同一时刻三个人都查到了商品剩余1个然后同时进行购买。这样只能一个人买到另外两人肯定买不到此时就发生了超卖行为。这就是经典的并发问题。常见的数据库锁类型有两种悲观锁和乐观锁悲观锁查询时就锁定数据在请求完成之前不会释放锁。完成后才释放锁。释放锁以后其他请求才可以对数据进行读写。这样虽解决了并发问题但是效率较低。实际开发中很少使用悲观锁。乐观锁通过表字段完成设计。乐观锁的核心思想是在读取的时候不加锁其他请求仍可以读取这个数据在修改的时候判断一个数据是否有被修改过如果修改过那本次请求的修改操作失效。具体设计如下增加一个字段version。购买商品时先查询此时能获取到version的值。在执行购买操作时更新库存数量前再查询一次version的值如果两次的version值一样表示可以进行更新库存操作更新时进行 version version 1表示我执行了一次。这样就完成了乐观锁的操作。如果两次的version 值不一致说明有人对库存数据进行了更新此时不能直接进行购买。需要重新进行先查询后购买的操作。MyBatis-plus中乐观锁实现步骤如下在实体类中使用Version注解指定版本字段// autoResultMap true 表示查询时自动将contact字段json格式的字符串转换为Map类型 TableName(autoResultMap true) public class User extends ModelUser { TableId private Long id; private String name; private Integer age; private String email; // 配置当前字段为逻辑删除字段 // 默认值是1删除状态的值是0 TableLogic(value 1,delval 0) private Integer status; private GenderEnum gender; // 指定类型处理器在新增或更新时自动将Map类型转换成json格式的字符串 // 这个处理器依赖于Fastjson所以要在pom文件中引入Fastjson依赖 TableField(typeHandler FastjsonTypeHandler.class) private MapString, String contact; // 联系方式 // 指定插入时填充 TableField(fill FieldFill.INSERT) private Data creatTime; // 指定插入或更新时填充 TableField(fill FieldFill.INSERT_UPDATE) private Data modifyTime; Version // 表示将此字段作为版本信息 private Integer version;// 版本 }在MybatisPlusConfig配置类中加入乐观锁拦截器Configuration MapperScan(com.ali.mapper) public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 乐观锁拦截器 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 防止全表更新与删除插件 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加 // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType return interceptor; } }测试代码// 第一次查询执行成功 version是1 User user1 userService.getById(1L); // 第二次查询执行成功 version是1 User user5 userService.getById(1L); user1.setAge(12); // 第一次更新:执行成功因为再次获取到version还是1和上次保持一致。 // 更新后 version 1 此时version是2 userService.updateById(user1); user5.setAge(22); // 第二次更新执行失败因为再次获取到version的值是2和上次获取的值不一样不执行更新操作。 userService.updateById(user5);代码生成器代码生成器和逆向功能的区别在于代码生成器可以生成更多的结构更多的内容允许配置更多的内容。具体步骤如下引入相关依赖!--代码生成器-- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-generator/artifactId version3.5.3/version /dependency !--freemarker 模板依赖-- dependency groupIdorg.freemarker/groupId artifactIdfreemarker/artifactId version2.3.31/version /dependency编写生成类可以在mybatis-plus官网直接copypublic class CodeGeneratorTest { public static void main(String[] args) { // 使用 FastAutoGenerator 快速配置代码生成器 FastAutoGenerator.create(jdbc:mysql://localhost:3306/mybatis_plus?serverTimezoneGMT%2B8, root, password) .globalConfig(builder - { builder.author(Your Name) // 设置作者 .outputDir(src/main/java); // 输出目录 }) .packageConfig(builder - { builder.parent(com.example) // 设置父包名 .entity(model) // 设置实体类包名 .mapper(dao) // 设置 Mapper 接口包名 .service(service) // 设置 Service 接口包名 .serviceImpl(service.impl) // 设置 Service 实现类包名 .xml(mappers); // 设置 Mapper XML 文件包名 }) .strategyConfig(builder - { builder.addInclude(table1, table2) // 设置需要生成的表名 .entityBuilder() .enableLombok() // 启用 Lombok .enableTableFieldAnnotation() // 启用字段注解 .controllerBuilder() .enableRestStyle(); // 启用 REST 风格 }) .templateEngine(new FreemarkerTemplateEngine()) // 使用 Freemarker 模板引擎 .execute(); // 执行生成 } }执行sql打印分析通过sql分析来获取sql语句的执行时间。具体步骤如下由于该功能依赖于p6spy组件p6spy是一个强大的工具它为MyBatis-Plus用户提供了便捷的SQL分析与打印功能。通过合理配置你可以在开发和测试阶段有效地监控和优化SQL语句。然而由于性能损耗建议在生产环境中谨慎使用。所以先引入依赖dependency groupIdp6spy/groupId artifactIdp6spy/artifactId version3.9.1/version /dependency在application.yml中修改配置spring: datasource: username: root password: root url: jdbc:p6spy:mysql://localhost:3306/spring6?useSSLfalseserverTimezoneUTCcharacterEncodingutf8useUnicodetrue driver-class-name: com.p6spy.engine.spy.P6SpyDriver在resource下创建spy.properties配置文件# 模块列表根据版本选择合适的配置 modulelistcom.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定义日志格式 logMessageFormatcom.baomidou.mybatisplus.extension.p6spy.P6SpyLogger # 日志输出到控制台 appendercom.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 取消JDBC驱动注册 deregisterdriverstrue # 使用前缀 useprefixtrue # 排除的日志类别 excludecategoriesinfo,debug,result,commit,resultset # 日期格式 dateformatyyyy-MM-dd HH:mm:ss # 实际驱动列表 # driverlistorg.h2.Driver # 开启慢SQL记录 outagedetectiontrue # 慢SQL记录标准单位秒 outagedetectioninterval2 # 过滤 flw_ 开头的表 SQL 打印 filtertrue excludeflw_*多数据源先引入依赖dependency groupIdcom.baomidou/groupId artifactIddynamic-datasource-spring-boot-starter/artifactId version3.1.0/version /dependencydynamic-datasource是一个开源的 Spring Boot 多数据源启动器提供了丰富的功能包括数据源分组、敏感信息加密、独立初始化表结构等配置数据源spring: datasource: dynamic: # 默认数据源名为 master可通过 spring.datasource.dynamic.primary 修改。 primary: master strict: false datasource: master: url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver slave_1: url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver slave_2: url: ENC(xxxxx) username: ENC(xxxxx) password: ENC(xxxxx) driver-class-name: com.mysql.jdbc.Driver使用DS切换数据源Service DS(master) public class UserServiceImpl implements UserService { Autowired private JdbcTemplate jdbcTemplate; Override DS(slave_1) public List selectByCondition() { return jdbcTemplate.queryForList(select * from user where age 10); } }

更多文章