第一章:了解Mybatis Plus(摘自官网)
1.1 Mybatis Plus介绍
- Mybatis Plus(简称MP)是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发、提高效率而生。
- 官网。
愿景: 我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
1.2 代码以及文档
1.3 特性
无侵入
:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑损耗小
:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作强大的CRUD操作
:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求支持 Lambda形式调用
:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错支持主键自动生成
:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题支持 ActiveRecord模式
:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作支持自定义全局通用操作
:支持全局通用方法注入( Write once, use anywhere )内置代码生成器
:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用内置分页插件
:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询分页插件支持多种数据库
:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库内置性能分析插件
:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询内置全局拦截插件
:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
1.4 架构
第二章:整合Mybatis Plus
2.1 准备工作
- MySQL 5.7版本以上。
- JDK 11。
- IDEA 2021+。
- Sql脚本:
-- 创建数据库
CREATE DATABASE IF NOT EXISTS mp
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_general_ci;
-- 创建测试表
CREATE TABLE `tb_user` (
`id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_name` VARCHAR ( 20 ) NOT NULL COMMENT '用户名',
`password` VARCHAR ( 20 ) NOT NULL COMMENT '密码',
`name` VARCHAR ( 30 ) DEFAULT NULL COMMENT '姓名',
`age` INT ( 11 ) DEFAULT NULL COMMENT '年龄',
`email` VARCHAR ( 50 ) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY ( `id` )
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4;
-- 插入测试数据
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES ('1', 'zhangsan', '123456', '张三', '18', 'test1@qq.com');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES ('2', 'lisi', '123456', '李四', '20', 'test2@qq.com');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES ('3', 'wangwu', '123456', '王五', '28', 'test3@qq.com');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES ('4', 'zhaoliu', '123456', '赵六', '21', 'test4@qq.com');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES ('5', 'sunqi', '123456', '孙七', '24', 'test5@qq.com');
2.2 SpringBoot整合Mybatis Plus
2.2.1 导入相关依赖
- pom.xml
<?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.1</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>mp</artifactId>
<version>0.0.1</version>
<name>mp</name>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.2.2 编写application.yml
- application.yml
# 服务器配置
server:
# 端口
port: 8080
servlet:
# 项目名称
context-path: /mp
# tomcat的配置
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# tomcat最大线程数,默认为200
max-threads: 800
# Tomcat启动初始化的线程数,默认值25
min-spare-threads: 30
max-http-header-size: 65536
spring:
# 配置数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
password: 123456
# Hikari 连接池配置
hikari:
# 最小空闲连接数量
minimum-idle: 5
# 空闲连接存活最大时间,默认600000(10分钟)
idle-timeout: 180000
# 连接池最大连接数,默认是10
maximum-pool-size: 1000
# 此属性控制从池返回的连接的默认自动提交行为,默认值:true
auto-commit: true
# 连接池名称
pool-name: MPHikariCP
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
max-lifetime: 1800000
# 数据库连接超时时间,默认30秒,即30000
connection-timeout: 30000
connection-test-query: SELECT 1
data-source-properties:
useInformationSchema: true
# 开启日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2.2.3 编写POJO
- User.java
package com.example.mp.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-17 21:01
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User implements Serializable {
/**
* 主键
*/
private Long id;
/**
* 用户名
*/
@TableField("user_name")
private String username;
/**
* 密码
*/
private String password;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 电子邮件
*/
private String email;
}
2.2.4 编写Mapper
- UserMapper.java
package com.example.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mp.domain.User;
import org.apache.ibatis.annotations.Mapper;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-17 21:10
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
2.2.5 编写启动类
- MpApplication.java
package com.example.mp;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.mp.mapper") //设置mapper接口的扫描包
public class MpApplication {
public static void main(String[] args) {
SpringApplication.run(MpApplication.class, args);
}
}
2.2.6 编写测试用例
- MpApplicationTests.java
package com.example.mp;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
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 MpApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect() {
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
}
第三章:通用CRUD
3.1 概述
- 通过上面的学习,我们知道通过继承BaseMapper可以实现各种各样的单表操作,下面将详细讲解这些操作。
3.2 插入操作
3.2.1 方法定义
- 方法:
/**
* 插入一条记录
*
* @param entity 实体对象
*/
int insert(T entity);
3.2.2 测试用例
- 示例:
package com.example.mp;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class MpApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void testInsert(){
User user = new User();
user.setUsername("zhangsan");
user.setPassword("123456");
user.setName("张三");
user.setAge(18);
user.setEmail("123456@qq.com");
int count = userMapper.insert(user);
System.out.println("count = " + count); //数据库受影响的行数
System.out.println("user.getId() = " + user.getId()); //自增后的id会回填到对象中
}
}
3.2.3 测试
- 结果:
- 数据库记录:
- 可以看到,数据已经写入到数据库中了,但是,id的值是不正确的,我们期望的是数据库自增长,实际是MP生成了id的值写入到数据库中。
- MP支持的id策略:
package com.baomidou.mybatisplus.annotation;
import lombok.Getter;
/**
* 生成ID类型枚举类
*
* @author hubin
* @since 2015-11-10
*/
@Getter
public enum IdType {
/**
* 数据库ID自增
* <p>该类型请确保数据库设置了 ID自增 否则无效</p>
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* <p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 分配ID (主键类型为number或string),
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4);
private final int key;
IdType(int key) {
this.key = key;
}
}
- 修改User对象:
package com.example.mp.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-17 21:01
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User implements Serializable {
/**
* 主键
*/
@TableId(type= IdType.AUTO)
private Long id;
/**
* 用户名
*/
@TableField("user_name")
private String username;
/**
* 密码
*/
private String password;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 电子邮件
*/
private String email;
}
- 数据插入成功:
3.2.4 @TableField注解
- 在MP中通过@TableField注解可以指定字段的一些属性,常常解决的问题有2个:
- ①对象中的属性名和字段名不一致的问题(非驼峰)。
- ②对象中的属性字段在表中不存在的问题。
- 示例:
package com.example.mp.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-17 21:01
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User implements Serializable {
@TableId(type= IdType.AUTO)
private Long id;
@TableField("user_name") //解决字段名不一致
private String username;
@TableField(select = false) //该字段不加入到查询中
private String password;
private String name;
private Integer age;
private String email;
@TableField(exist = false)
private String address; //该字段在数据库表中不存在
}
3.3 更新操作
3.3.1 概述
- 在MP中,更新操作有2种,一种是根据id更新,另一种是根据条件更新。
3.3.2 根据id更新
- 方法定义:
/**
* 根据 ID 修改
*
* @param entity 实体对象(对象属性可以为null)
*/
int updateById(@Param(Constants.ENTITY) T entity);
- 测试:
package com.example.mp;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 19:22
*/
@SpringBootTest
public class TestUpdate {
@Autowired
private UserMapper userMapper;
@Test
public void testUpdateById() {
User user = new User();
user.setId(1L);
user.setUsername("许大仙");
//根据id更新,更新不为null的字段,返回更新的条数
int result = userMapper.updateById(user);
System.out.println("result = " + result);
}
}
- 结果:
- 数据库记录:
3.3.3 根据条件更新
- 方法定义:
/**
* 根据 whereEntity 条件,更新记录
*
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
- 测试:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 19:22
*/
@SpringBootTest
public class TestUpdate {
@Autowired
private UserMapper userMapper;
@Test
public void testUpdate() {
//封装更新字段的对象
User user = new User();
user.setAge(18);
//封装更新条件的对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_name", "zhangsan");
int result = userMapper.update(user, queryWrapper);
System.out.println("result = " + result);
}
@Test
public void testUpdate2() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("age", 18)
.set("password", "123456").eq("user_name", "zhangsan");
int result = userMapper.update(null, updateWrapper);
System.out.println("result = " + result);
}
}
- 结果:
- 数据库记录:
3.4 删除操作
3.4.1 deleteById
- 方法定义:
/**
* 根据 ID 删除
*
* @param id 主键ID
*/
int deleteById(Serializable id);
- 测试:
package com.example.mp;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 20:04
*/
@SpringBootTest
public class TestDeleteById {
@Autowired
private UserMapper userMapper;
@Test
public void test(){
//执行删除操作
int result = userMapper.deleteById(6);
System.out.println("result = " + result);
}
}
- 结果:
3.4.2 deleteByMap
- 方法定义:
/**
* 根据 columnMap 条件,删除记录
*
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
- 测试:
package com.example.mp;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 20:10
*/
@SpringBootTest
public class TestDeleteByMap {
@Autowired
private UserMapper userMapper;
@Test
public void test(){
Map<String,Object> map = new HashMap<>();
map.put("id", "5");
map.put("age", "24");
//将map中的元素设置为删除的条件,多个条件之间是and的关系
int result = userMapper.deleteByMap(map);
System.out.println("result = " + result);
}
}
- 结果:
3.4.3 delete
- 方法定义:
/**
* 根据 entity 条件,删除记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
- 测试:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 20:31
*/
@SpringBootTest
public class TestDelete {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 20);
userMapper.delete(queryWrapper);
}
}
- 结果:
3.4.4 deleteBatchIds
- 方法定义:
/**
* 删除(根据ID 批量删除)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
- 测试:
package com.example.mp;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 20:39
*/
@SpringBootTest
public class TestDeleteBatchIds {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
//根据id集合批量删除
int result = userMapper.deleteBatchIds(List.of(1, 2, 3));
System.out.println("result = " + result);
}
}
- 结果:
3.5 查询操作
3.5.1 概述
- MP提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分页查询等操作。
3.5.2 selectById
- 方法定义:
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
T selectById(Serializable id);
- 示例:
package com.example.mp;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 21:00
*/
@SpringBootTest
public class TestSelectById {
@Autowired
private UserMapper userMapper;
@Test
public void test(){
//根据id查询数据
User user = userMapper.selectById(1);
System.out.println("user = " + user);
}
}
- 结果:
3.5.3 selectBatchIds
- 方法定义:
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
- 测试:
package com.example.mp;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 22:01
*/
@SpringBootTest
public class TestSelectBatchIds {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
//根据id集合批量查询
List<User> userList = userMapper.selectBatchIds(List.of(1, 2, 3));
System.out.println("userList = " + userList);
}
}
- 结果:
3.5.4 selectOne
- 方法定义:
/**
* 根据 entity 条件,查询一条记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
- 测试:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 22:08
*/
@SpringBootTest
public class TestSelectOne {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getUsername, "zhangsan");
//根据条件查询一条数据,如果结果超过一条则报错
User user = userMapper.selectOne(lambdaQueryWrapper);
System.out.println("user = " + user);
}
}
- 结果:
3.5.5 selectList
- 方法定义:
/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
- 测试:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 22:14
*/
@SpringBootTest
public class TestSelectList {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.ge(User::getId, 3);
//根据条件查询数据
List<User> userList = userMapper.selectList(lambdaQueryWrapper);
System.out.println("userList = " + userList);
}
}
- 结果:
3.5.6 selectCount
- 方法定义:
/**
* 根据 Wrapper 条件,查询总记录数
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
- 测试:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 22:21
*/
@SpringBootTest
public class TestSelectCount {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
//根据条件查询数据条数
Integer count = userMapper.selectCount(new LambdaQueryWrapper<User>().ge(User::getId, 2));
System.out.println("count = " + count);
}
}
- 结果:
3.5.7 selectPage
- 方法定义:
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
- 配置分页插件:
package com.example.mp.config;
import com.baomidou.mybatisplus.annotation.DbType;
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;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 22:42
*/
@Configuration
public class MybatisPlusConfig {
// 配置分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
- 测试:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-18 22:36
*/
@SpringBootTest
public class TestSelectPage {
@Autowired
private UserMapper userMapper;
@Test
public void test(){
Page<User> page = new Page<>(1,5);
Page<User> selectPage = userMapper.selectPage(page, new QueryWrapper<>());
long total = selectPage.getTotal();
long pages = selectPage.getPages();
System.out.println("数据总条数 = " + total);
System.out.println("总页数 = " + pages);
List<User> userList = selectPage.getRecords();
System.out.println("userList = " + userList);
}
}
- 结果:
3.6 SQL注入原理
- 我们知道,MP在启动后会将
BaseMapper
中的一系列的方法注册到MappedStatement
中,那么是如何注入的?流程是什么? - 在MP中,
ISqlInjector
负责SQL的注入工作,它是一个接口,AbstractSqlInjector
是它的实现类,实现关系如下:
- 在
AbstractSqlInjector
中,主要是由inspectInject
方法进行注入的,源码如下:
package com.baomidou.mybatisplus.core.injector;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import java.util.List;
import java.util.Set;
/**
* SQL 自动注入器
*
* @author hubin
* @since 2018-04-07
*/
public abstract class AbstractSqlInjector implements ISqlInjector {
private static final Log logger = LogFactory.getLog(AbstractSqlInjector.class);
@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);
if (modelClass != null) {
String className = mapperClass.toString();
Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
List<AbstractMethod> methodList = this.getMethodList(mapperClass);
if (CollectionUtils.isNotEmpty(methodList)) {
TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
// 循环注入自定义方法
methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
} else {
logger.debug(mapperClass.toString() + ", No effective injection method was found.");
}
mapperRegistryCache.add(className);
}
}
}
/**
* <p>
* 获取 注入的方法
* </p>
*
* @param mapperClass 当前mapper
* @return 注入的方法集合
* @since 3.1.2 add mapperClass
*/
public abstract List<AbstractMethod> getMethodList(Class<?> mapperClass);
}
- 在实现方法中,
methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
是关键,循环遍历方法,进行注入。 - 查看
m.inject
方法,可以看到如下的代码:
public abstract class AbstractMethod implements Constants {
protected static final Log logger = LogFactory.getLog(AbstractMethod.class);
protected Configuration configuration;
protected LanguageDriver languageDriver;
protected MapperBuilderAssistant builderAssistant;
/**
* 注入自定义方法
*/
public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
this.configuration = builderAssistant.getConfiguration();
this.builderAssistant = builderAssistant;
this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
/* 注入自定义方法 */
injectMappedStatement(mapperClass, modelClass, tableInfo);
}
....
}
- 由此可知,最终是通过调用
injectMappedStatement
进行真正的注入的。
/**
* 注入自定义 MappedStatement
*
* @param mapperClass mapper 接口
* @param modelClass mapper 泛型
* @param tableInfo 数据库表反射信息
* @return MappedStatement
*/
public abstract MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo);
injectMappedStatement
方法的实现如下:
- 以
SelectById
为例查看:
public class SelectById extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),
sqlSelectColumns(tableInfo, false),
tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
tableInfo.getLogicDeleteSql(true, true)), Object.class);
return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
}
}
- 可以看到,生成了
SqlSource
对象,再将SQL通过addSelectMappedStatementForTable
方法添加到了MappedStatement
中。
第四章:Mybatis Plus的配置
4.1 概述
- 在MP中有大量的配置,其中有一部分是Mybatis原生的配置,另一部分是MP的配置。
4.2 基本配置
4.2.1 configLocation
- MyBatis配置文件位置,如果有单独的MyBatis配置,将其路径配置到
configLocation
中。 - SpringBoot:
mybatis-plus:
config-location: classpath:mybatis-config.xml
4.2.2 mapperLocations
- Mybatis Mapper所对应的XML文件配置,如果在Mapper中有自定义方法(XML中有自定义实现),需要进行该配置,告诉Mapper所对应的XML文件位置。
- SpringBoot:
mybatis-plus:
mapper-locations: classpath*:mybatis/*.xml
4.2.3 typeAliasesPackage
- Mybatis别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在Mapper对应的XML文件中可以直接使用类名,而不用使用全限定的类名(即XML中调用的时候不用包含包名)。
- SpringBoot:
mybatis-plus:
type-aliases-package: com.example.mp.domain
4.3 进阶配置
4.3.1 mapUnderscoreToCamelCase
- mapUnderscoreToCamelCase
- 类型:boolean。
- 默认值:true。
是否开启自动驼峰命名规则的映射,即从经典数据库列名A_COLUMN(下划线命名)到经典的Java属性名AColumn的类似映射。
注意:
- 此属性在Mybatis中的原默认值为false,在MybatisPlux中,此属性也将用于生成最终的SQL的select body。
- 如果数据库命名符合规则则无需使用@TableField注解指定数据库字段名。
SpringBoot:
mybatis-plus:
configuration:
#关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在,默认值为true,一般情况下,默认即可
map-underscore-to-camel-case: false
4.3.2 cacheEnabled
- cacheEnabled:
- 类型:boolean。
- 默认值:true。
- 全局的开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为true。
- SpringBoot:
mybatis-plus:
configuration:
# 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true,一般情况下,默认即可
cacheEnabled: false
4.4 DB策略配置
4.4.1 idType
- idType:
- 类型:com.baomidou.mybatisplus.annotation.IdType。
- 默认值:ASSIGN_ID。
- 全局设置主键类型,设置后,即可省略实体对象中的
@TableId(type=IdType.AUTO)
配置。 - SpringBoot:
mybatis-plus:
global-config:
db-config:
id-type: auto
4.4.2 tablePrefix
- tablePrefix:
- 类型:String。
- 默认值:null。
- 表名前缀,全局配置后可省略
@TableName()
的配置。 - SpringBoot:
mybatis-plus:
global-config:
db-config:
table-prefix: tb_
第五章:条件构造器
5.1 概述
- 在MP中,
Wrapper
接口的实现类关系如下:
- 可以看到,
AbstractWrapper
和AbstractChainWrapper
是重点实现,我们重点学习AbstractWrapper
及其实现子类。
说明:QueryWrapper(LambdaQueryWrapper)和UpdateWrapper(LambdaUpdateWrapper)的父类用于生成sql的where条件, entity属性也用于生成sql的where条件。 注意:entity生成的where条件与使用各个api生成的where条件没有任何关联行为。
5.2 allEq
5.2.1 说明
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
参数说明:
- params:key是数据库字段名,value为字段值。
- null2IsNull:为true则在map的value为null时调用isNull方法,为false时则忽略value为null。
5.2.2 应用示例
- 示例:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-19 11:04
*/
@SpringBootTest
public class TestAllEq {
@Autowired
private UserMapper userMapper;
@Test
public void testAllEq() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//设置条件
Map<String,Object> params = new HashMap<>();
params.put("name","zhangsan");
params.put("age", null);
//SELECT id,user_name,password,name,age,email FROM tb_user WHERE (name = ? AND age IS NULL)
wrapper.allEq(params);
List<User> userList = userMapper.selectList(wrapper);
System.out.println("userList = " + userList);
}
@Test
public void testAllEqNull2IsNullFalse() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//设置条件
Map<String,Object> params = new HashMap<>();
params.put("name","zhangsan");
params.put("age", null);
//SELECT id,user_name,password,name,age,email FROM tb_user WHERE (name = ?)
wrapper.allEq(params,false);
List<User> userList = userMapper.selectList(wrapper);
System.out.println("userList = " + userList);
}
}
5.3 基本操作比较
- eq:等于
=
- ne:不等于
<>
- gt:大于
>
- lt:小于
<
- ge:大于等于
>=
- le:小于等于
le
- between:
between 值1 AND 值2
- notBetween:
NOT between 值1 AND 值2
- in:
字段 IN (值1,值2,...)
- notIn:
字段 NOT IN (值1,值2,...)
- 示例:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-19 11:20
*/
@SpringBootTest
public class TestEq {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("password", "123456")
.ge("age", 20)
.in("name", "张三", "李四", "王五");
List<User> userList = userMapper.selectList(wrapper);
System.out.println("userList = " + userList);
}
}
5.4 模糊查询
- like:
- LIKE ‘%值%’。
- 例:
like("name", "王")
—->name like '%王%'
- notlike:
- NOT LIKE ‘%值%’。
- 例:
notLike("name", "王")
—->name not like '%王%'
- likeLeft:
- LIKE ‘%值’。
- 例:
likeLeft("name", "王")
—->name like '%王'
- LikeRight:
- LIKE ‘值%’。
- 例:
likeRight("name", "王")
—->name like '王%'
- 示例:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-19 11:27
*/
@SpringBootTest
public class TestLike {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//SELECT id,user_name,password,name,age,email FROM tb_user WHERE (name LIKE %张%)
wrapper.like("name", "张");
List<User> userList = userMapper.selectList(wrapper);
System.out.println("userList = " + userList);
}
}
5.5 排序
- orderBy:
- 排序:ORDER BY 字段, …
- 例:
orderBy(true, true, "id", "name")
—->order by id ASC,name ASC
- orderByAsc:
- 排序:ORDER BY 字段, … ASC。
- 例:
orderByAsc("id", "name")
—->order by id ASC,name ASC
- orderByDesc:
- 排序:ORDER BY 字段, … DESC。
- 例:
orderByDesc("id", "name")
—->order by id DESC,name DESC
- 示例:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-19 13:40
*/
@SpringBootTest
public class TestOrderBy {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//SELECT id,user_name,password,name,age,email FROM tb_user ORDER BY age ASC
wrapper.orderByAsc("age");
List<User> userList = userMapper.selectList(wrapper);
System.out.println("userList = " + userList);
}
}
5.6 逻辑查询
- or:
- 拼接 OR。
- 例:
eq("id",1).or().eq("name","老王")
—->id = 1 or name = '老王'
- and:
- AND 嵌套。
- 例:
and(i -> i.eq("name", "李白").ne("status", "活着"))
—->and (name = '李白' and status <> '活着')
- 示例:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-19 13:44
*/
@SpringBootTest
public class TestOrAnd {
@Autowired
private UserMapper userMapper;
@Test
public void test(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//SELECT id,user_name,password,name,age,email FROM tb_user WHERE (name = ? OR age = ?)
wrapper.eq("name", "张三").or().eq("age", 18);
List<User> userList = this.userMapper.selectList(wrapper);
System.out.println("userList = " + userList);
}
}
5.7 select
select:设置查询字段。
示例:
package com.example.mp;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.mp.domain.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author 许大仙
* @version 1.0
* @since 2021-06-19 13:48
*/
@SpringBootTest
public class TestSelect {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//SELECT user_name,age,password FROM tb_user WHERE (name = ? OR age = ?)
wrapper
.eq("name", "张三")
.or()
.eq("age", 18)
.select("user_name", "age", "password");
List<User> userList = this.userMapper.selectList(wrapper);
System.out.println("userList = " + userList);
}
}