Mybatis-plus

195 阅读4分钟

Mybatis-plus

Mybatis-plus框架入门

引入依赖

<!--mybatis-plus-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>

mapper简化

数据源和原生的mybatis配置一样

@Repository
public interface UserMapper extends BaseMapper<User>{
}

​ 通过继承BaseMapper类,从而直接继承了常规的CRUD方法,避免大量重复代码的编写,将实体类写入BaseMapper的泛型中。

​ 不要忘了在启动类上加上MapperScan注解开启mapper扫描

查看sql日志

在配置文件中添加:

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

ID的生成策略

在Mybatis-plus框架中,可以指定不同的ID生成策略,只需要在需要指定的ID属性上加上@TableId注解,注解中指定Type属性,就能实现不同的ID生成。

也可以设置全局的主键生成策略,在配置文件中配置:

# 全局采用自增策略
mybatis-plus.global-config.db-config.id-type=auto

自增策略

​ 自增策略采取表中的主键依次自增,在分表的时候,新表的ID生成需要首先查询前一张表的最后一个ID,然后再+1,会影响执行效率,但是便于主键的排序。

@TableId(type = IdType.AUTO)//IdType.AUTO	自增策略
private Long id;

UUID生成ID

​ 使用UUID生成完全随机数,在分表时不需要再查询上一张表的ID信息,可以直接生成,但是这样的完全随机数不利于对表的排序操作

@TableId(type = IdType.UUID)//IdType.AUTO	UUID策略
private Long id;

redis生成ID

​ 由于redis是单线程的,所以可以用redis来生成ID,用redis的原子操作INCR和INCRBY来实现

例如:在集群环境下有5太redis,江浙舞台redis编号1,2,3,4,5,然后每台redis生成的ID值都是+5,如下:

A: 1 6 11 16 21

B: 2 7 12 17 22

C: 3 8 13 18 23

D: 4 9 14 19 24

E: 5 10 15 20 15

​ 这样redis生成的ID既不需要在分表的时候查询前表,也能实现对表的排序操作

@TableId(type = IdType.INPUT)//IdType.INPUT	自定义策略
private Long id;
@TableId(type = IdType.NONE)//IdType.NONE	无策略
private Long id;

mybatis-plus生成id(默认)

​ Mybatis-plus采用的雪花算法,生成的是有序的随机数,大多采取这种主键ID的方案

@TableId(type = IdType.ID_WORKER)//IdType.ID_WORKER	mybatis-plus默认策略
private Long id;

//当id为String类型时
@TableId(type = IdType.ID_WORKER_STR)//IdType.ID_WORKER_STR	mybatis-plus默认策略
private Long id;

自动填充

​ 自动填充是用mybatis-plus的方式来实现对一些系统生成的字段的生成,例如:create_time,update_time

配置需要填充的字段

    @TableField(fill = FieldFill.INSERT)//这里的FieldFill.INSERT表示此字段在执行insert操作的时候填充
    private Date createTime;

填充时机的枚举类型包括:

public enum FieldFill {
    /**
     * 默认不处理
     */
    DEFAULT,
    /**
     * 插入填充字段
     */
    INSERT,
    /**
     * 更新填充字段
     */
    UPDATE,
    /**
     * 插入和更新填充字段
     */
    INSERT_UPDATE
}

配置填充执行器

​ 填充逻辑需要创建一个类,实现MetaObjectHandler接口的方法,并且需要将此类加入ioc容器中

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {//使用mapper的insert操作,就会执行此方法
        this.setFieldValByName("createTime",new Date(),metaObject);//根据名称设置填充值
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {//使用mapper的修改操作,这个方法会执行
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

乐观锁

​ 乐观锁是为了解决写操作的更新失效的问题,通过记录版本号,实现对数据的正常更新。

乐观锁的实现步骤

  • 首先需要在数据库表中添加一个字段用来记录版本号
ALTER TABLE USER ADD COLUMN 'version' INT;
  • 然后在表对应的实体类中的版本号属性前加上@Version注解,表明这是版本号
@Version
private Integer version;
  • 在配置类中注册乐观锁插件(官网直接复制)
//乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
}
  • (可选)为了使版本号默认,可以在insert数据时自动填充为初始值

分页插件

​ mybatis-plus中整合了分页,只需要配置分页插件就能实现分页功能。

分页插件的使用步骤

  • 配置分页插件的bean
    //分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
  • 编写分页的逻辑
//分页查询
@Test
void testSelectByPage(){
    Page<User> page=new Page<>(1L,3L);

    userMapper.selectPage(page,null);//第二个参数是查询条件

    System.out.println(page.getCurrent());//获取当前页
    System.out.println(page.getRecords());//获取分页后的数据list
    System.out.println(page.getSize());//每页显示的记录数
    System.out.println(page.getTotal());//总记录数
    System.out.println(page.getPages());//总页数

    System.out.println(page.hasNext());//是否有下一页
    System.out.println(page.hasPrevious());//是否有上一页
}

==注意==: 在执行了selectPage(page,wrapper)方法之后,所有的分页数据都会存储在page对象中,无需接收返回值


逻辑删除

​ 逻辑删除和物理删除的区别是:

​ 逻辑删除并没有将数据从数据库中删除,而是通过一个字段来设置该数据为删除数据,让查询语句不能查询出这类删除数据。当需要对被删除数据恢复时,只需要将删除的状态修改为未删除即可。

​ 物理删除是将数据从数据库中删除,当需要对被删除的数据做恢复时比较麻烦。

逻辑删除的实现

  • 在数据库表中添加一个用于表示逻辑删除的列(deleted)
  • 在表的实体类中的逻辑删除属性上加上注解@TableLogic
@TableLogic
private Integer deleted;
  • 在配置类中注册逻辑删除的插件
//逻辑删除插件
@Bean
public ISqlInjector sqlInjector(){
    return new LogicSqlInjector();
}
  • (可选)配置逻辑删除的默认值
#逻辑删除的默认值,1已删除,0未删除
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

==注意==:逻辑删除实际上执行的是update的操作,只是将逻辑字段更新,并且在执行查询操作的时候会加上逻辑字段=0的条件。


性能分析

​ mybatis-plus集成了一个性能分析插件,主要用于在开发和测试环境下对sql语句的执行时间的监控和控制

性能分析插件的使用步骤

  • 在配置类中注册性能分析插件
/*SQL性能分析插件
* 开发环境使用,线上不推荐,maxTime值得是sql最大执行时长
* */
@Bean
@Profile({"dev","test"})//在dev和test环境下开启
public PerformanceInterceptor performanceInterceptor(){
    PerformanceInterceptor performanceInterceptor=new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(200);//ms,超过此处设置的ms则sql不执行,并报错
    performanceInterceptor.setFormat(true);//是否将sql格式化,默认是false
    return performanceInterceptor;
}
  • 在配置文件中指定当前的项目环境
#环境设置:dev、test、prod
spring.profiles.active=dev

Wrapper实现复杂查询

​ mybatis-plus的BaseMapper提供了基本的CRUD方法,但是在还可以从这些基本的方法中自定义一些条件,通过Wrapper可以实现较为复杂的查询(过于复杂的查询只能通过xml配置文件的方式来实现)。

Wrapper的继承体系

//TODO 图片待补充

用QueryWrapper比较多

Wrapper复杂查询的实现步骤

//复杂查询
@Test
public void testWrapper(){
    //创建queryWrapper对象
    QueryWrapper<User> wrapper=new QueryWrapper<User>();

    //ge,gt,le,lt   >=,>,<=,<
    //wrapper.ge("age",120);
    //wrapper.lt("age",30);

    //eq,ne     ==,!=
    //wrapper.eq("name","Jone");
    //wrapper.ne("name","Jone");


    //between
    //wrapper.between("id",1,3);

    //like:模糊查询
    //wrapper.like("name","j");

    //orderByDesc/Asc:排序
    //wrapper.orderByDesc("age");

    //last:在sql的最后加上一条语句
    //wrapper.last("limit 1");

    //指定要查询的列
    //wrapper.select("id","name");

    List<User> users = userMapper.selectList(wrapper);
    System.out.println(users);
}