Mybatis-plus 快速开发 超级全面的总结包括高级查询

Mybatis-plus

概况

Mybatis-plus 是在mybatis的基础上进行开发,简化Mybatis的一些操作,提高开发效率,并支持任意mybatis支持的数据库。

优点(相对于mybatis)

  1. 简化了CRUD 操作 sql 语句,通过反射分析类名,扫描字段来拼接 sql 语句,不用我们写,就可以进行简单增删改查。

  2. 内置代码生成器,分页插件,性能分析插件等

实现原理

继承BaseMapper并在后面的泛型中添加实体类,让BaseMapper根据你的实体类,通过反射获取实体类名,默认把实体类名当作表名,而把内部的属性默认当成列名,根据你调用的方法,判断对数据库的操作是什么,来进行数据库操作。

如:你的EmployeeMapper接口继承了BaseMapper,并在泛型中写把 Employee 实体类,通过反射获取 类名 employee 当表名,字段当列名 ,调用的selectList 方法判断你是进行查询操作,就可以拼接sql语句进行查询。

select 列名 from 表名 

快速开发

添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.17</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.22</version>
</dependency>

继承BaseMapper

public interface EmployeeMapper extends BaseMapper<Employee> {}

实体类

@Data
public class Employee {
    private Long id;
    private String username;
    private String name;
    private String password;
    private String email;
    private Long age;
    private boolean admin;
    private Long deptId;
}

配置文件

#mysql
spring.datasource.url=jdbc:mysql://localhost:3306/car?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=rootproperties
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 配置slq打印日志
logging.level.cn.wolfcode.mp.mapper=debug

还有app配置类

@SpringBootApplication
@MapperScan(basePackages = "cn.wolfcode.mapper")
public class app {
    public static void main(String[] args) {
        SpringApplication.run(app.class,args);
    }
}

测试 (测试类要添加依赖 spring-boot-starter-test)

@SpringBootTest
public class TestMybatisPlus {
    @Autowired
    private EmployeeMapper employeeMapper;
    @Test
    public void test(){
        List<Employee> list = employeeMapper.selectList(null);
        for (Employee employee : list) {
            System.out.println(employee);
        }
    }
}
​

注意:

如果是使用 insert 方法,id默认是用内部的雪花算法,计算出唯一 id ,而不是数据库自动增长 id ,要自动增长 id 需要使@TableId 自己设置。

相关注解

概况

Mybatis-plus 是根据实体来拼接 sql 语句,来达到无 sql 操作,为了避免表名和实体类名不一样、字段名和数据库列名不一样,而通过一些相关注解来解决相关问题。

注解与相关作用

  1. @TableName:指定当前实体类映射哪张表数据库表,默认是跟实体类名一样,贴在实体类上。

  2. @TableField:指定当前属性映射数据库表哪一列,默认跟属性名一样,value是指定数据库名,exist指定是否当查询列名,如果是false,不查询该列。

  3. @TableId:标记当前属性映射表主键, IdType 指定当前 id 是自动增长还是使用默认的雪花算法 id 等其他值。exist 表示是否为是数据库字段

使用BaseMapper

日志使用

mybatis使用的日志格式是

logging.level.cn.wolfcode.mp.mapper=debug

而mybatis-plus使用的日志格式是

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

这两者是有区别的,一个使用mybatis内置得日志,在springboot中输出在信息初始化列中,不方便看。mybatis-plus是直接输出在控制台中,方便看

插入数据Id异常

数据插入时,默认是使用雪花算法,计算出唯一字符串,当 id 插入数据库。

解决方法

使用@TableId解决,就是把默认的雪花算法关闭,type 指定主键生成的方式。

使用 insert(Object entity) 方法

@Test
public void insert(){
    Employee employee = new Employee();
    employee.setUsername("f");
    employee.setName("f");
    employee.setPassword("f");
    employeeMapper.insert(employee);
}

实体类中有不是不属于 列 的属性报错

使用exist 表示是否为是数据库字段,默认是true,如果指定为false,则不会在查询的时候添加到 sql 语句中。

修改数据错误

参数实体属性值为null,不参与 sql 拼接,如果参数实体属性类型是8大基本类型,有默认值,mybatis-plus认为有属性值,就参与sql拼接,会引起参数丢失问题。

解决方案

  1. 把基本类型改成包装类型。

  2. 先查询,再修改,后更新。

  3. 使用update(null,wrapper)方法操作,部分字段更新方法。

updateById(Object entity)

//sql语句   UPDATE employee SET username=?, name=?, password=?, admin=? WHERE id=?
//因为admin是基本 boolean 类型有值。
@Test
public void updateById(){
    Employee employee = new Employee();
    employee.setId(1408412759464095748L);
    employee.setUsername("cc");
    employee.setName("cc");
    employee.setPassword("c");
    employeeMapper.updateById(employee);
}

update(null,wrapper)的使用

@Test
public void update(){
    /*
        //普通的条件构造器
        UpdateWrapper<Employee> updateWrapper = new UpdateWrapper();
        //拼接条件语句 如: where name = ?
        updateWrapper.eq("id",1408412759464095748L);
        //修改列
        updateWrapper.set("name","sdfsd");
        //sql 语句:  UPDATE employee SET name=? WHERE (id = ?)
*/
    //支持lambda的 条件构造器
    LambdaUpdateWrapper<Employee> lambdaUpdateWrapper = new LambdaUpdateWrapper();
    //使用 Lambda 表达式 注入sql语句
    lambdaUpdateWrapper.eq(Employee::getId,"1408412759464095748L");
    lambdaUpdateWrapper.set(Employee::getName,"fdf");
    //        UPDATE employee SET name=? WHERE (id = ?)
    employeeMapper.update(null,lambdaUpdateWrapper);
}

updateById 和 update 怎么选择?

在sql 的 where 条件为 id 时 和 全部字段更新时,使用 updateById

在sql 的 where 条件不一定为 id 时,和部分字段更新时,使用 update

delete方法

  1. deleteById(Long id) :

    //删除 id 为 1408412759464095748L 数据
    //DELETE FROM employee WHERE id=?
    employeeMapper.deleteById("1408412759464095748L");
  2. deleteBatchId(List list) sql 语句

    //删除 id 为 list 中的值
    //DELETE FROM employee WHERE id IN ( ? , ? )
    employeeMapper.deleteBatchIds(Arrays.asList(1408412759464095747L,35L));
  3. delete(Wrapper wrapper) sql 语句

    //删除符合 wrapper 中的条件的数据
    //DELETE FROM employee WHERE (id = ? OR username = ?)
    QueryWrapper<Employee> queryWrapper = new QueryWrapper();
    queryWrapper.eq("id",33L).or().eq("username","a");
    employeeMapper.delete(queryWrapper);
  4. deleteByMap(Map map) sql 语句

    //删除条件为 map 中的 数据
    // DELETE FROM employee WHERE name = ?
    HashMap<String, Object> hashMap = new HashMap<>();
    hashMap.put("name","l");
    employeeMapper.deleteByMap(hashMap);

    注意:只进行 id 条件的删除请用 deleteById 或 deleteBatchId。

select方法

  1. selectById(Long id) 根据 id 查询

    // SELECT id,username,name,password,email,age,admin,dept_id FROM employee WHERE id=?
    employeeMapper.selectById(9L);
  2. selectBatchIds(List list) 查询 list 的 id 。

    /*SELECT id,username,name,password,email,age,admin,dept_id 
    FROM employee 
    WHERE id IN ( ? , ? )*/
    employeeMapper.selectBatchIds(Arrays.asList(10L,11L));
  3. selectByMap(Map map) 查询条件为 map 中的数据,全部默认为 and 查询,有条件为 or 不建议使用

    /* SELECT id,username,name,password,email,age,admin,dept_id 
        FROM employee 
        WHERE name = ? AND username = ? */
    HashMap<String, Object> hashMap = new HashMap<>();
    hashMap.put("username","菠萝吹雪");
    hashMap.put("name","王五");
    employeeMapper.selectByMap(hashMap);
  4. selectCount(Wrapper wrapper) 查询条件password 为 ?并 id 存在的数量,返回值为 Integer 类型。

    //SELECT COUNT(id) FROM employee WHERE (password = ?)
    QueryWrapper queryWrapper = new QueryWrapper();
    queryWrapper.eq("password","c4ca4238a0b923820dcc509a6f75849b");
    queryWrapper.select("id");
    employeeMapper.selectCount(queryWrapper);
  5. selectList(Wrapper wrapper) 查询wrapper 中条件的所有数据,如果要自己设置查询列,使用wrapper.select(String column)

    /*SELECT id,username,name,password,email,age,admin,dept_id 
        FROM employee 
        WHERE (admin = ?)*/
    QueryWrapper queryWrapper = new QueryWrapper();
    queryWrapper.eq("admin",false);
    employeeMapper.selectList(queryWrapper);
  6. selectMaps(Wrapper wrapper) 这查询的返回值是List<泛型>,泛型一般使用 Map ,可以装一些和实体无关的属性。

    // SELECT id,username FROM employee
    QueryWrapper queryWrapper = new QueryWrapper();
    queryWrapper.select("id","username");
    List<Map<String,Object>> list = employeeMapper.selectMaps(queryWrapper);
  7. selectPage(Wrapper wrapper) 使用分页查询首先去配置类中,配置启动分页插件bean

    启动分页插件

    ​
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        paginationInnerInterceptor.setOverflow(true); //合理化
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }

    使用分页查询

    QueryWrapper queryWrapper = new QueryWrapper();
    IPage<Employee> page = new Page<>(1,5);
    queryWrapper.select("id","username","name");
    employeeMapper.selectPage(page,queryWrapper);

selectMaps和selectList的使用区别

selectMaps返回的是一个 List< Map > 集合,而selectList返回的是List< 对象 >集合,如果查询的数据能封装成 domain 使用 selectList ,不能复制成 domain 使用 selectMaps 。一般我们分组查询数量时,就使用 selectMaps,好处就是查询的数据不在实体类,并且我们不用添加额外的属性和创建一个新的类去封装数据。

条件构造器

条件构造器是 mybatis-plus 特有的,是用增强 mybatis 用的,可以帮我们编写 sql 语句。

继承体系图

我们使用的所有条件构造器都间接或直接继承了 AbstractWrapper 类,而继承 AbstractLambdaWrapper 这个类,就可以使用 Lambda 表达式来指定条件。

继承 AbstractWrapper 这个类有两种条件类型,一种是修改型子类,独立的实现方法有 set 和setSql,另一种是查询型子类,独立的实现方法有 select。

set 和 setSql的区别

set 是使用预编译来拼接 sql 语句,而 setSql 是直接拼接 sql 语句,使用 set 效率比 setSql 效率高,所以建议尽量使用 set。

注意: 使用查询 Wrapper 的 select(String column,...) 时,要直接把想显示的列全都安照顺序写好,注意,第二次调用 select 时会替换掉第一次的 select

建议

使用Lambda条件构造器来操作。

UpdateWrapper 和 LambdaUpdateWrapper 的使用

//普通的条件构造器
UpdateWrapper<Employee> updateWrapper = new UpdateWrapper();
//拼接条件语句 如: where name = ?
updateWrapper.eq("id",1408412759464095748L);
//修改列 update set bane='sefad' from employee
updateWrapper.set("name","sdfsd");
employeeMapper.update(null,lambdaUpdateWrapper);
​
//------------------------------------------------------
​
//支持lambda的 条件构造器
LambdaUpdateWrapper<Employee> lambdaUpdateWrapper = new LambdaUpdateWrapper();
//使用 Lambda 表达式 注入sql语句
lambdaUpdateWrapper.eq(Employee::getId,"1408412759464095748L");
lambdaUpdateWrapper.set(Employee::getName,"fdf");
//        UPDATE employee SET name=? WHERE (id = ?)
employeeMapper.update(null,lambdaUpdateWrapper);

QueryWrapper 和 LambdaQueryWrapper的使用

//普通的条件构造器
QueryWrapper<Employee> queryWrapper = new QueryWrapper();
//指定要显示出来的列
queryWrapper.select("id","username");
List<Map<String,Object>> list = employeeMapper.selectMaps(qeryWrapper);
​
//------------------------------------
​
//支持lambda的 条件构造器
LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper();
//指定根据排序列
lambdaQueryWrapper.orderByDesc(Employee::getId);
//指定要显示出来的列
lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword);
List<Employee> employees = employeeMapper.selectList(lambdaQueryWrapper);

工具类 wrappers ,可以用来开始创建一个各种条件构造器。

高级查询

  1. select:指定返回的列

  2. 排序

    1. orderByAsc:指定某列进行顺序排序

    2. orderByDesc :指定某列进行倒序排序

    3. orderBy:根据条件指定谋列进行排序

    使用

    ​
    LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper();
    lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword);
    lambdaQueryWrapper.orderByAsc(Employee::getId);
    List<Employee> employees = employeeMapper.selectList(lambdaQueryWrapper);
    employees.forEach(System.err::println);
    ​
    //清空lambdaQueryWrapper
    lambdaQueryWrapper.clear();
    lambdaQueryWrapper.orderByDesc(Employee::getId);
    lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword);
    List<Employee> employees2 = employeeMapper.selectList(lambdaQueryWrapper);
    employees2.forEach(System.err::println);
    ​
    //清空lambdaQueryWrapper
    lambdaQueryWrapper.clear();
    //       按调用来进行先后排序
    lambdaQueryWrapper.orderByAsc(Employee::getAge);
    lambdaQueryWrapper.orderByDesc(Employee::getId);
    lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword);
    List<Employee> employees3 = employeeMapper.selectList(lambdaQueryWrapper);
    employees3.forEach(System.err::println);
    ​
    //清空lambdaQueryWrapper
    lambdaQueryWrapper.clear();
    //        condition (条件):允许排序, isAse :是否进行正排序 排序的列,可以多个
    lambdaQueryWrapper.orderBy(true,false,Employee::getAge,Employee::getId);
    lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword);
    List<Employee> employees4 = employeeMapper.selectList(lambdaQueryWrapper);
    employees4.forEach(System.err::println);
    ​
    //      排序的列为空的数据排在最上面,可以调用多个排序方法
    ​

  3. 分组查询

    1. groupBy:根据谋列进行分组

    2. having:显示符合该条件的分组

    使用

    ​
    LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper();
    lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword);
    lambdaQueryWrapper.groupBy(Employee::getAge);
    List<Map<String, Object>> employees = employeeMapper.selectMaps(lambdaQueryWrapper);
    employees.forEach(System.err::println);
    ​
    QueryWrapper queryWrapper = new QueryWrapper();
    queryWrapper.groupBy("age");
    queryWrapper.select("age","count(id) count");
    queryWrapper.having("count > 2");
    List<Map<String, Object>> employees1 = employeeMapper.selectMaps(queryWrapper);
    employees1.forEach(System.err::println);

  4. 条件查询

    1. 比较运算符

      1. allEq: 把 list 中的全部条件进行 and 比较,查询出符合要求的数据

      2. eq:查询等于该条件的数据,后面可以使用多个 eq ,使用 and 拼接。

      3. ne:查询不等于该条件的数据。

      4. gt:大于该条件的数据

      5. ge:大于或等于该条件的数据

      6. lt:小于该条件的数据

      7. le:小于或等于该条件的数据

      8. between:取在 betwenn 中间的数据

      9. notBetWeen:取不在 betwenn 中间的数据

      10. isNull:取等于null的数据、

      11. in:取等于 in 中所有的数据

      12. notIn:取不等于 in 中所有的数据

      13. inSql:取等于 in 中所有的数据,字符串拼接进去的

      14. notinSql:取不等等于 in 中所有的数据,字符串拼接进去的

      使用

        QueryWrapper<Employee> queryWrapper = new QueryWrapper<Employee>();
              queryWrapper.select("id","username","name");
      //        Map<String,Object> map = new HashMap<>();
      //        map.put("age",29L);
      //        map.put("password","c4ca4238a0b923820dcc509a6f75849b");
      //        queryWrapper.allEq(map);
      //        List<Map<String, Object>> employees = employeeMapper.selectMaps(queryWrapper);
      ​
      ​
              //获取不等于34的数据
      //        queryWrapper.ne("age",34L);
      //        List<Map<String, Object>> employees = employeeMapper.selectMaps(queryWrapper);
      ​
      //        queryWrapper.inSql("age","34,19,29");
      //        List<Map<String, Object>> employees = employeeMapper.selectMaps(queryWrapper);

    2. 模糊查询

      1. like:查询某列中能匹配到该条件的数据

      2. notList:查询某列中能不匹配到该条件的数据

      3. likeLeft:查询某列中能在第一个字母开始,匹配到该条件的数据

      4. likeRight:查询某列中能在最后一个字母开始,匹配到该条件的数据

      使用

      QueryWrapper<Employee> queryWrapper = new QueryWrapper<Employee>();
      queryWrapper.select("id","username","name");
      queryWrapper.like("username","de");
      employeeMapper.selectMaps(queryWrapper);
      //
      queryWrapper.clear();
      queryWrapper.notLike("username","de");
      employeeMapper.selectMaps(queryWrapper);
      //
      queryWrapper.clear();
      queryWrapper.likeLeft("username","de");
      employeeMapper.selectMaps(queryWrapper);
      //
      queryWrapper.clear();
      queryWrapper.likeRight("username","de");
      employeeMapper.selectMaps(queryWrapper);

    3. 逻辑运算符

      1. or:指定条件为 Or

      2. and:指定条件 And

      使用

      QueryWrapper<Employee> queryWrapper = new QueryWrapper<Employee>();
      queryWrapper.select("id","username","name");
      //不管事and 还是 or 条件当成参数传进去后 sql 一定在括号中
      queryWrapper.or(wq->wq.eq("username","defei").or().eq("id",21L)).eq("age",34L);
      employeeMapper.selectList(queryWrapper);
      ​
      //注意:and一定要传参数
      queryWrapper.eq("id",21L).and(wq->wq.eq("age",34L));
      employeeMapper.selectList(queryWrapper);
      ​
  5. 自定义Sql

    1. 和mybatis的使用方式一模一样

    2. 注解方式

      1. 在Mapper接口中使用,直接在方法上贴一个@Select 注解,并且在注解中写sql语句

      2. 在使用多表查询的时候,可以使用@Result注解定哪个列映射到哪个属性中。

Service接口

Service接口的一些常用方法,mybatis-plus 官方已经已经帮我们写好了,我们只要在接口中继承mybatis-plus 官方帮我们写 IService<泛型>接口,就可以指定一些常用的方法,到时候我们的实体类实现这个来后,我们就可以自己实现这些方法,也可以在继承mybatis-plus 官方帮我们写 ServiceImpl<泛型1,泛型2>,只要我们把 接口Mapper 放在泛型1,实体类放在泛型2,IService接口中的所有方法都帮我们实现好了,到时候我们直接调用就行了。

例子

​
public interface IEmployeeService extends IService<Employee> {
​
}
​
​
@Transactional
@Service
//这里是通过把 EmployeeMapper Employee 传给ServiceImpl 让它帮我们实现 IService 的方法
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper,Employee> implements IEmployeeService {
​
}
​

注意:ServiceImpl 内部的实现和我们平时写的一样,如果我们自己也要实现一些方法可以在接口中定义,并在实现类中实现,就可以调用 BaseMapper() 方法,对比一下从 IoC 容器中获取的 Mapper 是一样的。

上一篇:Mybatis-Plus 逻辑删除 & 通用枚举


下一篇:redis 学习-hiredis库使用(一)