MybatisPlus

MybatisPlus

 

一、介绍

MybatisPlus 是一个Mybatis的增强工具,为简化开发、提升效率而生。

功能:

  • 自动生成单表的CRUD 功能

  • 提供丰富的 条件拼接方式

  • 全自动ORM类型持久层框架

 

二、简单使用

  1. 导入 mybatis-plus 的 启动器

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    ​
        <!-- 测试环境 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    ​
        <!-- mybatis-plus  -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>
    ​
        <!-- 数据库相关配置启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
    ​
        <!-- druid启动器的依赖  -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-3-starter</artifactId>
            <version>1.2.23</version>
        </dependency>
    ​
        <!-- 驱动类-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
    ​
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
        </dependency>

    导入 mybatis-plus 就不用 导入 mybatis了有依赖传递

  2. 创建配置文件、启动类、实体类等

  3. 创建Mapper 接口,该接口继承 BaseMapper,泛型就是对应的实体类

    继承该接口,自带 crud 方法

    package com.ztone.mapper;
    ​
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.ztone.pojo.User;
    ​
    public interface UserMapper extends BaseMapper<User> {
    }

    image-20240821151844672

    image-20240822092822776

     

    @Test
    public void test(){
        List<User> users = userMapper.selectList(null);
        System.out.println(users);
    }

    使用时直接调用 里面的方法即可。

     

 

三、Service层的 crud

在mapper 接口中继承了 BaseMapper后,就可以直接调用上面的增删查改。

在service 层也可以直接进行 crud 的操作然后返回给controller,不用经过 mapper。

具体方式如下:

  • service 层接口 继承 IService 接口,该接口提供了一些crud的操作方法,但是实现了一部分,另一部分没有实现

  • 需要service 层接口的实现类,在实现接口的同时,继承ServiceImpl 类。这个类是 IService的实现类,实现了IService中没有实现的方法。

package com.ztone.service;
​
import com.baomidou.mybatisplus.extension.service.IService;
import com.ztone.pojo.User;
​
public interface UserService extends IService<User> {
}
package com.ztone.service.impl;
​
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ztone.mapper.UserMapper;
import com.ztone.pojo.User;
import com.ztone.service.UserService;
​
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

继承 ServiceImpl 传入的泛型是:继承了 BaseMapper的mapper 和 实体类

service层提供的方法:

Save:

//插入一条记录
boolean save(T entity);
//批量插入
boolean saveBatch(Collection<T> entityList);
//批量插入,指定插入的最大条数
boolean saveBatch(Collection<T> entityList,int batchSize);

 

SaveOrUpdate:

//主键如果没有值就是插入,有值就是更新
boolean saveOrUpdate(T entity);
...

 

Remove:

//根据 queryWrapper 设置的条件,删除记录
boolean remove(Wrapper<T> queryWrapper);
//根据id删除
boolean removeById(Serializable id);
//根据map条件删除
boolean removeByMap(Map<String,Object> columnMap);
//批量删除
boolean removeByIds(Collection idList);

 

Update:

//根据updateWrapper 更新
boolean update(Wrapper<T> updateWrapper);
//根据id更新
boolean updateById(T entity);
//根据id批量更新
boolean updateBatchById(Collection entityList);

 

Get:

//根据id查询
T getById(Serializable id);
//根据wrapper 查询一条记录
T getOne(Wrapper queryMapper);

 

四、分页

实现分页的步骤:

  1. 创建分页插件

    在配置类中,将mybatis-plus 的插件集合加入到 ioc容器

    将需要的 分页插件加到 插件集合中

    @Bean
    public MybatisPlusInterceptor plusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor
                (new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }

    创建分页插件时,需要传入 数据库的类型,如DBType.MYSQL

  2. 使用

    创建Page对象,传入的参数是页码和页容量,调用 selectPage,传入 page对象

    查询的结果也会保存在 page对象中

    @Test
    public void test_page(){
        Page<User> page = new Page<>(1, 2);
    ​
        userMapper.selectPage(page,null);
        //当前页数据
        List<User> records = page.getRecords();
        //总条数
        long total = page.getTotal();
        System.out.println(records);
    }

在自定义的方法中使用 Page 分页,mapper 接口中的查询方法的 返回值和第一个参数都是 Page 类型

Page<User> queryByAge(Page page,Integer age);

这样在调用该方法时传入 page 对象,得到的就是分页的结果

 

五、条件构造器

条件构造器是把查询条件封装成对象,不需要手动编写复杂的sql语句。

直接调用对象的方法,传入列名或值,就能拼接成响应的条件语句

方法:

  • eq("列名","值") ---相等

  • ne("列名","值") ---不相等

  • gt("列名","值") ---大于

  • ge("列名","值") ---大于等于

  • lt("列名","值") ---小于

  • le("列名","值") ---小于等于

  • between("列名","值","值") ---设置 between

  • between("列名","值","值") ---设置 not between ,不在这两个值之间

  • like("列名","值") ---设置 like 模糊查询

  • isNull("列名") ---设置 is null

...........

1.QueryWrapper

@Test
public void test_01(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("name","a");
    queryWrapper.between("age",21,30);
    userMapper.selectList(queryWrapper);
}

 

默认是查询全部列,如果想要指定查询的列,可以调用 select 方法

queryWrapper.select("name","age");

 

通过方法连续调用是用 and 关键字来连接每个条件

QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","a").between("age",21,30);

上面的代码相当于 SELECT id,name,age,email FROM user WHERE (name LIKE a AND age BETWEEN 21 AND 30)

如果想要使用 or 来 连接条件,可以调用 or方法

QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","a").between("age",21,30).or().isNull("email");

上面的代码相当于

SELECT id,name,age,email FROM user WHERE (name LIKE a AND age BETWEEN 21 AND 30 OR email IS NULL)

 

 

每个条件方法,都会有一个boolean 类型的参数,这个参数可以是一个表达式,当返回true时,该条件生效,返回false不生效

QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(name),"name","a")
    .between(age != null && age >18,"age",21,30).or().isNull("email");

这样可以动态判断 参数的条件,就像 mybatis 中的 动态sql

< if test="" 标签

 

2.UpdateWrapper

主要是用来做 更新的条件对象,用queryWrapper 也可以进行更新操作,但是有两个问题:

  • 需要自己创建实体类,传到更新方法中

  • 不能将值修改为null,因为在queryWrapper中,值是 null 的话就不去修改该列

所以进行更新操作可以用 updateWrapper

调用对象的set 方法,传入相应的列名和值,就可以指定列了

@Test
public void test_02(){
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.ge("age",20)
            .set("name","xxxxxx")
            .set("email","123345@");
    
    userMapper.update(null,updateWrapper);
    
}

 

3.LambdaQueryWrapper 和 LambdaUpdateWrapper

这两个类是 对 QueryWrapper 和 UpdateWrapper 的升级

当传入列名时,有可能会将列名写错。

那么用 LambdaQueryWrapper ,第一个参数传入一个 lambda表达式,来表示列名

该表达式是用 实体类的方法引用 User::getName() 来表示该属性,从而代表了数据库的列

@Test
public void test_02(){
    LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper.ge(User::getAge,20)
            .set(User::getName,"xxxxxx")
            .set(User::getEmail,"123345@");
    
    userMapper.update(null,updateWrapper);
    
}

 

六、注解

1. @TableName

当数据库的表名和 实体类不同时,用BaseMapper 的方法就会找不到数据库表。

这时可以在实体类上使用 @TableName 注解,标注数据库表名可以解决这一问题。

如果实体类和数据库表名的差距只是一个前缀的问题,可以在配置文件中设置给所有实体类加上前缀

mybatis-plus:
  global-config:
    db-config:
      table-prefix: t_

这样就不用在每个实体类前面使用 @TableName,前提是你的数据库表名前缀相同并且后面和实体类名相同

 

2. @TableId

@TableId 是用在 实体类的主键属性上

  • 当主键的列名和属性名不一致时,可以使用该注解指定属性对应的列名

    @TableId(value="t_id")
    private Long id;

 

该注解还有一个属性是 type 用来指定插入数据时如何生成主键值,type有两个值

  • AUTO 数据库id自增长,要求数据库主键自增长开启

  • ASSING_ID 通过雪花算法生成不重复的long类型数字,也是默认值,要求数据库主键是bigint或 varchar(64)

 

当一个表数据非常多,需要横切分成几个表,那么主键的生成建议用雪花算法,因为如果自增长可能会和下一个表中的主键重复

 

全局设置主键策略:

mybatis-plus:
  global-config:
    db-config: 
      id-type: auto

 

3. @TableField

当属性名和数据库表的列名不一致时,可以在实体类的属性上使用该注解,指定列名

他还有一个属性是 exist,设置为false表示无论属性名和列名相不相同都忽略这个属性

 

 

七、高级扩展

1. 逻辑删除

  • 物理删除:真实删除,将数据在数据库中删除,之后完全查询不到

  • 逻辑删除:假删除,给数据库添加一个字段,逻辑删除后,将该字段的状态修改为被删除状态,在数据库中仍然可以看到该数据。

逻辑删除的实现:

  1. 给数据库添加 删除状态字段

  2. 给实体类添加逻辑删除属性

  3. 在属性上使用 @TableLogic

当你删除时,他会自动把删除语句改成 更新语句,并且查询时条件加上不查询已删除状态的字段

 

除了使用@TableLogic 指定删除状态字段,还可以全局指定,并且指定字段的已删除和未删除对应的值(默认是0和1)

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: delete
      logic-delete-value: 1
      logic-not-delete-value: 0

 

2.乐观锁

乐观锁和悲观锁 是防止在并发数据更新时出现错误数据

乐观锁的实现方式这里用的是版本号的方式

版本号:为数据添加一个版本号字段,每次数据更新时,比较当前版本号是否与期望值一致,如果一致则更新成功,不一致表示数据已经被更改,那么就不会去更新数据了

实现步骤:

  1. 给数据库添加 版本号的字段

  2. 在配置类中,添加乐观锁的插件

    @Bean
    public MybatisPlusInterceptor plusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }

    OptimisticLockerInnerInterceptor() 就是乐观锁插件

     

  3. 给实体类添加版本号属性,并且在属性上使用 @Version 注解

    @Data
    public class User {
        private Integer id;
        private String name;
        private Integer age;
        private String email;
    ​
        @Version
        private int version;
    }

 

正常进行更新操作即可。

在执行更新语句时,会判断版本号是否是最新的,并且更新时也会更新版本号

 

3.防止全表更新或删除

通过在配置类中添加 拦截器实现

@Bean
public MybatisPlusInterceptor plusInterceptor(){
    MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
  
    mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
    return mybatisPlusInterceptor;
}

BlockAttackInnerInterceptor

 

八、逆向工程

通过数据库表生成 实体类、mapper接口、mapper的xml文件、service接口和实现类

需要idea 的插件 MyBatisX

将数据库导入到idea后

image-20240822165820868

MyBatisX-Generator 选项

image-20240822170120645

生成文件的配置

 

image-20240822170211187

选择根据mybatis-plus3 生成文件

 

自动生成sql代码:

在mapper接口中,选择想要生成的sql

image-20240822170949009

选择好想要生成的 sql alt+ enter 然后选择第一个就能直接生成sql语句

posted @ 2024-08-22 17:25  GrowthRoad  阅读(46)  评论(0编辑  收藏  举报