一篇文章带你掌握MyBatis简化框架——MyBatisPlus

一篇文章带你掌握MyBatis简化框架——MyBatisPlus

我们在前面的文章中已经学习了目前开发所需的主流框架

类似于我们所学习的SpringBoot框架用于简化Spring开发,我们的国人大大也开发了一款MyBatisPlus框架用来简化MyBatis开发

下面让我们来逐步掌握MyBatisPlus的内容吧~

MyBatisPlus简介

首先我们来简单介绍一下MyBatisPlus:

  • MyBatisPlus(MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发,提高效率

MyBatisPlus开发具有三种开发方式:

  • 基于MyBatis使用MyBatisPlus
  • 基于Spring使用MyBatisPlus
  • 基于SpringBoot使用MyBatisPlus

MyBatisPlus入门案例

我们以基于SpringBoot使用MyBatisPlus为案例来展示MyBatisPlus开发的便捷性

SpringBoot使用MyBatis

首先我们回忆一下SpringBoot使用MyBatis开发的相关步骤:

  1. 创建SpringBoot工程
  2. 勾选配置使用技术
  3. 设置DataSource相关属性(JDBC参数)
  4. 定义数据层接口映射配置

其中最为繁琐的就是数据层接口的配置,需要书写大量的@注解来进行数据库的查询

package com.itheima.dao;  import com.itheima.domain.Book; import org.apache.ibatis.annotations.*;  import java.util.List;  @Mapper public interface BookDao {      @Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})")     public int save(Book book);      @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}")     public int update(Book book);      @Delete("delete from tbl_book where id = #{id}")     public int delete(Integer id);      @Select("select * from tbl_book where id = #{id}")     public Book getById(Integer id);      @Select("select * from tbl_book")     public List<Book> getAll(); } 

SpringBoot使用MyBatisPlus

我们的SpringBoot使用MyBatisPlus大量简化了数据层代码书写

我们下面依次介绍整体步骤:

  1. 创建项目(SpringBoot项目,勾选相应技术栈)

一篇文章带你掌握MyBatis简化框架——MyBatisPlus

  1. 导入相关依赖坐标
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>     <parent>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-parent</artifactId>         <version>2.5.0</version>         <relativePath/> <!-- lookup parent from repository -->     </parent>     <groupId>com.itheima</groupId>     <artifactId>mybatisplus_01_quickstart</artifactId>     <version>0.0.1-SNAPSHOT</version>     <properties>         <java.version>1.8</java.version>     </properties>     <dependencies>          <!--由于SpringBoot中未整合MyBatisPlus,所以我们需要手动添加MyBatisPlus依赖坐标-->         <dependency>             <groupId>com.baomidou</groupId>             <artifactId>mybatis-plus-boot-starter</artifactId>             <version>3.4.1</version>         </dependency>          <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter</artifactId>         </dependency>          <!--德鲁伊坐标-->         <dependency>             <groupId>com.alibaba</groupId>             <artifactId>druid</artifactId>             <version>1.1.16</version>         </dependency>          <dependency>             <groupId>mysql</groupId>             <artifactId>mysql-connector-java</artifactId>             <scope>runtime</scope>         </dependency>          <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-test</artifactId>             <scope>test</scope>         </dependency>      </dependencies>      <build>         <plugins>             <plugin>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-maven-plugin</artifactId>             </plugin>         </plugins>     </build>  </project> 
  1. 配置环境yaml
spring:   datasource:     type: com.alibaba.druid.pool.DruidDataSource     driver-class-name: com.mysql.cj.jdbc.Driver     url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC     username: root     password: root 
  1. 书写数据库实体类
package com.itheima.domain;  import lombok.*;  @Data public class User {     private Long id;     private String name;     private String password;     private Integer age;     private String tel;      public Long getId() {         return id;     }      public void setId(Long id) {         this.id = id;     }      public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     }      public String getPassword() {         return password;     }      public void setPassword(String password) {         this.password = password;     }      public Integer getAge() {         return age;     }      public void setAge(Integer age) {         this.age = age;     }      public String getTel() {         return tel;     }      public void setTel(String tel) {         this.tel = tel;     }       @java.lang.Override     public java.lang.String toString() {         return "User{" +                 "id=" + id +                 ", name='" + name + ''' +                 ", password='" + password + ''' +                 ", age=" + age +                 ", tel='" + tel + ''' +                 '}';     } } 
  1. 数据层书写
package com.itheima.dao;  import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.itheima.domain.User; import org.apache.ibatis.annotations.Mapper;  // @Mapper为了使扫描到该数据层包(SpringBoot中提及) @Mapper // 注意:MyBatisPlus不需要书写方法,我们直接继承BaseMapper类,并表明我们所使用的实体类即可 public interface UserDao extends BaseMapper<User> {     // 不需要书写方法,在BaseMapper类中为我们配置了大量的数据库方法来查询,新增,修改,删除 } 
  1. 测试
package com.itheima;  import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.itheima.dao.UserDao; import com.itheima.domain.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List;  @SpringBootTest class Mybatisplus01QuickstartApplicationTests {      @Autowired     private UserDao userDao;      // 我们只需要得到UserDao类,并调用其方法即可(MyBatisPlus提供方法)     // 在这里我们仅调用selectById方法,当作测试     @Test     void testGetById(){         User user = userDao.selectById(2L);         System.out.println(user);     } } 

至此,我们的第一个MyBatisPlus案例就结束了

MyBatisPlus概述

在使用过MyBatisPlus后,我们就可以重新介绍一下MyBatisPlus了:

  • MyBatisPlus(MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发,提高效率
  • 官网:MyBatis-Plus (baomidou.com)

MyBatisPlus特征:

  • 无侵入:制作增强,不做改变,不会对现有工程产生影响
  • 强大的CRUD操作:内置通用的Mapper,少量配置即可实现单表CRUD操作
  • 支持Lambda:编写查询条件无需担心字段错误
  • 支持逐渐自动生成
  • 内置分页插件

最后提及一句:MyBatisPlus是由国人开发,官网也是以中文书写,具有中国风范~

标准数据层开发

我们在前面已经大致掌握了MyBatisPlus的具体操作流程,下面让我们更加细腻的分析数据层开发

Lombok依赖坐标

在正式开始讲解数据层开发前,为大家提供一个简单可靠的依赖:

  • Lombok依赖

那么这个坐标具有什么作用呢

  • Lombok依赖可以用来简化实体类的开发
  • Lombok,属于Java类库,提供了一组注解,简化POJO实体类开发

我们采用一个简单的案例来展示:

  1. 首先我们需要导入该坐标
        <dependency>             <groupId>org.projectlombok</groupId>             <artifactId>lombok</artifactId>             <version>1.18.12</version>             <scope>provided</scope>         </dependency> 
  1. 我们的实体类中产生多个新注解
package com.itheima.domain;  import lombok.*;  //lombok  @Data public class User {     private Long id;     private String name;     private String password;     private Integer age;     private String tel; } /* lombok为我们提供了多个注解:  @Setter:提供所有set方法 @Getter:提供所有Get方法 @ToString:提供ToString重构方法 @NoArgsConstructor:无参构造 @AllArgsConstructor:有参构造  其中我们最常用的注解是: @Data:包括了除构造函数外的所有方法(Set,Get,ToString,hashCode,equals) */ 

标准数据层开发(简单版)

首先我们来列出一些我们通常开发中会使用的数据层语句:

功能 MP接口
新增 int insert(T t)
删除 int deleteById(Serializable id)
修改 int updateById(T t)
根据id查询 T selectById(Serializable id)
查询全部 List selectList()

上述方法我们无需在数据层定义,直接测试即可:

package com.itheima;  import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.itheima.dao.UserDao; import com.itheima.domain.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List;  @SpringBootTest class Mybatisplus01QuickstartApplicationTests {      // 自动装配数据层     @Autowired     private UserDao userDao;      // 注意:id的数据类型为LONG,数值后需要加L          // 注意:下述部分方法需要返回id,实体类,null          // 新增     @Test     void testSave(){         User user = new User();         user.setName("黑马程序员");         user.setPassword("itheima");         user.setAge(12);         user.setTel("4006184000");         userDao.insert(user);     }      // 删除     @Test     void testDelete(){         userDao.deleteById(1L);     }      // 更新(注意:根据id进行更新,更新与原数据不同的数值,null值不进行更新)     @Test     void testUpdate(){         User user = new User();         user.setId(1L);         user.setName("Tom888");         user.setPassword("tom888");         userDao.updateById(user);     }      // 根据id查询     @Test     void testGetById(){         User user = userDao.selectById(2L);         System.out.println(user);     }  	// 查询全部     @Test     void testGetAll() {         List<User> userList = userDao.selectList(null);         System.out.println(userList);     }  } 

标准数据层开发(分页查询)

我们将分页查询单独列为一个小节进行讲解:

功能 MP接口
分页查询 IPage<T> selectPage(IPage<T> page)

MyBatisPlus的分页查询需要一些前置条件,我们下面一一讲述:

  1. 添加拦截器,进行分页操作:
// MyBatisPlus的分页操作需要添加拦截器  // 我们在Java文件夹下创建Config文件夹,创建MPConfig的Java类作为配置类  package com.itheima.config;  import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  // 作为配置类,让spring可以直接扫描 @Configuration public class MpConfig {          // 需要设置为Bean     @Bean     public MybatisPlusInterceptor mpInterceptor(){         //1.定义Mp拦截器MybatisPlusInterceptor(相当于外层拦截器)         MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();         //2.添加具体的拦截器PaginationInnerInterceptor(相当于在大拦截器中添加小拦截器)         mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());         return mpInterceptor;     } } 
  1. 直接测试即可
package com.itheima;  import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.itheima.dao.UserDao; import com.itheima.domain.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List;  @SpringBootTest class Mybatisplus01QuickstartApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testGetByPage(){        // selectPage需要两个参数IPage和Wrapper,IPage具有Page的实现类,Wrapper我们会在后续讲到,这里暂设为null                  //IPage对象封装了分页操作相关的数据,第一个参数为开始数,第二个数为本页可展示数据数         IPage page  = new Page(2,3);                  userDao.selectPage(page,null);                  // 下述为page本身携带的一些方法,用于查看相关数据         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());     }  } 
  1. 查看日志
# 如果我们想要查看实际开发的数据库语句,我们可以选择开启日志进行查看  # 查看日志方法设置在yaml配置文件中  # 开启mp的日志(输出到控制台) mybatis-plus:   configuration:     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 

DQL编程控制

我们在前面已经讲解了简单的数据层开发,下面我们将会进一步讲解DQL的编程内容

清除版本图

在正式开始前,我们讲一些简单轻松的知识点:

  • 我们在每次开启服务器后都会出现SpringBoot和MyBatisPlus的版本图,有时会影响我们代码的阅读

一篇文章带你掌握MyBatis简化框架——MyBatisPlus

那么我们该如何清除呢

  1. 设置logback.xml文件
<!--我们在sources中新创一个spring config 文件-->  <!--设置一个configuration双标签,不用填写内容即可--> <?xml version="1.0" encoding="UTF-8"?> <configuration> </configuration> 
  1. 在yaml配置文件中做清除工作
# 我们只需要将spring和MyBatisPlus的banner设置为false即可关闭版本图  # dataSource spring:   datasource:     type: com.alibaba.druid.pool.DruidDataSource     driver-class-name: com.mysql.cj.jdbc.Driver     url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC     username: root     password: root   main:     banner-mode: off # mp日志 mybatis-plus:   configuration:     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl   global-config:     banner: false 

基本条件查询

在介绍条件查询前,我们需要先来介绍Wrapper:

  • Wrapper属于一种接口参数
  • Wrapper是我们的条件查询中所携带的进行条件判断的参数
  • 接口方法的参数中,会出现各种 Wrapper,比如 queryWrapper、updateWrapper 等

接下来我们就来介绍三种基本条件查询:

  1. 按条件查询
package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testGetAll() {         //方式一:按条件查询                  // 我们的条件查询一般采用QueryWrapper类型的类来创造Wrapper条件判断参数         QueryWrapper qw = new QueryWrapper();         // 我们采用QueryWrapper的一些方法来进行给予判断条件,后续我们会进行介绍         // 这里采用lt小于方法,后面跟String类型字符串代表数据库列名,在跟数值表示值         qw.lt("age",18);         // 我们采用userDao的selectList方法根据qw条件判断机制来进行获取数据         // 获取了User数值的List,并打印即可         List<User> userList = userDao.selectList(qw);         System.out.println(userList);      }  } 
  1. lambda格式按条件查询(方法版)
package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testGetAll() {          //方式二:lambda格式按条件查询                  QueryWrapper<User> qw = new QueryWrapper<User>();         // MyBatisPlus可支持Lambda表达式,我们使用lambda方法使后续操作均变为Lambda表达式形式         // 我们的String数据库列名可以采用Lambda表达式形式书写         qw.lambda().lt(User::getAge, 10);         List<User> userList = userDao.selectList(qw);         System.out.println(userList);      }  } 
  1. lambda格式按条件查询(继承类版)
package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testGetAll() {                  //方式三:lambda格式按条件查询                  // 这里直接继承LambdaQueryWrapper,后续操作可以直接采用Lambda表达式,不用携带方法lambda         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         lqw.lt(User::getAge, 10);         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);      }  } 

除了上面三种基本条件查询外,我们可以发现条件查询是可以叠加使用的,主要分为两种叠加方式:

  1. 正常叠加
package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testGetAll() {          // 10到30岁之间         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         // 依次分开书写         lqw.lt(User::getAge, 30);         lqw.gt(User::getAge, 10);         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);      }  } 
  1. 链式叠加
package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testGetAll() {          // 10到30岁之间         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         // 链式叠加书写         lqw.lt(User::getAge, 30).gt(User::getAge, 10);         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);      }  } 

最后我们还要介绍两种组合查询条件形式:

  1. 并且(and)
package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testGetAll() {          //并且关系         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         //并且关系:10到30岁之间(直接链式书写即可)         lqw.lt(User::getAge, 30).gt(User::getAge, 10);         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);      }  } 
  1. 或者(or)
package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testGetAll() {          //或者关系         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         //或者关系:小于10岁或者大于30岁(在条件之间采用or方法来隔离)         lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);      }  } 

空值处理

我们在做实际项目处理时,会出现各种查询框架

例如价格查询搜索产品,但我们可能不会同时给出最低价限制和最高价限制

我们以代码来做一个简单示例:

package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;          @Test     void testGetAll() { 		// 模拟页面传递过来的查询数据         UserQuery uq = new UserQuery();         // 如果我们下面的数据有一个没有设置,就会导致查询语句中的?没有填充,导致搜索失败         uq.setAge(10);         uq.setAge2(null);          // null判定         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         lqw.lt(User::getAge, uq.getAge2());         lqw.gt(User::getAge, uq.getAge());         List<User> userList = userDao.selectList(lqw);         System.out.println(userList)      }  } 

在之前我们的null值处理大部分都是采用if语句来进行判断:

package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;          @Test     void testGetAll() { 		// 模拟页面传递过来的查询数据         UserQuery uq = new UserQuery(); 		// 可能出现有任意一个数没有赋值         uq.setAge(10);         uq.setAge2(null);          // null判定         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         lqw.lt(User::getAge, uq.getAge2());                  // 正常我们采用if来进行判断是否有值,若有值则加入操作         // 但当if语句过多,导致代码冗杂         if( null != uq.getAge()) {             lqw.gt(User::getAge, uq.getAge());         }         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);      }  } 

MyBatisPlus给出了一种新的判定方法来决定是否加载该语句:

package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;          @Test     void testGetAll() { 		// 模拟页面传递过来的查询数据         UserQuery uq = new UserQuery();         uq.setAge(10);         uq.setAge2(30);  		// LambdaQueryWrapper的各种方法中均携带了一个判定条件在最前面的参数中,当成立执行后续操作,不成立直接跳过         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         // 先判定第一个参数是否为true,如果为true连接当前条件         lqw.lt(null != uq.getAge2(),User::getAge, uq.getAge2());         lqw.gt(null != uq.getAge(),User::getAge, uq.getAge());         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);      }  } 

查询投影

我们选择性的查询数据库列称为查询投影,接下来让我们来介绍查询投影的实现方法

查询投影的实现我们大致分为两种类型:

  1. 查询结果包含模型类中部分属性
package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;          @Test     void testGetAll() {         // 查询投影          	// 当我们所查询的内容属于实体类中包含的属性,我们可以采用QW或LambdaQW来实现     	// 我们大部分采用LambdaQW来实现,因为带有自动识别,不易出错         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();     	// 我们采用select方法来添加查询种类,LambdaQueryWrapper采用Lambda表达式添加         lqw.select(User::getId,User::getName,User::getAge);         QueryWrapper<User> lqw = new QueryWrapper<User>();         // 我们采用select方法来添加查询种类,QueryWrapper采用String添加         lqw.select("id","name","age","tel");         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);      }  } 
  1. 查询结果包含模型类中未定义属性
package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;          @Test     void testGetAll() {         // 查询投影              // 未定义的属性我们只能采用QueryWrapper的String字符串来书写,例如count(*)和分组条件         QueryWrapper<User> lqw = new QueryWrapper<User>();         lqw.select("count(*) as count, tel");         // QueryWrapper提供了分组方法groupBy,参数为String类型的数据库列名         lqw.groupBy("tel");         List<Map<String, Object>> userList = userDao.selectMaps(lqw);         System.out.println(userList);      }  } 

查询条件展示

首先我们给出所有查询条件官网链接:条件构造器 | MyBatis-Plus (baomidou.com)

一篇文章带你掌握MyBatis简化框架——MyBatisPlus

如果有需要可以上网查询相关构造方法

下面我们仅对一些常用查询条件进行展示:

package com.itheima;  import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.itheima.dao.UserDao; import com.itheima.domain.User; import com.itheima.domain.query.UserQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.List; import java.util.Map;  @SpringBootTest class Mybatisplus02DqlApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testGetAll() {          //条件查询         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         // eq等同于=         lqw.eq(User::getName,"Jerry").eq(User::getPassword,"jerry");         User loginUser = userDao.selectOne(lqw);         System.out.println(loginUser);          LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         //范围查询 lt le gt ge eq between         lqw.between(User::getAge,10,30);         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);          LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         //模糊匹配 like         lqw.likeLeft(User::getName,"J");         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);  		// 查询全部         LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();         List<User> userList = userDao.selectList(lqw);         System.out.println(userList);      }  } 

映射处理

最后介绍的依旧是我们在MyBatis中也出现的老问题:

  • 当我们的实现类和数据库表出现不同点时我们该如何处理

我们在下面分为几种情况来讲解:

  1. 表名与编码开发设计不同步
// 假设我们的数据库表名为tbl_user,但我们的实体类设计为User  package com.itheima.domain;  import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;  // 采用注解@TableName,后面跟对应的数据库表名 @TableName("tbl_user") public class User {     private Long id;     private String name;     private String password;     private Integer age;     private String tel; } 
  1. 列名与编码开发设计不同步
// 假设我们的数据库密码设计为pwd,但是我们的实体类密码属性设计为password  package com.itheima.domain;  import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;  //lombok @Data @TableName("tbl_user") public class User {     private Long id;     private String name;          // 采用@TableField注解,后面采用属性value=数据库列名     @TableField(value = "pwd")     private String password;          private Integer age;     private String tel; } 
  1. 该列不应当被打印时
// 例如我们的pwd密码,在查询时不应当被查询,但是我们采用select * from tbl_user来查询,如何屏蔽  package com.itheima.domain;  import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;  //lombok @Data @TableName("tbl_user") public class User {     private Long id;     private String name;          // 采用@TableField注解,后面采用属性select设置为false,即为不可查询     @TableField(value = "pwd",select = false)     private String password;          private Integer age;     private String tel; } 
  1. 当实体类中出现数据库中不存在的列时
// 例如我们设计了一个属性online判断是否在线,不用放于数据库中,我们该如何在select * 中去除该属性  package com.itheima.domain;  import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;  //lombok @Data @TableName("tbl_user") public class User {     private Long id;     private String name;     @TableField(value = "pwd",select = false)     private String password;     private Integer age;     private String tel;          // 采用@TableField注解,后面跟属性exist设置为false,则判断该属性不存在于数据库中     @TableField(exist = false)     private Integer online; } 

我们将上述内容分离出来主要解释两个注解:

  1. @TableName
  • 名称:@TableName
  • 类型:类注解
  • 位置:模型类定义上方
  • 作用:设置当前类对应的数据库表关系
  • 相关属性:value设置数据库表名称
  1. @TableField
  • 名称:@TableField
  • 类型:属性注解
  • 位置:模型类属性定义上方
  • 作用:设置当前属性众多关系
  • 相关属性:value设置数据库字段名称,exist设置属性在数据库字段中是否存在,select设置属性是否参与查询

DML编程控制

这一章节我们来讲一些MyBatisPlus中经常用到的操作,下面我们一一介绍

ID生成策略控制

我们在实际开发中会有需要ID生成需求,对于不同的情况需要采取不同的ID生成方法:

  • 员工编号:递增ID生成
  • 快递单号:地区ID生成
  • 网络编号:随机ID生成

因此MyBatisPlus提供了一种新的注解来实现这类需求:

  • 名称:@TableId
  • 类型:属性注解
  • 位置:模型类中用于表示主键的属性定义上方
  • 作用:设置当前类中主键属性的生成策略
  • 相关属性:value设置数据库主键名称,type设置主键生成策略具体参考IdType枚举值

ID生成策略枚举值:

  • AUTO(0):使用数据库id自增策略控制id生成
  • NONE(1):不设置ID生成策略
  • INPUT(2):用户手工输入ID
  • ASSIGN_ID(3):雪花算法生成ID(可兼容数值型与字符串型)
  • ASSIGN_UUID(4):以UUID生成算法作为ID生成策略

我们给出示例演示:

package com.itheima.domain;  import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.Version; import lombok.Data;  @Data //设置表名映射关系 @TableName("tbl_user") public class User {     //设置主键生成策略(这里设置为自增)     @TableId(type = IdType.AUTO)     private Long id;     private String name;     @TableField(value = "pwd",select = false)     private String password;     private Integer age;     private String tel;     @TableField(exist = false)     private Integer online;     private Integer deleted;     private Integer version; } 

我们也可以同一设置@TableId的type属性,使整个项目的@TableId都以一种形态执行:

# dataSource spring:   datasource:     type: com.alibaba.druid.pool.DruidDataSource     driver-class-name: com.mysql.cj.jdbc.Driver     url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC     username: root     password: root   main:     banner-mode: off # mp日志 mybatis-plus:   configuration:     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl   global-config:     banner: false     db-config:       id-type: assign_id # 设置为雪花算法设置ID 

多记录操作

我们在实际开发中常常会进行多条记录操作:

  • 根据主键删除多条操作
  • 根据主键查询多条操作

MyBatisPlus也为我们提供了相对应的方法:

package com.itheima;  import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.itheima.dao.UserDao; import com.itheima.domain.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.ArrayList; import java.util.List;  @SpringBootTest class Mybatisplus03DqlApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testDelete(){         //删除指定多条数据         //deleteBatchIds参数为数组形式,我们提供id的数组即可         List<Long> list = new ArrayList<>();         list.add(1402551342481838081L);         list.add(1402553134049501186L);         list.add(1402553619611430913L);         userDao.deleteBatchIds(list);     }          @Test     void testSelect()}{         //查询指定多条数据     	//selectBatchIds参数为数组形式,我们提供id的数组即可         List<Long> list = new ArrayList<>();         list.add(1L);         list.add(3L);         list.add(4L);         userDao.selectBatchIds(list); 	}  } 

逻辑删除

我们在实际开发中面对项目的删除操作有时并非是真正的删除操作:

  • 例如对于一些数据的存储,我们需要在年终总结中计算总产量,所以所有利益收入不能删除
  • 但对于一些离职的员工,关于他们的数据需要暂时性删除,因而产生矛盾

因而我们提出了逻辑删除的概念:

  • 我们在总列表中添加一个属性deleted来设置是否被删除
  • 当deleted为1时,当作数据删除;当deleted为1时,当作数据存在
  • 我们在实际开发中以一种虚拟的删除思想(逻辑删除)来代替真正的数据删除

下面我们来讲解如何在MyBatisPlus中实现这种思想:

  1. 数据库中添加deleted列
ALTER TABLE tb_user ADD deleted int(1) DEFAULT 0; 
  1. 实体库中添加deleted属性,并用注解注明为逻辑删除属性
package com.itheima.domain;  import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.Version; import lombok.Data;  @Data //设置表名映射关系 @TableName("tbl_user") public class User {     //设置主键生成策略     @TableId(type = IdType.ASSIGN_ID)     private Long id;     private String name;     @TableField(value = "pwd",select = false)     private String password;     private Integer age;     private String tel;     @TableField(exist = false)     private Integer online;     //逻辑删除字段,标记当前记录是否被删除(value原值,delval修改值)     @TableLogic(value = "0" ,delval = "1")     private Integer deleted; }  /* @TableLogic 在设置之后,我们采用delete方法时,不会直接删除数据,而是将该数据值从value变为delval 类似于:UPDATE tbl_user SET deleted = 1 WHERE id = ? AND deleted = 0; */ 

同样,我们的逻辑删除值也可以进行统一设置:

# dataSource spring:   datasource:     type: com.alibaba.druid.pool.DruidDataSource     driver-class-name: com.mysql.cj.jdbc.Driver     url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC     username: root     password: root   main:     banner-mode: off # mp日志 mybatis-plus:   configuration:     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl   global-config:     banner: false     db-config:       id-type: assign_id       table-prefix: tbl_       # 逻辑删除字段名       logic-delete-field: deleted       # 逻辑删除字面值:未删除为0       logic-not-delete-value: 0       # 逻辑删除字面值:删除为1       logic-delete-value: 1 

乐观锁

我们在业务开发中也会遇到多线程的问题:

  • 例如我们的秒杀活动,在之前多线程中也有提及过的问题
  • 如果我们不希望我们的剩余商品数变为负数,那么就需要设置Lock来解决这个问题

首先我们来讲解一下乐观锁的基本概念:

  • 乐观锁首先读取当前该商品的状态
  • 然后利用当前商品的状态为条件来修改商品状态,并且将该商品状态进行修改
  • 如果用户A和用户B同时读取商品状态,当用户A的WHERE条件成立后修改商品,则用户B的WHERE条件就不再成立无法修改

在MyBatisPlus中我们采用乐观锁的概念来解决:

  1. 在数据库中新添version属性
ALTER TABLE tb_user ADD version int(11) DEFAULT 1; 
  1. 实体类中新添version属性并加上注解
package com.itheima.domain;  import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.Version; import lombok.Data;  @Data //设置表名映射关系 @TableName("tbl_user") public class User {     @TableId(type = IdType.ASSIGN_ID)     private Long id;     private String name;     @TableField(value = "pwd",select = false)     private String password;     private Integer age;     private String tel;     @TableField(exist = false)     private Integer online;     @TableLogic(value = "0" ,delval = "1")     private Integer deleted;          // version版本注解(乐观锁)     @Version     private Integer version; } 
  1. 使用拦截器
/* 我们来讲解一下为什么需要使用拦截器 我们将前面我们介绍的乐观锁概念转化为语句:  SELETE version FROM tbl_user  UPDATE tbl_user SET ...(用户修改) version=version+1 WHERE id = ? AND version = version(我们之前读取的version)  倘若用户操作前有其他用户操作,那么version就会发生变化,导致用户无法找到对应的数据,无法操作  因为我们需要对前面的version进行修改,我们需要将语句拦截下来进行一定修改,所以这里采用拦截器 */  package com.itheima.config;  import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  @Configuration public class MpConfig {     @Bean     public MybatisPlusInterceptor mpInterceptor() {         //1.定义Mp拦截器         MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();         //2.添加具体的拦截器         mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());         //3.添加乐观锁拦截器         mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());         return mpInterceptor;     } } 
  1. 测试
package com.itheima;  import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.itheima.dao.UserDao; import com.itheima.domain.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;  import java.util.ArrayList; import java.util.List;  @SpringBootTest class Mybatisplus03DqlApplicationTests {      @Autowired     private UserDao userDao;      @Test     void testUpdate(){                  // 我们设置一个实例User,id为1,version为1         User user = new User();         user.setId(1L);         user.setName("Jock666");         user.setVersion(1);         userDao.updateById(user);          // 我们用user,user2来当作两个用户                  // 假设两个用户同时读取数据         User user = userDao.selectById(3L);     //version=1          User user2 = userDao.selectById(3L);    //version=1          // user用户首先进行操作(这时 实例version为1 操作成立 ,操作结束后version变为2)         // UPDATE tbl_user SET ...(用户修改) version=2 WHERE id = 1 AND version = 1         user.setName("Jock aaa");         userDao.updateById(user2);                		// user2用户开始操作(这时 实例version为2 但前面读取的version为1,读取不到数据,无法操作)         // UPDATE tbl_user SET ...(用户修改) version=2 WHERE id = 1 AND version = 1(已读取不到数据)         user2.setName("Jock bbb");         userDao.updateById(user);                     }  } 

结束语

好的,关于MyBatisPlus的内容就介绍到这里,希望能为你带来帮助!

附录

该文章属于学习内容,具体参考B站黑马程序员李老师的SSM框架课程

这里附上链接:MyBatisPlus-01-MybatisPlus入门案例_哔哩哔哩_bilibili

发表评论

评论已关闭。

相关文章