MyBatis Plus 常用插件

MyBatis Plus 常用插件

一、逻辑删除

在 Spring Boot 中的 application.yml 文件中配置。如果我们保持默认,则此配置就不用写了。

mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

然后在实体类上,添加 @TableLogic 注解。

@TableLogic
private Integer deleted;

【注意】使用 mp 自带方法删除和查找都会附带逻辑删除功能 ,但自己写的 xml 不会。

java 逻辑删除简单理解就是“删除”,它是为了方便数据恢复和保护数据本身价值等等的一种方案。如果这个数据你需要再去查出来,就不应该使用逻辑删除,而是以一个状态去表示。比如,员工离职、帐号锁定等都应该是一个状态字段,不适合使用逻辑删除。如果是要查找删除数据,则需要单独手写 SQL 来实现,比如需要查看过往所有数据的统计汇总信息。

二、通用枚举

主要是为了让 MyBatis 更优雅的使用枚举属性,抛弃那些超繁琐的配置。

从 3.1.1 版本开始,直接使用默认枚举类会更好。

推荐的配置方式:

1)使用 IEnum 接口,推荐配置 defaultEnumTypeHandler

2)使用注解枚举处理,推荐配置 typeEnumsPackage

3)注解枚举处理与 IEnum 接口,推荐配置 typeEnumsPackage

4)与原生枚举混用,需配置 defaultEnumTypeHandler 与 typeEnumsPackage

1. 使用通用枚举属性的两种方式

方式一:使用 @EnumValue 注解枚举属性
public enum GradeEnum {

    PRIMARY(1, "小学"),  SECONDORY(2, "中学"),  HIGH(3, "高中");

    GradeEnum(int code, String descp) {
        this.code = code;
        this.descp = descp;
    }

    @EnumValue//标记数据库存的值是code
    private final int code;
    //。。。
}
方式二:枚举属性,实现 IEnum 接口
public enum AgeEnum implements IEnum<Integer> {
    ONE(1, "一岁"),
    TWO(2, "二岁"),
    THREE(3, "三岁");
    
    private int value;
    private String desc;
    
    @Override
    public Integer getValue() {
        return this.value;
    }
}

实体属性使用枚举类型

public class User{
    /**
     * 名字
     * 数据库字段: name varchar(20)
     */
    private String name;
    
    /**
     * 年龄,IEnum接口的枚举处理
     * 数据库字段:age INT(3)
     */
    private AgeEnum age;
        
        
    /**
     * 年级,原生枚举(带{@link com.baomidou.mybatisplus.annotation.EnumValue}):
     * 数据库字段:grade INT(2)
     */
    private GradeEnum grade;
}

2. 配置扫描通用枚举

编辑 Spring Boot 项目 applicaton.yml 文件

mybatis-plus:
    # 支持统配符 * 或者 ; 分割
    typeEnumsPackage: com.test.entity.enums

3. JSON 序列化处理

1)Jackson

在需要响应描述字段的 get 方法上添加 @JsonValue 注解即可

2)Fastjson

全局处理方式

FastJsonConfig config = new FastJsonConfig();

// 设置WriteEnumUsingToString	config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString);

converter.setFastJsonConfig(config);

局部处理方式

@JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString)
private UserStatus status;

二选一,然后在枚举中重写 toString() 方法。

三、 自动填充功能

在使用的时候,需要实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler

1. 配置 @TableField 注解

注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置。

public class User {

    // 注意!这里需要标记为填充字段
    @TableField(.. fill = FieldFill.INSERT)
    private String fillField;

    ....
}

自定义实现类,需要实现 MetaObjectHandler 接口,填充处理器在 Spring Boot 中使用的时候,需要再加上 @Component 注解。

必须使用父类的 setFieldValByName() 或者 setInsertFieldValByName / setUpdateFieldValByName 方法,否则不会根据注解中的 FiledFill.xxx 来区分。

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyMetaObjectHandler.class);

    @Override
    public void insertFill(MetaObject metaObject) {
        LOGGER.info("start insert fill ....");
        this.setFieldValByName("operator", "Jerry", metaObject);//版本号3.0.6以及之前的版本
        //this.setInsertFieldValByName("operator", "Jerry", metaObject);//@since 快照:3.0.7.2-SNAPSHOT, @since 正式版暂未发布3.0.7
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        LOGGER.info("start update fill ....");
        this.setFieldValByName("operator", "Tom", metaObject);
        //this.setUpdateFieldValByName("operator", "Tom", metaObject);//@since 快照:3.0.7.2-SNAPSHOT, @since 正式版暂未发布3.0.7
    }
}
public enum FieldFill {
    /**
     * 默认不处理
     */
    DEFAULT,
    /**
     * 插入填充字段
     */
    INSERT,
    /**
     * 更新填充字段
     */
    UPDATE,
    /**
     * 插入和更新填充字段
     */
    INSERT_UPDATE
}

四、SQL 注入器

注入器配置:全局配置 sqlInjector 用于注入 ISqlInjector 接口的子类,实现自定义方法注入。

自定义的通用方法可以实现接口 ISqlInjector ,也可以继承抽象类 AbstractSqlInjector 注入通用方法 SQL 语句, 然后继承 BaseMapper 添加自定义方法,全局配置 sqlInjector 注入 MP 会自动将类所有方法注入到 mybatis 容器中。

public interface ISqlInjector {

    /**
     * 检查SQL是否注入(已经注入过不再注入)
     *
     * @param builderAssistant mapper 信息
     * @param mapperClass      mapper 接口的 class 对象
     */
    void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass);
}

五、攻击 SQL 阻断解析器

主要作用:阻止恶意的全表更新删除。

@Bean
public PaginationInterceptor paginationInterceptor() {
    PaginationInterceptor paginationInterceptor = new PaginationInterceptor();

    ...

    List<ISqlParser> sqlParserList = new ArrayList<>();
    // 攻击 SQL 阻断解析器、加入解析链
    sqlParserList.add(new BlockAttackSqlParser());
    paginationInterceptor.setSqlParserList(sqlParserList);

    ...

    return paginationInterceptor;
}

六、性能分析插件

性能分析拦截器,用于输出每条 SQL 语句及其执行时间。如果对 SQL 的打印效果要求较高,可以使用下面的【执行SQL分析打印】插件。

先配置 <plugins>标签

<plugins>
    ....

    <!-- SQL 执行性能分析,开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长 -->
    <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
        <property name="maxTime" value="100" />
        <!--SQL是否格式化 默认false-->
        <property name="format" value="true" />
    </plugin>
</plugins>

然后,在 Spring Boot 中使用

//Spring boot方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {

    /**
     * SQL执行效率插件
     */
    @Bean
    @Profile({"dev","test"})// 设置 dev test 环境开启
    public PerformanceInterceptor performanceInterceptor() {
        return new PerformanceInterceptor();
    }
}

参数说明

  • 参数:maxTime SQL 执行最大时长,超过自动停止运行,有助于发现问题。

  • 参数:format SQL SQL是否格式化,默认false。

**【**注意】该插件只用于开发环境,不建议生产环境使用。

七、执行 SQL 分析打印

在 3.1.0+ 版本,该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长。

1)在 Maven 中引入 p6spy 依赖

<dependency>
  <groupId>p6spy</groupId>
  <artifactId>p6spy</artifactId>
  <version>最新版本</version>
</dependency>

2)配置 application.yml 文件

spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:h2:mem:test
    ...

3)配置 spy.properties 文件

module.log=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,batch,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2

【注意】
driver-class-name 为 p6spy 提供的驱动类。
url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址。
此插件有性能损耗,不建议生产环境使用。

八、乐观锁插件

1. 主要适用场景

意图:当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式

  • 取出记录时,获取当前 version
  • 更新时,带上这个 version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

2. 使用方式

插件配置

在 Spring Boot 中的配置

@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
}

2)一定要在实体字段上使用 @Version 注解

@Version
private Integer version;
特别说明
  • 支持的数据类型:int, Integer, long, Long, Date, Timestamp, LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity
  • 仅支持 updateById(id)update(entity, wrapper) 方法
  • 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
示例

编写 Java 代码,具体如下:

int id = 100;
int version = 2;

User u = new User();
u.setId(id);
u.setVersion(version);
u.setXXX(xxx);

if(userService.updateById(u)){
    System.out.println("Update successfully");
}else{
    System.out.println("Update failed due to modified by others");
}

上面所对应的 SQL 原理非常简单:

update tbl_user set name = 'update',version = 3 where id = 100 and version = 2

九、动态数据源

img

1. 快速了解

1. 简介

dynamic-datasource-spring-boot-starter:一个基于 Springboot 的快速继承多数据源的启动器。

github 地址:https://github.com/baomidou/dynamic-datasource-spring-boot-starter

2. 优势

关于动态数据源的切换,核心只有两种。

1)构建多套环境,优势是方便控制也容易集成一些简单的分布式事务,缺点是非动态同时代码量较多,配置难度大。

2)基于spring提供原生的 AbstractRoutingDataSource ,参考一些文档自己实现切换。

如果你的数据源较少,场景不复杂,选择以上任意一种都可以。如果你需要更多特性,请尝试本动态数据源。

1)数据源分组,适用于多种场景、纯粹多库、读写分离、一主多从、混合模式。

2)简单集成 Druid 数据源监控多数据源,简单集成 Mybatis-Plus 简化单表,简单集成 P6sy 格式化 sql,简单集成 Jndi 数据源。

3)简化 Druid 和 HikariCp 配置,提供全局参数配置。

4)提供自定义数据源来源(默认使用 yml 或 properties 配置)。

5)项目启动后能动态增减数据源。

6)使用 spel 动态参数解析数据源,如从 session,header 和参数中获取数据源。(多租户架构神器)

7)多层数据源嵌套切换。(一个业务ServiceA调用ServiceB,ServiceB调用ServiceC,每个Service都是不同的数据源)

8)使用正则匹配或spel表达式来切换数据源(实验性功能)。
3. 劣势

不能使用多数据源事务(同一个数据源下能使用事务),网上其他方案也都不能提供。

如果你需要使用到分布式事务,那么你的架构应该到了微服务化的时候了。

4. 约定

1)本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何 CRUD。

2)配置文件所有以下划线 _ 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。

3)切换数据源即可是组名,也可是具体数据源名称,切换时默认采用负载均衡机制切换。

4)默认的数据源名称为 master ,你可以通过spring.datasource.dynamic.primary修改。

5)方法上的注解优先于类上注解。

5. 建议

强烈建议在 主从模式 下遵循普遍的规则,以便他人能更轻易理解你的代码。

主数据库 建议 只执行 INSERT UPDATE DELETE 操作。

从数据库 建议 只执行 SELECT 操作。

2. 具体使用

1)引入依赖

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>${version}</version>
</dependency>

2)配置数据源

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      datasource:
        master:
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
        slave_1:
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
        slave_2:
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://xx.xx.xx.xx:3308/dynamic
       #......省略
       #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
# 多主多从   纯粹多库(记得设置primary)  混合配置
spring:       spring:                spring:
  datasource:   datasource:            datasource:
    dynamic:      dynamic:               dynamic:
      datasource:   datasource:            datasource:
        master_1:     mysql:                  master:
        master_2:     oracle:                 slave_1:
        slave_1:      sqlserver:              slave_2:
        slave_2:      postgresql:             oracle_1:
        slave_3:      h2:                     oracle_2:

3)使用 @DS 切换数据源。

@DS 可以注解在方法上和类上,同时存在方法注解优先于类上注解

【注意】注解在 service 实现或 mapper 接口方法上,但强烈不建议同时在 service和 mapper 注解。 (可能会有问题)

注解结果
没有@DS默认数据源
@DS(“dsName”)dsName可以为组名也可以为具体某个库的名称
@Service
@DS("slave")
public class UserServiceImpl implements UserService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  public List<Map<String, Object>> selectAll() {
    return  jdbcTemplate.queryForList("select * from user");
  }
  
  @Override
  @DS("slave_1")
  public List<Map<String, Object>> selectByCondition() {
    return  jdbcTemplate.queryForList("select * from user where age >10");
  }
}

在 mybatis 环境下也可注解在 mapper 接口层。

@DS("slave")
public interface UserMapper {

  @Insert("INSERT INTO user (name,age) values (#{name},#{age})")
  boolean addUser(@Param("name") String name, @Param("age") Integer age);

  @Update("UPDATE user set name=#{name}, age=#{age} where id =#{id}")
  boolean updateUser(@Param("id") Integer id, @Param("name") String name, @Param("age") Integer age);

  @Delete("DELETE from user where id =#{id}")
  boolean deleteUser(@Param("id") Integer id);

  @Select("SELECT * FROM user")
  @DS("slave_1")
  List<User> selectAll();
}

另可参考更多更细致的内容:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/wikis/pages

十、多租户 SQL 解析器

这里配合 分页拦截器 使用, spring boot 例子配置如下。

@Bean
public PaginationInterceptor paginationInterceptor() {
    PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    paginationInterceptor.setLocalPage(true);// 开启 PageHelper 的支持
    /*
     * 【测试多租户】 SQL 解析处理拦截器<br>
     * 这里固定写成住户 1 实际情况你可以从cookie读取,因此数据看不到 【 麻花藤 】 这条记录( 注意观察 SQL )<br>
     */
    List<ISqlParser> sqlParserList = new ArrayList<>();
    TenantSqlParser tenantSqlParser = new TenantSqlParser();
    tenantSqlParser.setTenantHandler(new TenantHandler() {
        @Override
        public Expression getTenantId() {
            return new LongValue(1L);
        }

        @Override
        public String getTenantIdColumn() {
            return "tenant_id";
        }

        @Override
        public boolean doTableFilter(String tableName) {
            // 这里可以判断是否过滤表
            /*
            if ("user".equals(tableName)) {
                return true;
            }*/
            return false;
        }
    });
    sqlParserList.add(tenantSqlParser);
    paginationInterceptor.setSqlParserList(sqlParserList);
    paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
        @Override
        public boolean doFilter(MetaObject metaObject) {
            MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);
            // 过滤自定义查询此时无租户信息约束【 麻花藤 】出现
            if ("com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId())) {
                return true;
            }
            return false;
        }
    });
    return paginationInterceptor;
}

十一、动态表明 SQL 解析

实现 ITableNameHandler 接口注入到 DynamicTableNameParser 处理器链中,将动态表名解析器注入到 MP 解析链。

【注意】原理为解析替换设定表名为处理器的返回表名,表名建议可以定义复杂一些避免误替换。例如:真实表名为 user 设定为 mp_dt_user 处理器替换为 user_2019 等。

十二、MyBatisX 快速开发插件

MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。

安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。

项目码云地址:https://gitee.com/baomidou/MybatisX

功能:
1)Java 与 XML 调回跳转
2)Mapper 方法自动生成 MXL

返回篇头

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿钟小哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值