MyBatis-Plus详解

MyBatis-Plus

 
1.简介
  1.1 操作步骤
  1.2 mybatis-plus mapper编写规则

2.注解介绍
  2.1 常用注解
  2.2 mybatis-plus通用Mapper接口

3.条件构造器

4.高级查询
  4.1 列投影 select
  4.2 排序
  4.3 分组
  4.4 条件查询

5.mybatis-plus业务层定义
  5.1 实现分页逻辑


简介

mybatis-plus :为简化开发而生

MyBatis-plus 是在Mybatis的基础上进行二次开发的具有MyBatis所有功能, 也添加了不少好用的功能(再不改变原有框架基础上,进行简化),一句话就是牛逼,用就对了

优点:

  • 提供无sql 的crud操作
  • 内置代码生成器,分页插件, 性能分析插件等
  • 提供功能丰富的条件构造器快速进行无sql开发等等

读懂官方这张图就理解个大概了

在这里插入图片描述

首先mybatis-plus 会扫描domain包下的实体类,扫描后,通过反射内省得到该实体对象的类名,字段名,进而分析自己数据中的表名与字段进行对比,然后拼接 表名 与列名 ,sql随之出来,注入到mybatis容器中


操作步骤

  1. 建库建表
  2. 新建项目(改造Springboot项目)
  3. 导入对应依赖

    <!--改造成Springboot项目  添加springboot-parent-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version>
        <relativePath/>
    </parent>

    <dependencies>

        <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>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

4.改造完成后,新建domain包,mapper包

@Getter
@Setter
@ToString
public class Employee {
    @TableId(type = IdType.AUTO)  //id自动增长
    private Long id;
    private String name;
    private String password;
    private String email;
    private int age;
    @TableField("`admin`")
    private int admin;
    private Long deptId;
}

mybatis-plus 会自动的将dept_id 中的 _ 转换成java中的驼峰命名法 deptId

5.启动类贴上对应注解 ,然后就可以进行测试了


mybatis-plus mapper编写规则

  1. 自定义一个mapper接口
  2. 接口继承BaseMapper接口
  3. BashMapper接口明确指定要操作的实体对象泛型
public interface EmployeeMapper extends BaseMapper<Employee> {
}

编写对应测试类,注意:Springboot2.3版本后,测试用的是Junit5

以员工Mapper为例
问题1:EmployeeMapper接口并没有编写CRUD方法,为什么测试类中可以直接使用

EmployeeMapper接口继承BaseMapper接口,可以继承BaseMapper接口中所有crud方法	

问题2:项目中并没有编写EmployeeMapper.xml,也没有crud sql操作,为什么能进行crud操作

mybatis-plus框架帮忙写了 ,怎么写的?
    以查询所有数据为例子 :
             接口方法:employeeMapper.selectList()
             执行sql:select id,name,age from employee 
                 
    分析: sql语句中表名跟domain中类名一致
          sql语句中列名跟domain中字段名一致
        
    原理: mybatis-plus启动后悔解析BaseMapper接口汇总指定的泛型类型(实体),通过这个类型字节码对象
         反射内省得到该实体对象的类名,字段名,进而拼接 表名 与列名 ,sql随之出来


注解介绍


@TableField

@TableField(value="") //当前这个字段映射指定名字的列
@TableField(exist=false) //不将当前字段作为表的映射列

@TableName

当数据库表名与实体类中的名字不一样时,可以使用

@TableName("表名") //当前类名映射表名 这张表

@TableId

标记当前属性映射表主键	
value="" 映射表名
type=IdType.AUTO :指定id操作 (AUTO主键自动增长)
     IdType.NONE:	
     无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
@Verison
乐观锁注解、标记 @Verison 在字段上
作用:用于标记乐观锁操作字段 ---- 后续补充


mybatis-plus通用Mapper接口

insert

mybatis-plus 做添加操作时,如果属性值为空,不会将属性作为insert sql中的列

update

  • updateById(id):根据 ID 修改
  • update(entity, wrapper):根据 whereEntity 条件,更新记录实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
updateById与Update
1.传入的参数(实体)对象,如果属性值为空(null),那么该属性不参与sql 列拼接
2.如果传入参数(实体)对象属性为基本属性,它们有默认值,mybatis-plus认为是属性值不为null,这些属性会参与 sql 列拼接

如何不想让有默认值的基本属性参与 sql 列拼接?
  方案一:将基本属性字段设置包装类型
  方案二:使用BaseMapper中的update(null,wrapper)  [部分字段更新方式]
        实体对象设置为null ,wrapper设置更新条件
  UpdateWrapper<实体类>  wrapper= new UpdateWrapper<>(); 
   wrapper.eq()  :可以暂时理解为sql where拼接条件
   
   wrapper.set() :可以暂时理解为sql set拼接条件
     两个参数 : 正常set操作
     三个参数: 参数一:booean为true才拼接后面的set操作
     
   wrapper.setSql("age=18"); //不需要占位符,直接塞sql片段  

使用区别

updateById 适用范围:

  1. update的实体对象必须有id值
  2. 全字段(全量)更新

update适用范围:

  1. update数据筛选条件不仅仅是id
  2. 部分字段更新

delete

  • deleteById(id):根据 ID 删除
  • deleteBatchIds(idList):删除(根据ID 批量删除)
  • deleteByMap(map):根据 columnMap 条件,删除记录
  • delete(wrapper):根据 entity 条件,删除记录

select

  • selectById(id):根据 ID 查询
  • selectBatchIds(idList):查询(根据ID 批量查询)
  • selectByMap(map):查询(根据 columnMap 条件)
  • selectCount(wrapper):根据 Wrapper 条件,查询总记录数 返回值是 Integer
  • selectList(wrapper):根据 entity 条件,查询全部记录
  • selectMaps(wrapper):根据 Wrapper 条件,查询全部记录,使用场景 :没法使用对象封装数据的时候,可以使用map封装
QueryWrapper<Employee> wrapper =new QueryWrapper<>();
List<Map<String,Object>> mapList =employeeMapper.selectMaps(wrapper);
  • selectPage(page, wrapper):根据 entity 条件,查询全部记录(并翻页)分页操作
//分页拦截器
在启动类中加入拦截配置
//分页逻辑
QueryWrapper<Employee> wrapper =new QueryWrapper<>();
IPage :mybatis-plus分页对象等价之前的PageResult 或者PageInfo
Ipage<Employee> page =new Page (2,3)  //参数1:当前页,参数2:每页显示条数
employeeMapper.selectPage(page,wrapper);

启动类配置分页拦截

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


条件构造器

Wrapper:条件构造抽象类

在这里插入图片描述

叫做:sql片段更准确

推荐使用LambdaUpdateWrapper更新 LambdaQueryWrapper查询

LambdaUpdateWrapper :编译阶段避免了列名之类的出错

LambdaUpdateWrapper<Employee> wrapper =new LambdaUpdateWrapper<>();
Wrapper.eq(Employee::getId,1L);
Wrapper.eq(Employee::getAge,1L);
------------------
LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Employee::getName,"yue").eq(Employee::getAge,18);

例子

@SpringBootTest
public class MapperTest {
    @Autowired
    private EmployeeMapper employeeMapper;

    @Test  //添加
    public void testSave(){
        Employee employee = new Employee();
        employee.setAdmin(1);
        employee.setAge(18);
        employee.setDeptId(1L);
        employee.setEmail("xiaoluo@qq.com");
        employee.setName("小罗");
        employee.setPassword("111");
        employeeMapper.insert(employee);
    }
     //----------------------------------------------------------

    @Test  //更新
    public void testUpdate(){
        Employee employee = new Employee();
        employee.setId(22L);
        employee.setAdmin(1);
        employee.setAge(18);
        employee.setDeptId(1L);
        employee.setEmail("xuan@123.cn");
        employee.setName("xuan");
        employee.setPassword("111");
        employeeMapper.updateById(employee);
    }


    @Test
    //演示updateById ,不为null的数据丢失
    public void testUpdate2(){
        Employee employee = new Employee();
        employee.setId(22L);
       // employee.setAge(18);
        employee.setDeptId(2L);
        employee.setName("kent");
        employeeMapper.updateById(employee);
    }

    @Test  //部分更新
    public void testUpdate3(){
        UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
        wrapper.eq("id",22l);
        wrapper.set("dept_id",3L);
        wrapper.set("name","qqq");
        employeeMapper.update(null,wrapper);

        //lambda
        LambdaUpdateWrapper<Employee> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(Employee::getId,"22L");
        lambdaUpdateWrapper.set(Employee::getAge,18);
        lambdaUpdateWrapper.set(Employee::getDeptId,2L);
        lambdaUpdateWrapper.set(Employee::getName,"yue");
        employeeMapper.update(null,lambdaUpdateWrapper);
    }

//-------------------------------------------------------------------------


    @Test
    //删除单条
    public void testDelete(){
        employeeMapper.deleteById(23L);
    }

    @Test
    //批量删除  DELETE FROM employee WHERE id IN ( ? , ? , ? )
    public void testDelete2(){
        employeeMapper.deleteBatchIds(Arrays.asList(24L,25L,26L));
    }

    @Test
    //根据条件删除Map  DELETE FROM employee WHERE name = ? AND age = ?
    public void testDelete3(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","ff");
        map.put("age",18);
        employeeMapper.deleteByMap(map);
    }


    @Test
    //根据 entity 条件,删除记录  DELETE FROM employee WHERE (id = ?)
    public void testDelete4(){
        UpdateWrapper wrapper = new <Employee>UpdateWrapper();
        wrapper.eq("id",28L);
        employeeMapper.delete(wrapper);
    }


    //---------------------------------------------------------------
    //查询操作

    @Test  //根据id查询
    public void testGet(){
        System.out.println(employeeMapper.selectById(22L));
    }


    @Test  //查询多个员工
    public void testList2(){
        List<Employee> employees = employeeMapper.selectBatchIds(Arrays.asList(1L, 2L));
        employees.forEach(System.out::println);
    }


    //查询(根据 columnMap 条件)
    @Test
    public void testList3(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("id",20L);
        List<Employee> employees = employeeMapper.selectByMap(map);
        employees.forEach(System.out::println);
    }


    //根据 Wrapper 条件,查询总记录数
    @Test  //SELECT COUNT( 1 ) FROM employee
    public void testList4(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
            employeeMapper.selectCount(wrapper); //没有给条件相当于传null
    }


     //根据 entity 条件,查询全部记录
    //需求: 查询满足条件的所有的员工信息, 返回List<Employee>
     @Test
     public void testList5(){
         QueryWrapper<Employee> wrapper = new QueryWrapper<>();
         List<Employee> employees = employeeMapper.selectList(wrapper);
         employees.forEach(System.out::println);
     }


    @Test //查询全部员工
    public void testList(){
        System.out.println(employeeMapper.selectList(null));
    }

    //根据 Wrapper 条件,查询全部记录 Maps
    @Test
    public void testList6(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        wrapper.eq("name","yue");
        List<Map<String, Object>> mapList = employeeMapper.selectMaps(wrapper);
        mapList.forEach(System.out::println);
    }


    //根据 entity 条件,查询全部记录(并翻页) 分页查询
    @Test //查询全部员工
    public void testList7(){
        //1.分页拦截器,启动类中编写拦截配置

        //2.分页逻辑
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        IPage<Employee> page =new Page<>(2,3);
        employeeMapper.selectPage(page,wrapper);
        System.out.println("当前页:" + page.getCurrent());
        System.out.println("每页显示条数:" + page.getSize());
        System.out.println("总页数:" + page.getPages());
        System.out.println("总数:" + page.getTotal());
        System.out.println("当前页数据:" + page.getRecords());
    }


    //使用LambdaQueryWrapper查询
    //需求:查询name=yue, age=18的用户
    @Test //查询全部员工
    public void testList8(){
        LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Employee::getName,"yue").eq(Employee::getAge,18);
        employeeMapper.selectList(wrapper);
    }


//----------------------------------------------------------------------------------

    // UpdateWrapper  set  setSql使用
    @Test
    public void testSet(){
        UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
        wrapper.eq("id",20L);
        //wrapper.set("name","zhuzhu");
        wrapper.setSql("name='qqqq'");
        employeeMapper.update(null,wrapper);
    }

    //需求:将id=1的员工age改为18, 如果传入uname变量值不等于null或者“”,修改为员工name为uname变量值
    @Test
    public void testSet2(){
        String uname="hello";
        UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
        wrapper.eq("id",20L);
        if (StringUtils.hasText(uname)){
            wrapper.set("name",uname);
        }
        employeeMapper.update(null,wrapper);
    }

}


高级查询


列投影 select

// 列投影
    //需求:查询所有员工 ,返回员工name ,age列
    @Test
    public void testQuery(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        //wrapper.select("name","age");  //使用列投影
        wrapper.select("name , age");  //sql片段
        List<Employee> employees = employeeMapper.selectList(wrapper);
        employees.forEach(System.err::println);
    }


排序 orderByAsc/orderByDesc

//  排序
    //需求:查询所有员工age 正序, age相同 --id倒序排
    @Test
    public void testQuery2(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        wrapper.orderByAsc("age").orderByDesc("id");
        List<Employee> employees = employeeMapper.selectList(wrapper);
        employees.forEach(System.err::println);
    }


分组 groupBy / having

//  分组
    //需求:以部门id进行分组查询,查询每个部门员工个数
    @Test
    public void testQuery3(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        wrapper.groupBy("dept_id");
        wrapper.select("dept_id","count(id) count");
        List<Map<String, Object>> maps = employeeMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);
    }

    //需求:以部门id进行分组查询,查询每个部门员工个数 部门个数大于3的 having
    @Test
    public void testQuery4(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        wrapper.groupBy("dept_id");
        wrapper.select("dept_id","count(id) count");
        //wrapper.having("count>3");
        wrapper.having("count>{0}",3);
        List<Map<String, Object>> maps = employeeMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);
    }


条件查询

比较运算符 /逻辑
  • allEq /eq /ne
allEq : 全等匹配
allEq : 全等匹配(带条件过滤的)
eq:单个参数判断是否相等
ne: 不等于 
    
    //比较运算符  allEq  eq  ne
    @Test
    public void testQuery5(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        // WHERE (id = ? AND age = ?)
        wrapper.eq("id",21L).eq("age",18);
        List<Employee> employees = employeeMapper.selectList(wrapper);
        employees.forEach(System.err::println);
    }
    @Test
    public void testQuery6(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","yue");
        map.put("age",18);
        wrapper.allEq(map);
        List<Employee> employees = employeeMapper.selectList(wrapper);
        employees.forEach(System.err::println);
    }

    @Test  //查询姓名 不等于yue  ,age 不为18的值
    public void testQuery7(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        //WHERE (name <> ? AND age <> ?)
        wrapper.ne("name","yue").ne("age",18);
        List<Employee> employees = employeeMapper.selectList(wrapper);
        employees.forEach(System.err::println);
    }
  • gt / ge / lt /le /between / or
gt : 大于 >           lt:小于 <
ge:大于等于 >=       le:小于等于 <=
between : BETWEEN 值1 AND 值2
or : 拼接 OR    
    
     //   gt / ge / lt /le  /between / or
    @Test
    public void testQuery8(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        //WHERE (age > ? AND age < ?)
        wrapper.gt("age",18).lt("age",40);

        //WHERE (age >= ? AND age <= ?)
        wrapper.ge("age",18).le("age",40);

        //WHERE (age BETWEEN ? AND ?)
        wrapper.between("age",18,45);

        //WHERE (age > ? OR age < ?)
       //注意:多个条件默认是and需要or,调用or() 
        wrapper.gt("age",40).or().lt("age",18);
   

        List<Employee> employees = employeeMapper.selectList(wrapper);
        employees.forEach(System.err::println);
    }
模糊查询 /判空
  • isNull isNotNull ,in inSql ,like likeLeft
like: LIKE '%%
notLike : NOT LIKE '%值%'
likeLeft : LIKE '%值'
    
    //isNull isNotNull  ,in inSql ,like likeLeft
    @Test
    public void testQuery9(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();

            wrapper.isNull("age");  //where age is null
            wrapper.isNotNull("age"); //where age is not null

            wrapper.in("id",1L,20L,19L);//where in in(1,20,19)
            wrapper.inSql("id","1,2,3"); //sql片段

            wrapper.like("name","y");  //where name like '%y%'
            wrapper.likeLeft("name","罗");  //where name like '%罗'

        List<Employee> employees = employeeMapper.selectList(wrapper);
        employees.forEach(System.err::println);
    }
嵌套逻辑拼接
嵌套逻辑拼接
    wrapper.like("name","x").and(wr->wr.lt().or().gt())
//需求:查询所有员工 ,返回员工name ,age列
wrapper.select("name","age"); //列投影:列表查出指定列,其他列不查
wrapper.select("name, age"); //sql片段,把片段塞进sql查询语句


    //需求:查询所有员工age 正序, age相同 --id倒序排
wrapper.orderByAsc("age").orderByDesc("id")
//参数1:满足排序条件 参数2:排序策略(true:正序,false:倒序)     
wrapper.orderBy(true,true,"age") 
    
//需求:以部门id进行分组查询,查询每个部门员工个数
    employeeMapper.selectMaps()
    wrapper.select("dept_id","count(id) count")
    wrapper.group("dept_id");  
将大于3的部门筛选
    wrapper.having("count>3")
    或者 wrapper.having("count>{0}",3)
    
  
    比较运算符 
    //需求,查询name=xuan ,age=18
   1. wrapper.eq("name","xuan").eq("age",18) // AND操作
   2. map.put("name","xuan")
      map.put("age",18)
     wrapper.allEq(map)   //AND操作
    3.ne  //<>不等于操作
      wrapper.ne("age",18)
    
    4.between("age","12","13") age between 2 and 3
    
    5.wrapper.isNull("age")  //where age is null
    6.wrapper.isNotNull("age")  //where age is not null
    
    7.wrapper.in("id",1L,2L) //where in in(1,2)
      wrapper.inSql("id","1, 2"); //sql片段


模糊查询
    //查询name中含有x字样的员工 
    wrapper.like("name","x")   //where name like '%x%'
    wrapper.likeLeft("name","x")  //  like '%x'
    
逻辑运算    
    大于等于 ge()   小于等于 le()  多个条件默认是and需要or,调用or()  
    lt() .or(). gt()
    
嵌套逻辑拼接
    wrapper.like("name","x").and(wr->wr.lt().or().gt())

关联查询

mybatis-plus是没有办法做多表关联查询,即无法使用join,但是它支持额外sql

如果需要关联查询:如果采用额外sql方式,直接用mybatis-plus


mybatis-plus业务层

mybatis-plus 通用service层接口:

  1. 自定义一个接口:IEmployeeService
  2. 继承一个通用接口:IService
  3. 指定一泛型:要操作实体对象:Employee

实现类

EmployeeServiceImpl extends ServiceImpl<EmployeeMapper,Employee> implements IEmployeeService

mybatis-plus 通用service层实现类定义规则:

  1. 继承一个ServiceImpl类
  2. 指定泛型 操作实体对象对应mapper接口 ,操作实体对象
  3. 实现自定义service接口


实现分页逻辑

  • IEmployeeService接口
public interface IEmployeeService extends IService<Employee> {
    IPage<Employee> query(EmployeeQueryObject qo);
}
  • EmployeeServiceImpl实现类

    @Service
    public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements IEmployeeService {
    
        /*@Autowired
        private EmployeeMapper employeeMapper;*/
    
        @Override
        public IPage<Employee> query(EmployeeQueryObject qo) {
            QueryWrapper<Employee> wrapper = new QueryWrapper<>();
            IPage<Employee> page = new Page<>(qo.getCurrentPage(),qo.getPageSize());
             return super.page(page,wrapper);
            //return super.getBaseMapper().selectPage(page, queryWrapper);
            //return employeeMapper.selectPage(page,wrapper);
        }
    }
    

    测试类

    //service 分页操作
        @Test
        public void testMapper3(){
            EmployeeQueryObject qo = new EmployeeQueryObject();
            qo.setPageSize(3);
            qo.setCurrentPage(2);
            IPage<Employee> page = employeeService.query(qo);
            System.out.println("当前页:" + page.getCurrent());
            System.out.println("总页数:" + page.getPages());
            System.out.println("每页显示条数:" + page.getSize());
            System.out.println("总记录数:" + page.getTotal());
            System.out.println("当前页显示记录:" + page.getRecords());
            employeeService.query(qo);
        }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值