1、了解Mybatis-Plus
1.1 Mybatis-Plus介绍
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
官网:https://mybatis.plus/ 或 https://mp.baomidou.com/
愿景:我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
1.2 特性
无侵入 :只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑 损耗小 :启动即会自动注入基本 CURD ,性能基本无损耗,直接面向对象操作 强大的 CRUD 操作 :内置通用 Mapper 、通用 Service ,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 支持 Lambda 形式调用 :通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 支持多种数据库 :支持 MySQL 、 MariaDB 、 Oracle 、 DB2 、 H2 、 HSQL 、 SQLite 、 Postgre 、 SQLServer2005 、 SQLServer 等多种数据库 支持主键自动生成 :支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可*配置,完美解决主键问题 支持 XML 热加载 : Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动 支持 ActiveRecord 模式 :支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作 支持自定义全局通用操作 :支持全局通用方法注入( Write once, use anywhere ) 支持关键词自动转义 :支持数据库关键词( order 、 key...... )自动转义,还可自定义关键词 内置代码生成器 :采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用 内置分页插件 :基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List查询 内置性能分析插件 :可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询 内置全局拦截插件 :提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作 内置 Sql 注入剥离器 :支持 Sql 注入剥离,有效预防 Sql 注入攻击1.3 框架结构
2、快速开始
对于Mybatis整合MP有常常有三种用法,分别是Mybatis+MP、Spring+Mybatis+MP、Spring Boot+Mybatis+MP。
2.1、创建数据库以及表
-- 创建数据库
CREATE DATABASE mp;
-- 创建数据表
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(30) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-- 添加数据
INSERT INTO tb_user VALUES (NULL, 'zhangsan', '123456', '张三', 18, 'test1@itcast.cn'),(NULL, 'lisi', '123456', '李四', 20, 'test2@itcast.cn'),(NULL, 'wangwu', '123456', '王五', 25, 'test3@itcast.cn'),(NULL, 'zhaoliu', '123456', '赵六', 26, 'test4@itcast.cn'),(NULL, 'sunqi', '123456', '孙七', 27, 'test5@itcast.cn');
-- 查看数据表
SELECT * FROM tb_user;
2.2 创建工程
2.2.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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mybatis-plus-案例01</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>itcast-mybatis-plus-simple</module>
<module>itcast-mybatis-plus-spring</module>
</modules>
<dependencies>
<!--导入mybatis-plus依赖包-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.1.1</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<!--简化bean代码的工具包-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.4</version>
</dependency>
<!--测试单元包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--日志包-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
</dependencies>
<!--导入插件,指定我们jdk的一个版本-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration> <source>1.8</source>
<target>1.8</target> </configuration>
</plugin>
</plugins>
</build>
</project>
2.3 Mybatis + MP
下面演示,通过纯Mybatis与Mybatis-Plus整合。
2.3.1、创建子Module
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>mybatis-plus-案例01</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>itcast-mybatis-plus-simple</artifactId>
</project>
2.3.2 导入日志文件 log4j.properties
log4j.rootLogger=DEBUG,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
2.3.3 Mybatis实现查询User
第一步,编写MybatisConfig.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD约束-->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration 核心根标签-->
<configuration>
<!--environments配置数据库环境,环境可以有多个。default属性指定使用的是哪个-->
<environments default="mysql">
<!--environment配置数据库环境 id属性唯一标识-->
<environment id="mysql">
<!-- transactionManager事务管理。 type属性,采用JDBC默认的事务-->
<transactionManager type="JDBC"></transactionManager>
<!-- dataSource数据源信息 type属性 连接池-->
<dataSource type="POOLED">
<!-- property获取数据库连接的配置信息 -->
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mp" />
<property name="username" value="root" />
<property name="password" value="qal991213" />
</dataSource>
</environment>
</environments>
<!-- mappers引入映射配置文件 -->
<mappers>
<!-- mapper 引入指定的映射配置文件 resource属性指定映射配置文件的名称 -->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
第二步,编写User实体对象
第一种方法:使用get和set方法
package com.itheima.domain;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-23
* Time:17:29
*/
@TableName("tb_user")
public class User {
private long id;
private String username;
private String password;
private String name;
private Integer age;
private String email;
public User() {
}
public User(int id, String user_name, String password, String name, int age, String email) {
this.id = id;
this.username = user_name;
this.password = password;
this.name = name;
this.age = age;
this.email = email;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", user_name='" + username + '\'' +
", password='" + password + '\'' +
", name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
第二种方法:使用lombok进行了简化bean操作
package cn.itcast.mp.simple.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String email;
}
第三步,编写UserMapper接口
package com.itheima.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.User;
import java.util.List;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-24
* Time:9:36
*/
public interface UserMapper{
List<User> findAll();
}
第四步,编写映射配置文件 UserMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
mapper:核心根标签
namespace属性:名称空间
-->
<mapper namespace="com.itheima.mapper.UserMapper">
<!--
select:查询功能的标签
id属性:唯一标识
resultType属性:指定结果映射对象类型
parameterType属性:指定参数映射对象类型
-->
<select id="findAll" resultType="com.itheima.domain.User">
select * from tb_user
</select>
</mapper>
第五步,编写TestMybatis测试用例
package com.itheima;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-24
* Time:11:25
*/
public class TestMybatis {
@Test
public void testUserList() throws Exception{
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.findAll();
for (User user : list) { System.out.println(user);
}
}
}
测试结果
2.3.4 Mybatis+MP实现查询User
第一步,将UserMapper继承BaseMapper,将拥有了BaseMapper中的所有方法
package com.itheima.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.User;
import java.util.List;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-24
* Time:9:36
*/
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
第二步,使用MP中的MybatisSqlSessionFactoryBuilder生成SqlSessionFactory对象进程构建 测试用例 TestMybatisPlus:
package com.itheima;
import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-24
* Time:11:25
*/
public class TestMybatisPlus {
@Test
public void testUserList() throws Exception{
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//测试用例
//List<User> list = mapper.findAll();
List<User> list = mapper.selectList(null);
for (User user : list) { System.out.println(user);
}
}
}
运行报错
解决:在User对象中添加@TableName,指定数据库表名
测试结果
简单说明:
由于使用了MybatisSqlSessionFactoryBuilder进行了构建,继承的BaseMapper中的方法就载入到了SqlSession中,所以就可以直接使用相关的方法;
2.4 Spring + Mybatis + MP
引入了Spring框架,数据源、构建等工作就交给了Spring管理。
2.4.1 创建子Module
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>mybatis-plus-案例01</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>itcast-mybatis-plus-spring</artifactId>
<properties>
<spring.version>5.1.6.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
2.4.2 实现查询User
第一步,编写jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mp
jdbc.username=root
jdbc.password=qal991213
第二步,编写applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:*.properties"/>
<!-- 定义数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="10"/>
<property name="minIdle" value="5"/>
</bean>
<!--这里使用MP提供的sqlSessionFactory,完成了Spring与MP的整合-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--扫描mapper接口,使用的依然是Mybatis原生的扫描器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.mapper"/>
</bean>
</beans>
第三步,编写User对象以及UserMapper接口
User对象同上,这里不在阐述
编写UserMapper接口:
package com.itheima.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.User;
import java.util.List;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-24
* Time:16:06
*/
public interface UserMapper extends BaseMapper<User> {
}
第四步,编写测试用例
package com.itheima.test;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-24
* Time:16:11
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestMybatisPlusSpring {
@Autowired
private UserMapper userMapper;
@Test
public void findAll(){
List<User> list = this.userMapper.selectList(null);
for (User user : list) {
System.out.println(user);
}
}
}
测试结果
2.5 SpringBoot + Mybatis + MP
使用SpringBoot将进一步的简化MP的整合,需要注意的是,由于使用SpringBoot需要继承parent,所以需要重新创建工程,并不是创建子Module。
2.5.1 创建工程
2.3.2 导入依赖
<?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 http://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.1.4.RELEASE</version>
</parent>
<groupId>org.example</groupId>
<artifactId>mybatis-plus-springboot-案例01</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--简化代码的工具包-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--mybatis-plus的springboot支持-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
添加日志文件log4j.properties
log4j.rootLogger=DEBUG,A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.Aa1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
2.5.3 编写application.properties
spring.application.name = itcast-mp-springboot spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mp spring.datasource.username=root spring.datasource.password=qal991213
2.5.4 编写pojo即User的实体类
同上,此处不再阐述
2.5.5 编写UserMapper接口
package com.itheima.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.User;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-24
* Time:17:11
*/
public interface UserMapper extends BaseMapper<User> {
}
2.5.6 编写启动类 MyApplication
package com.itheima;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-24
* Time:17:14
*/
@MapperScan("com.itheima.mapper")//设置mapper接口的扫描包
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class,args);
}
}
2.5.7 编写测试用例
package com.itheima.test;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-24
* Time:17:19
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class TestMybatisSpringboot {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect(){
List<User> list = this.userMapper.selectList(null);
for (User user : list) {
System.out.println(user);
}
}
}
测试结果
3、通用CRUD
通过前面的学习,我们了解到通过继承BaseMapper就可以获取到各种各样的单表操作,接下来我们将详细讲解这些操作。
3.1 插入操作
3.1.1 方法定义
/**
插入一条记录
@param entity 实体对象
*/
int insert(T entity);
3.1.2 测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class TestMybatisSpringboot {
@Autowired
private UserMapper userMapper;
//插入一条数据
@Test
public void testInsert(){
User user = new User();
user.setUsername("wangtongxue");
user.setPassword("666666");
user.setName("王晓娟");
user.setAge(18);
user.setMail("wangtongxue@163.com");
int result = this.userMapper.insert(user);//返回的result是受影响的行数,并不是自增后的id
System.out.println("result =>" + result);
System.out.println(user.getId());//自增后的id会回填到对象中
}
}
3.1.3 测试
可以看到,数据已经写入到了数据库,但是,id的值不正确,我们期望的是数据库自增长,实际是MP生成了id的值写入到了数据库。
如何设置id的生成策略呢?
MP支持的id策略:
package com.baomidou.mybatisplus.annotation;
import lombok.Getter;
/*
生成ID类型枚举类
@author hubin
@since 2015-11-10
*/
@Getter
public enum IdType {
/*
数据库ID自增
*/
AUTO(0),
/*
该类型为未设置主键类型
*/
NONE(1),
/*
用户输入ID
<p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2),
/*
以下3种类型、只有当插入对象ID 为空,才自动填充。
*/
/*
全局唯一ID (idWorker)
*/
ID_WORKER(3),
/*
全局唯一ID (UUID)
*/
UUID(4),
/*
字符串全局唯一ID (idWorker 的字符串表示)
*/
ID_WORKER_STR(5);
private final int key;
IdType(int key) {
this.key = key;
}
}
修改User对象:
@TableName("tb_user")
public class User {
@TableId(type = IdType.AUTO)//指定ID类型为自增长
private long id;
private String username;
3.1.4 @TableField
在MP中通过@TableField注解可以指定字段的一些属性,常常解决的问题有2个:
1、对象中的属性名和字段名不一致的问题(非驼峰)
2、对象中的属性字段在表中不存在的问题
使用:
其他用法,如大字段不加入查询字段:
效果:
3.2 更新操作
在MP中,更新操作有2种,一种是根据id更新,另一种是根据条件更新。
3.2.1 根据id更新
方法定义:
/**
* 根据 ID 修改
*
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);
测试用例:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class TestMybatisSpringboot {
@Autowired
private UserMapper userMapper;
//根据id更新
@Test
public void testUpdateById(){
User user = new User();
user.setId(1L);
//user.setAge(21);
user.setName("sha逼焦");
int result = this.userMapper.updateById(user);
System.out.println("result =>" + result);
}
}
3.2.2 根据条件更新
方法定义:
/**
* 根据 whereEntity 条件,更新记录
*
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
测试用例:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class TestMybatisSpringboot {
@Autowired
private UserMapper userMapper;
//根据条件更新
@Test
public void testUpdate(){
User user = new User();
user.setAge(44); //更新的字段
user.setName("狗逼焦");
//更新的条件
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("id",1L);
//执行更新操作
int result = this.userMapper.update(user, wrapper);
System.out.println("result =>" + result);
}
}
或者,通过UpdateWrapper进行更新:
@Test
public void testUpdate() {
//更新的条件以及字段
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.eq("id", 6).set("age", 23);
//执行更新操作
int result = this.userMapper.update(null, wrapper);
System.out.println("result = " + result);
}
均可达到更新的效果。
3.3 删除操作
3.3.1 deleteById
方法定义:
/**
* 根据 ID 删除
*
* @param id 主键ID
*/
int deleteById(Serializable id);
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testDeleteById() {
//执行删除操作
int result = this.userMapper.deleteById(6L);
System.out.println("result = " + result);
}
}
数据被删除。
3.3.2 deleteByMap
方法定义:
/**
* 根据 columnMap 条件,删除记录
*
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testDeleteByMap() {
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("age",20);
columnMap.put("name","张三");
//将columnMap中的元素设置为删除的条件,多个之间为and关系
int result = this.userMapper.deleteByMap(columnMap);
System.out.println("result = " + result);
}
}
3.3.3 delete
方法定义:
/**
* 根据 entity 条件,删除记录
*
* @param wrapper 实体对象封装操作类(可以为 null)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testDeleteByMap() {
User user = new User();
user.setAge(20);
user.setName("张三");
//将实体对象进行包装,包装为操作条件
QueryWrapper<User> wrapper = new QueryWrapper<>(user);
int result = this.userMapper.delete(wrapper);
System.out.println("result = " + result);
}
}
3.3.4 deleteBatchIds
方法定义:
/**
* 删除(根据ID 批量删除)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testDeleteByMap() {
//根据id集合批量删除
int result = this.userMapper.deleteBatchIds(Arrays.asList(1L,10L,20L));
System.out.println("result = " + result);
}
}
3.4 查询操作
MP提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分页查询等操作。
3.4.1 selectById
方法定义:
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
T selectById(Serializable id);
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectById() {
//根据id查询数据
User user = this.userMapper.selectById(2L);
System.out.println("result = " + user);
}
}
3.4.2 selectBatchIds
方法定义:
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectBatchIds() {
//根据id集合批量查询
List<User> users = this.userMapper.selectBatchIds(Arrays.asList(2L, 3L, 10L));
for (User user : users) {
System.out.println(user);
}
}
}
3.4.3 selectOne
方法定义:
/**
* 根据 entity 条件,查询一条记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectOne() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.eq("name", "李四");
//根据条件查询一条数据,如果结果超过一条会报错
User user = this.userMapper.selectOne(wrapper);
System.out.println(user);
}
}
3.4.4 selectCount
方法定义:
/**
* 根据 Wrapper 条件,查询总记录数
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectCount() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 23); //年龄大于23岁
//根据条件查询数据条数
Integer count = this.userMapper.selectCount(wrapper);
System.out.println("count = " + count);
}
}
3.4.5 selectList
方法定义:
/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectList() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 23); //年龄大于23岁
//根据条件查询数据
List<User> users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println("user = " + user);
}
}
}
3.4.6 selectPage
方法定义:
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
配置分页插件:
@Configuration
@MapperScan("cn.itcast.mp.mapper") //设置mapper接口的扫描包
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectPage() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 20); //年龄大于20岁
Page<User> page = new Page<>(1,1);
//根据条件查询数据
IPage<User> iPage = this.userMapper.selectPage(page, wrapper);
System.out.println("数据总条数:" + iPage.getTotal());
System.out.println("总页数:" + iPage.getPages());
List<User> users = iPage.getRecords();
for (User user : users) {
System.out.println("user = " + user);
}
}
}
3.5 SQL注入的原理
前面我们已经知道,MP在启动后会将BaseMapper中的一系列的方法注册到meppedStatements中,那么究竟是如何注入的呢?流程又是怎么样的?下面我们将一起来分析下。
在MP中,ISqlInjector负责SQL的注入工作,它是一个接口,AbstractSqlInjector是它的实现类,实现关系如下:
在AbstractSqlInjector中,主要是由inspectInject()方法进行注入的,如下:
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
Class<?> modelClass = this.extractModelClass(mapperClass);
if (modelClass != null) {
String className = mapperClass.toString();
Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
List<AbstractMethod> methodList = this.getMethodList();
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);
}
}
}
在实现方法中, methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo)); 是关键,循环遍历方法,进行注入。
最终调用抽象方法injectMappedStatement进行真正的注入:
查看该方法的实现:
以SelectById为例查看:
可以看到,生成了SqlSource对象,再将SQL通过addSelectMappedStatement方法添加到meppedStatements中。
4、配置
在MP中有大量的配置,其中有一部分是Mybatis原生的配置,另一部分是MP的配置,详情:https://mybatis.plus/config/
下面我们对常用的配置做讲解。
4.1 基本配置
4.1.1 configLocation
MyBatis 配置文件位置,如果您有单独的 MyBatis 配置,请将其路径配置到 configLocation 中。 MyBatisConfiguration 的具体内容请参考MyBatis 官方文档 (即指定全局的配置文件)
Spring Boot:
mybatis-plus.config-location = classpath:MybatisConfig.xml
Spring MVC:
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
4.1.2 mapperLocations
MyBatis Mapper 所对应的 XML 文件位置,如果您在 Mapper 中有自定义方法(XML 中有自定义实现),需要进行该配置,告诉 Mapper 所对应的 XML 文件位置。(可以写自己的sql语句)
Spring Boot:
mybatis-plus.mapper-locations = classpath*:mybatis/*.xml
Spring MVC:
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="mapperLocations" value="classpath*:mybatis/*.xml"/>
</bean>
Maven 多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)
测试:
UserMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper">
<select id="findById" resultType="User">
select * from tb_user where id = #{id}
</select>
</mapper>
UserMapper:
package com.itheima.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.User;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-24
* Time:17:11
*/
public interface UserMapper extends BaseMapper<User> {
User findById(Long id);
}
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectPage() {
User user = this.userMapper.findById(2L);
System.out.println(user);
}
}
4.1.3 typeAliasesPackage
MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML 文件中可以直接使用类名,而不用使用全限定的类名(即 XML 中调用的时候不用包含包名)。
Spring Boot:
mybatis-plus.type-aliases-package = cn.itcast.mp.pojo
Spring MVC:
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="typeAliasesPackage" value="com.baomidou.mybatisplus.samples.quickstart.entity"/>
</bean>
4.2 进阶配置
本部分(Configuration)的配置大都为 MyBatis 原生支持的配置,这意味着您可以通过 MyBatis XML 配置文件的形式进行配置。
4.2.1 map-Underscore-To-Camel-Case
- 类型: boolean
- 默认值: true
是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射。
注意:
此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQL 的 select body
如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
SpringBoot:
注意:关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
mybatis-plus.configuration.map-underscore-to-camel-case=false
4.2.2 cacheEnabled
- 类型: boolean
- 默认值: true
全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true。
mybatis-plus.configuration.cache-enabled=false
4.3 DB 策略配置
4.3.1 idType
- 类型: com.baomidou.mybatisplus.annotation.IdType
- 默认值: ID_WORKER
全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置。
SpringBoot:
mybatis-plus.global-config.db-config.id-type=auto
SpringMVC:
<!--这里使用MP提供的sqlSessionFactory,完成了Spring与MP的整合-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="globalConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<property name="idType" value="AUTO"/>
</bean>
</property>
</bean>
</property>
</bean>
4.3.2 tablePrefix
- 类型: String
- 默认值: null
表名前缀,全局配置后可省略@TableName()配置。
SpringBoot:
mybatis-plus.global-config.db-config.table-prefix=tb_
SpringMVC:
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="globalConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<property name="idType" value="AUTO"/>
<property name="tablePrefix" value="tb_"/>
</bean>
</property>
</bean>
</property>
</bean>
5 、条件构造器
在MP中,Wrapper接口的实现类关系如下:
可以看到,AbstractWrapper和AbstractChainWrapper是重点实现,接下来我们重点学习AbstractWrapper以及其子类。
说明:
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类 用于生成 sql的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
官网文档地址:https://mybatis.plus/guide/wrapper.html
5.1 allEq
5.1.1 说明
allEq(Map<K, V> params)
allEq(Map<K, V> params, boolean null2IsNull)
allEq(boolean condition, Map<K, V> params, boolean null2IsNull)
- 全部eq(或个别isNull)
个别参数说明: params : key 为数据库字段名, value 为字段值 ,null2IsNull : 为 true 则在 map 的 value 为 null 时调用 isNull 方法,为 false 时则忽略 value 为 null 的
- 例1: allEq({id:1,name:"老王",age:null}) ---> id = 1 and name = '老王' and age is null
- 例2: allEq({id:1,name:"老王",age:null}, false) ---> id = 1 and name = '老王'
allEq(BiPredicate<K, V> filter, Map<K, V> params)
allEq(BiPredicate<K, V> filter, Map<K, V> params, boolean null2IsNull)
allEq(boolean condition, BiPredicate<K, V> filter, Map<K, V> params, boolean null2IsNull)
个别参数说明: filter : 过滤函数,是否允许字段传入比对条件中 params 与 null2IsNull : 同上
- 例1: allEq((k,v) -> k.indexOf("a") > 0, {id:1,name:"老王",age:null}) ---> name = '老王' and age is null
- 例2: allEq((k,v) -> k.indexOf("a") > 0, {id:1,name:"老王",age:null}, false) ---> name = '老王'
5.1.2 测试用例
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//设置条件
Map<String,Object> params = new HashMap<>();
params.put("name","曹操");
params.put("age","20");
params.put("password",null);
//wrapper.allEq(params); SELECT * FROM tb_user WHERE password IS NULL AND name = ? AND age = ?
//wrapper.allEq(params,false); SELECT * FROM tb_user WHERE name = ? AND age = ?
//wrapper.allEq((K,V) -> (K.equals("name") || (K.equals("age"))) , params); SELECT * FROM tb_user WHERE name = ? AND age = ?
List<User> list = this.userMapper.selectList(wrapper);
for (User user : list) {
System.out.println(user);
}
}
}
5.2 基本比较操作
- eq
- 等于 =
- ne
- 不等于 <>
- gt
- 大于 >
- ge
- 大于等于 >=
- lt
- 小于 <
- le
- 小于等于 <=
- between
- BETWEEN 值1 AND 值2
- notBetween
- NOT BETWEEN 值1 AND 值2
- in
- 字段 IN (value.get(0), value.get(1), ...)
- notIn
- 字段 NOT IN (v0, v1, ...)
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void userMapper(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//SELECT * FROM tb_user WHERE password = ? AND age >= ? AND name IN (?,?,?)
wrapper.eq("password","123456")
.ge("age",20)
.in("name","李四","王五","赵六");
List<User> list = this.userMapper.selectList(wrapper);
for (User user : list) {
System.out.println(user);
}
}
}
5.3 模糊查询
- 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 '王%'
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test//模糊查询
public void testWrapperLike(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//wrapper.likeLeft("name","焦"); SELECT * FROM tb_user WHERE name LIKE ? Parameters: %焦(String)
//wrapper.like("name","张"); SELECT * FROM tb_user WHERE name LIKE ? Parameters: %张%(String)
List<User> list = this.userMapper.selectList(wrapper);
for (User user : list) {
System.out.println(user);
}
}
}
5.4 排序
- 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
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test//排序
public void testWrapperOrder(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//按照倒序进行排序
wrapper.orderByDesc("age"); //SELECT * FROM tb_user ORDER BY age DESC
List<User> list = this.userMapper.selectList(wrapper);
for (User user : list) {
System.out.println(user);
}
}
}
5.5 逻辑查询
- or
- 拼接 OR
- 主动调用 or 表示紧接着下一个方法不是用 and 连接!(不调用 or 则默认为使用 and 连接)
- and
- AND 嵌套
- 例: and(i -> i.eq("name", "李白").ne("status", "活着")) ---> and (name = '李白' and status <> '活着')
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test//逻辑查询
public void testWrapperOr(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","李四").or().eq("age",0); //SELECT * FROM tb_user WHERE name = ? OR age = ?
List<User> list = this.userMapper.selectList(wrapper);
for (User user : list) {
System.out.println(user);
}
}
}
5.6 select
在MP查询中,默认查询所有的字段,如果有需要也可以通过select方法进行指定字段。
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test//查询表中指定字段的数据
public void testWrapperSelect(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("username","password"); //SELECT username,password FROM tb_user
List<User> list = this.userMapper.selectList(wrapper);
for (User user : list) {
System.out.println(user);
}
}
}
6、插件
6.1 mybatis的插件机制
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
2. ParameterHandler (getParameterObject, setParameters)
3. ResultSetHandler (handleResultSets, handleOutputParameters)
4. StatementHandler (prepare, parameterize, batch, update, query)
我们看到了可以拦截Executor接口的部分方法,比如update,query,commit,rollback等方法,还有其他接口的一些方法等。
总体概括为:
1. 拦截执行器的方法
2. 拦截参数的处理
3. 拦截结果集的处理
4. 拦截Sql语法构建的处理
拦截器示例:
第一步,先创建一个 plugins 包,并写上自定义的拦截器
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//拦截方法,具体业务逻辑编写的位置
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
//创建target对象的代理对象,目的是将当前拦截器加入到该对象中
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
//属性设置
}
}
注入到Spring容器:
/**
* 自定义拦截器
*/
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}
或者通过xml配置,mybatisConfig.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="cn.itcast.mp.plugins.MyInterceptor"></plugin>
</plugins>
</configuration>
6.2 执行分析插件
在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作,
注意:该插件仅适用于开发环境,不适用于生产环境。
SpringBoot配置:
@Bean
public SqlExplainInterceptor sqlExplainInterceptor(){
SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
List<ISqlParser> sqlParserList = new ArrayList<>();
// 攻击 SQL 阻断解析器、加入解析链
sqlParserList.add(new BlockAttackSqlParser());
sqlExplainInterceptor.setSqlParserList(sqlParserList);
return sqlExplainInterceptor;
}
测试:
@Test
public void testUpdateAnalyse(){
User user = new User();
user.setAge(20);
int result = this.userMapper.update(user,null);
System.out.println(result);
}
结果:可以看到,当执行全表更新时,会抛出异常,这样有效防止了一些误操作。
6.3 性能分析插件
性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常。
该插件只用于开发环境,不建议生产环境使用。
配置:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<!-- SQL 执行性能分析,开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长 单位为ms -->
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
<property name="maxTime" value="100" />
<!--SQL是否格式化 默认false-->
<property name="format" value="true" />
</plugin>
</plugins>
</configuration>
执行结果:
Time:14 ms - ID:com.itheima.mapper.UserMapper.selectList
Execute SQL:
SELECT
id,
username,
password,
name,
age,
email AS mail
FROM
tb_user
可以看到,执行时间为14ms。如果将maxTime设置为1,那么,该操作会抛出异常。
Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: The SQL execution time is too large, please optimize !
6.4 乐观锁插件
6.4.1 主要适用场景
意图:当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
6.4.2 插件配置
spring xml:
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
spring boot:
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
6.4.3 注解实体字段
需要为实体字段添加@Version注解。
第一步,为表添加version字段,并且设置初始值为1:
ALTER TABLE `tb_user` ADD COLUMN `version` int(10) NULL AFTER `email`;
UPDATE `tb_user` SET `version`='1';
第二步,为User实体对象添加version字段,并且添加@Version注解:
@Version
private Integer version;
6.4.4 测试
测试用例:
@Test
public void testUpdateVersion(){
User user = new User();
User user1 = this.userMapper.selectById(1L);
user.setAge(17);
user.setId(1L);
user.setVersion(user1.getVersion());//获取到的version为1
int result = this.userMapper.updateById(user);
System.out.println("result =>" + result);
}
可以看到,更新的条件中有version条件,并且更新的version为2。
如果再次执行,更新则不成功。这样就避免了多人同时更新时导致数据的不一致。
6.4.5 特别说明
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下 newVersion = oldVersion + 1 newVersion 会回写到 entity 中
- 仅支持 updateById(id) 与 update(entity, wrapper) 方法
- 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
7、Sql 注入器
我们已经知道,在MP中,通过AbstractSqlInjector将BaseMapper中的方法注入到了Mybatis容器,这样这些方法才可以正常执行。
那么,如果我们需要扩充BaseMapper中的方法,又该如何实现呢?
下面我们以扩展findAll方法为例进行学习。
8、自动填充功能
有些时候我们可能会有这样的需求,插入或者更新数据时,希望有些字段可以自动填充数据,比如密码、version等。在MP中提供了这样的功能,可以实现自动填充。
8.1 添加@TableField注解
@TableField(fill = FieldFill.INSERT) //插入数据时进行填充
private String password;
为password添加自动填充功能,在新增数据时有效。
FieldFill提供了多种模式选择:
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}
8.2 编写MyMetaObjectHandler
package com.itheima.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-28
* Time:16:30
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
//首先得到表中password的值,然后对得到的值进行判断,如果为空则进行填充,否则不用管
Object password = getFieldValByName("password", metaObject);
if(password == null){
//字段为空,可以进行填充
setFieldValByName("password", "888888", metaObject);
}
}
@Override
public void updateFill(MetaObject metaObject) {
}
}
8.3 测试
//插入一条数据
@Test
public void testInsert(){
User user = new User();
user.setUsername("guanyu");
//user.setPassword("666666");
user.setName("关羽");
user.setAge(200);
user.setMail("guanyu@163.com");
int result = this.userMapper.insert(user);
System.out.println("result =>" + result);
System.out.println(user.getId());
}
9、逻辑删除
开发系统时,有时候在实现功能时,删除操作需要实现逻辑删除,所谓逻辑删除就是将数据标记为删除,而并非真正的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免数据被真正的删除。
MP就提供了这样的功能,方便我们使用,接下来我们一起学习下。
9.1 修改表结构
为tb_user表增加deleted字段,用于表示数据是否被删除,1代表删除,0代表未删除。
ALTER TABLE `tb_user` ADD COLUMN `deleted` INT(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER `version`;
同时,也修改User实体,增加deleted属性并且添加@TableLogic注解:
@TableLogic
private Integer deleted;
9.2 配置
application.properties:
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0
9.3 测试
//删除操作 根据ID删除
@Test
public void testDeleteById(){
int result = this.userMapper.deleteById(1l);
System.out.println("result =>" + result);
}
测试查询:
//根据id进行查询
@Test
public void selectById(){
User user = this.userMapper.selectById(1l);
System.out.println(user);
}
可见,已经实现了逻辑删除。
10、通用枚举
解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!
10.1 修改表结构
ALTER TABLE tb_user ADD COLUMN sex INT(1) NULL DEFAULT 1 COMMENT '1-男,2-女' AFTER deleted;
10.2 定义枚举
package com.itheima.enums;
import com.baomidou.mybatisplus.core.enums.IEnum;
/**
* Created with InteIIiJ IDEA.
* Description:
* User:qal
* Date:2021-10-28
* Time:17:07
*/
public enum SexEnum implements IEnum<Integer> {
MAN(1,"男"),
WOMAN(2,"女");
private int value;
private String desc;
SexEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Integer getValue() {
return this.value;
}
@Override
public String toString() {
return this.desc;
}
}
10.3 配置
# 枚举包扫描
mybatis-plus.type-enums-package=cn.itcast.mp.enums
10.4 修改实体
private SexEnum sex;
10.5 测试
测试插入数据:
//插入一条数据
@Test
public void testInsert(){
User user = new User();
user.setUsername("diaochan");
//user.setPassword("666666");
user.setName("貂蝉");
user.setAge(18);
user.setMail("diaochan@163.com");
user.setVersion(1);
user.setSex(SexEnum.WOMAN);
int result = this.userMapper.insert(user);
System.out.println("result =>" + result);
System.out.println(user.getId());
}
SQL:
==> Preparing: INSERT INTO tb_user ( username, password, name, age, email, version, sex ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
==> Parameters: diaochan(String), 888888(String), 貂蝉(String), 18(Integer), diaochan@163.com(String), 1(Integer), 2(Integer)
<== Updates: 1
查询:
//根据id进行查询
@Test
public void selectById(){
User user = this.userMapper.selectById(9l);
System.out.println(user);
}
结果:
User{id=9, username='diaochan', password='888888', name='貂蝉', age=18, mail='diaochan@163.com', address='null', version=1, deleted=0, sex=女}
从测试可以看出,可以很方便的使用枚举了。
查询条件时也是有效的:
@Test
public void testSelectBySex(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("sex",SexEnum.WOMAN);
List<User> list = this.userMapper.selectList(wrapper);
for (User user : list) {
System.out.println(user);
}
}
SQL:
==> Preparing: SELECT id,username,password,name,age,email AS mail,version,deleted,sex FROM tb_user WHERE deleted=0 AND sex = ?
==> Parameters: 2(Integer)
<== Total: 1
11、代码生成器
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、MapperXML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
11.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
http://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.1.4.RELEASE</version>
</parent>
<groupId>cn.itcast.mp</groupId>
<artifactId>itcast-mp-generator</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis-plus的springboot支持-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
11.2 代码
package cn.itcast.mp.generator;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
/**
* <p>
* mysql 代码生成器演示例子
* </p>
*/
public class MysqlGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotEmpty(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
/**
* RUN THIS
*/
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("itcast");
gc.setOpen(false);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://127.0.0.1:3306/mp? useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(scanner("模块名"));
pc.setParent("cn.itcast.mp.generator");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
List<FileOutConfig> focList = new ArrayList<>();
focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输入文件名称
return projectPath + "/itcast-mp- generator/src/main/resources/mapper/" + pc.getModuleName()+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
mpg.setTemplate(new TemplateConfig().setXml(null));
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//strategy.setSuperEntityClass("com.baomidou.mybatisplus.samples.generator.common.BaseE ntity");
strategy.setEntityLombokModel(true); //strategy.setSuperControllerClass("com.baomidou.mybatisplus.samples.generator.common.B aseController");
strategy.setInclude(scanner("表名"));
strategy.setSuperEntityColumns("id");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
// 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
测试:
请输入模块名:
user
请输入表明:
tb_user
12、MybatisX 快速开发插件
MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。
功能:
- Java 与 XML 调回跳转
- Mapper 方法自动生成 XML