微服务学习系列六:MyBatis-Plus用法

系列文章目录


目录

系列文章目录

前言

一、环境准备

一、为什么要使用utf8mb4字符集

 二、依赖准备

 三、配置准备

二、使用步骤

@TableName

@TableId 主键注解

IdType

@TableField

实体类UserInfoDO

Mapper层CRUD

Service层CRUD 

分页

多数据源 

 @DS注解

 动态表名插件DynamicTableNameInnerInterceptor

总结


前言

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

一、环境准备

本文基于Spring Boot、maven、jdk1.8、mySQL开发,所以开始前我们需要准备好这套环境。

mySQL 数据表 我采用utf8mb4这种字符集,做过微信的同学应该会知道,微信用户名称的表情,是需要这种字符集才能存储的。

一、为什么要使用utf8mb4字符集

低版本的MySQL支持的utf8编码,最大字符长度为 3 字节,如果遇到 4 字节的字符就会出现错误了。三个字节的 UTF-8 最大能编码的 Unicode 字符是 0xFFFF,也就是 Unicode 中的基本多文平面(BMP)。也就是说,任何不在基本多文平面的 Unicode字符,都无法使用MySQL原有的 utf8 字符集存储。这些不在BMP中的字符包括哪些呢?最常见的就是Emoji 表情(Emoji 是一种特殊的 Unicode 编码,常见于 ios 和 android 手机上)和一些不常用的汉字,以及任何新增的 Unicode 字符等等。
那么utf8mb4比utf8多了什么的呢?
✔ 多了emoji编码支持
如果实际用途上来看,可以给要用到emoji的库或者说表,设置utf8mb4,比如评论要支持emoji可以用到。

一 问题描述

插入sql时,Mybatis报错插入的值为错误的,发现数据中存在表情符号,其编码为四字节,默认的utf8编码为三字节。

二 修改MySQL编码

修改表编码

ALTER TABLE `table` DEFAULT CHARACTER SET utf8mb4;
修改字段编码

ALTER TABLE `tablename` CHANGE `字段名1` `字段名2` `类型` CHARACTER SET utf8mb4;
再次启动发现依旧报同样的错误。

三 修改依赖

版本要高,如果版本过低还会出现其问题。

            
                mysql
                mysql-connector-java
                8.0.28
            

 二、依赖准备

         
        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.2
            
                
                    org.springframework.boot
                    spring-boot-dependencies
                
            
        

         
        
            mysql
            mysql-connector-java
            8.0.23
        

         
        
            com.baomidou
            dynamic-datasource-spring-boot-starter
            3.5.0
        

 三、配置准备

Spring Boot启动类。配置@MapperScan注解,用于扫描Mapper文件位置:

/**
 * 用户微服务启动类
 *
 * @author yangyanping
 * @date 2022-02-28
 */
@SpringBootApplication
@EnableTransactionManagement
@ComponentScan("com.yyp.user")
@MapperScan("com.yyp.user.infra.gatewayImpl.database")
public class WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class);
    }
}

二、使用步骤

mybatis-plus为使用者封装了很多的注解,方便我们使用,我们首先看下实体类中有哪些注解。

@TableName

表名注解,用于标识实体类对应的表,使用位置:实体类。

属性 类型 必须指定 默认值 描述
value String "" 表名
schema String "" schema
keepGlobalPrefix boolean false 是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时)
resultMap String "" xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定)
autoResultMap boolean false 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入)
excludeProperty String[] {} 需要排除的属性名 @since 3.3.1

@TableId 主键注解

属性性 类型 必须指定 默认值 描述
value String "" 主键字段名
type Enum IdType.NONE 指定主键类型

IdType

描述
AUTO 数据库 ID 自增
NONE 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUT insert 前自行 set 主键值
ASSIGN_ID 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
ASSIGN_UUID 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法)
ID_WORKER 分布式全局唯一 ID 长整型类型(please use ASSIGN_ID)
UUID 32 位 UUID 字符串(please use ASSIGN_UUID)
ID_WORKER_STR 分布式全局唯一 ID 字符串类型(please use ASSIGN_ID)

@TableField

属性 类型 必须指定 默认值 描述
value String "" 数据库字段名
exist boolean true 是否为数据库表字段
condition String "" 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},参考(opens new window)
update String "" 字段 update set 部分注入,例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性)
insertStrategy Enum FieldStrategy.DEFAULT 举例:NOT_NULL
insert into table_a(column) values (#{columnProperty})
updateStrategy Enum FieldStrategy.DEFAULT 举例:IGNORED
update table_a set column=#{columnProperty}
whereStrategy Enum FieldStrategy.DEFAULT 举例:NOT_EMPTY
where column=#{columnProperty}
fill Enum FieldFill.DEFAULT 字段自动填充策略
select boolean true 是否进行 select 查询
keepGlobalFormat boolean false 是否保持使用全局的 format 进行处理
jdbcType JdbcType JdbcType.UNDEFINED JDBC 类型 (该默认值不代表会按照该值生效)
typeHandler Class UnknownTypeHandler.class 类型处理器 (该默认值不代表会按照该值生效)
numericScale String "" 指定小数点后保留的位数

实体类UserInfoDO

/**
 * 用户信息表
 *
 * @author yangyanping
 * @date 2022-11-17
 */
@Getter
@Setter
@TableName("user_info")
public class UserInfoDO {

    /**
     * 主键ID,递增
     */
    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 用户昵称
     */
    private String userName;

    /**
     * 用户状态(0启用,1停用,2 注销中)
     */
    private Integer status;

    /**
     * 用户手机号
     */
    private String mobile;

    /**
     * 用户密码
     */
    private String password;
}

Mapper层CRUD

代码如下(示例):

/**
 * 用户信息表
 *
 * @author yangyanping
 * @date 2023-03-18
 */
@DS("user")
public interface UserInfoMapper extends BaseMapper {
}

Service层CRUD 

/**
 * 用户服务接口
 *
 * @author yangyanping
 * @date 2023-03-18
 */
public interface UserInfoService extends IService {
}

Service 实现层 

/**
 * 用户服务实现类
 *
 * @author yangyanping
 * @date 2023-03-18
 */
@Service
public class UserInfoServiceImpl extends ServiceImpl implements UserInfoService {
}

分页

分页插件 PaginationInnerInterceptor,使用分页话需要增加分页插件的配置。

Page

该类继承了 IPage 类,实现了 简单分页模型 如果你要实现自己的分页模型可以继承 Page 类或者实现 IPage 类

/**
 * MyBatis Plus 配置类
 *
 * @author yangyanping
 * @date 2023-03-18
 */
public class MyBatisPlusConfig {
    /**
     * 插件集合
     */
    @Bean
    @ConditionalOnMissingBean(MybatisPlusInterceptor.class)
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //  插件
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MARIADB);
        // 添加分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor);

        return interceptor;
    }
}

如果返回类型是 IPage 则入参的 IPage 不能为null,因为 返回的IPage == 入参的IPage; 如果想临时不分页,可以在初始化IPage时size参数传 <0 的值;
如果返回类型是 List 则入参的 IPage 可以为 null(为 null 则不分页),但需要你手动 入参的IPage.setRecords(返回的 List);
如果 xml 需要从 page 里取值,需要 page.属性 获取 


多数据源 

多数据源既动态数据源,项目开发逐渐扩大,单个数据源、单一数据源已经无法满足需求项目的支撑需求。

使用方法

  • 引入dynamic-datasource-spring-boot-starter。

  com.baomidou
  dynamic-datasource-spring-boot-starter
  ${version}
  • 配置数据源
spring:
  datasource:
    dynamic:
      primary: book  #设置默认的数据源或者数据源组,默认值即为book
      strict: false  #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        book:
          url: jdbc:mysql://172.0.0.1:3306/book?zeroDateTimeBehavior=convertToNull&autoReconnect=true&generateSimpleParameterMetadata=true
          username: root
          password: root
        user:
          url: jdbc:mysql://172.0.0.41:3306/user?zeroDateTimeBehavior=convertToNull&autoReconnect=true&generateSimpleParameterMetadata=true
          username: root
          password: root
  • 使用 @DS 切换数据源

@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。 

注解 结果
没有@DS 默认数据源
@DS("dsName") dsName可以为组名也可以为具体某个库的名称
/**
 * 用户信息表
 *
 * @author yangyanping
 * @date 2023-03-18
 */
@DS("user")
public interface UserInfoMapper extends BaseMapper {
}

 动态表名插件DynamicTableNameInnerInterceptor

注意事项:

  • 原理为解析替换设定表名为处理器的返回表名,表名建议可以定义复杂一些避免误替换
  • 例如:真实表名为 user 设定为 mp_dt_user 处理器替换为 user_2019 等
  • 动态表名处理器 
/**
 * 动态表名处理器-----根据日期分表
 * mybatis-plus提供了动态表名处理器接口TableNameHandler,
 * 只需要在系统中实现该接口,并作为插件加载到mybatis-plus中就可以使用
 *
 * @author yangyanping
 * @date 2023-02-15
 */
public class DateTableNameHandler implements TableNameHandler {
    @Override
    public String dynamicTableName(String sql, String tableName) {
        // 模拟获取月份参数,实际应该从参数中获取
        String spiltName = DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
        String dynamicTableName = tableName + "_" + spiltName;

        return dynamicTableName;
    }
}
/**
 * 动态表名处理器---根据用户ID分表
 * mybatis-plus提供了动态表名处理器接口TableNameHandler,
 * 只需要在系统中实现该接口,并作为插件加载到mybatis-plus中就可以使用
 *
 * @author yangyanping
 * @date 2023-02-15
 */
@Slf4j
public class UserAscribeTableNameHandler implements TableNameHandler {
    //设置请求线程的用户ID数据
    private static final ThreadLocal USER_ID_CONTEXT = new ThreadLocal<>();

    @Override
    public String dynamicTableName(String sql, String tableName) {
        //表名增加hash值
        String tabSuffix = tableName + "_" + DbTableUtil.getTableIndex(USER_ID_CONTEXT.get(), 256);
   

        return tabSuffix;
    }

    public static void put(Long userId) {
        USER_ID_CONTEXT.set(userId);
    }

    public static void remove() {
        USER_ID_CONTEXT.remove();
    }
}

 

 

  • 注册动态表名拦截器 
/**
 * @author miemie
 * @since 2018-08-10
 */
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());

        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MARIADB);
        interceptor.addInnerInterceptor(paginationInnerInterceptor);

        return interceptor;
    }


      /**
     * 注册动态表名拦截器
     *
     * @return 动态表名拦截器
     */
    private DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor =
                new DynamicTableNameInnerInterceptor();

        Map tableNameHandlerMap = new HashMap<>();

        TableNameHandler tableNameHandler = new DateTableNameHandler();
        tableNameHandlerMap.put("user_history", tableNameHandler); 

        TableNameHandler testingPutUserIdTableNameHandler = new UserAscribeTableNameHandler();
        tableNameHandlerMap.put("user", testingPutUserIdTableNameHandler);

        dynamicTableNameInnerInterceptor.setTableNameHandlerMap(tableNameHandlerMap);

        return dynamicTableNameInnerInterceptor;
    }
}

MyBatis-Plus打印日志

在 logback.xml 中 自定义logger ,日志级别设置为 degug 

    
    
        
    

总结

以上主要是对MyBatis-Plus 在日常开发中做下总结和记录。

参考:

简介 | MyBatis-Plus

卷王必备学习的MyBatis-Plus用法,不来瞧瞧吗~~-阿里云开发者社区

MyBatisPlus如何进行分表查询? - 知乎

你可能感兴趣的:(分布式,servlet,和,mvc,spring,mybatis,架构,java,微服务)