Mybatis-Plus详解
Mybatis-Plus概念:
Mybatis-Plus介绍:
官⽹:https://mp.baomidou.com/
MyBatis-Plus(简称 MP)是⼀个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变(即自然内置了MyBatis的依赖,也就可以单独的使用MyBatis框架的内容),为简化开发、提高效率而生

愿景:
我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1 P、 2P,基友搭配,效率翻倍

特性:
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
架构:

作者:
Mybatis-Plus是由 baomidou(苞米豆)组织开发并且开源的,以前该组织大概有30人左右,现在可能少了些,如下就是17人
码云地址:https://gitee.com/organizations/baomidou

Mybatis-Plus快速入门:
安装:
全新的 MyBatis-Plus 3.0 版本基于 JDK8,提供了 lambda 形式的调用,所以安装集成 MP3.0 要求如下:
JDK 8+
Maven
如果是Spring Boot,那么对应的依赖就是:
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.0version>
dependency>
如果是Spring MVC,那么对应的依赖就是:
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plusartifactId>
<version>3.4.0version>
dependency>
实际上Spring Boot也可以自己操作扫描,因为他也是一种方式而已,所以,我们也可以单独的操作其他框架
只是Spring boot帮我们操作了而已,使得方便,而单独时
如操作Spring,需要我们些配置文件或者注解(有其他方式,则自然可以使用其他方式),并操作才可
所以上面的依赖,并不是绝对的必须是对应的依赖,虽然使用Spring Boot操作时,需要
当然,若你创建Spring boot而不引入对应的依赖,那么自然,可以自己引入依赖来单独操作,这是可以的

因为可能会覆盖mybatis的版本等等
对于Mybatis整合MP有常常有三种用法,分别是Mybatis+MP、Spring+Mybatis+MP、Spring Boot+Mybatis+MP
MyBatis-Plus简称 MP
创建数据库以及表:
CREATE DATABASE mp CHARACTER SET utf8;
USE mp;
DROP TABLE IF EXISTS tb_user;
CREATE TABLE USER(
id BIGINT(20) NOT NULL COMMENT '主键ID',
NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
INSERT INTO USER (id, NAME, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
创建工程(普通的创建,不是Spring boot工程):
实际上无论是普通的创建还是特殊的创建,都只是在空的创建下
加上对应的目录或者文件夹而已,或者设置为特殊文件夹,如web工程(创建时可以加上模板,或自己配置,具体可以百度)
导入依赖:
<dependencies>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plusartifactId>
<version>3.1.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.0.11version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.4version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.6.4version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
Mybatis + MP:
下面演示,通过纯Mybatis与Mybatis-Plus整合
创建该项目的子Module(并没有对应依赖的导入),对应目录如下:

对应的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
Mybatis实现查询User:
第⼀步,编写mybatis-config.xml文件(资源文件里面):
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties">properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
environments>
<mappers>
<package name="com.lagou.mapper">package>
mappers>
configuration>
再创建jdbc.properties文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mp
jdbc.username=root
jdbc.password=123456
#自己的数据库信息
第二步,编写User实体对象:(这里使用lombok进行了进化bean操作)
package com.lagou.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
我们除了可以运行程序看看是否添加了方法,也可以点击这里来进行查看:

第三步,编写UserMapper接⼝:
package com.lagou.mapper;
import com.lagou.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> findAll();
}
第四步,编写UserMapper.xml文件:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lagou.mapper.UserMapper">
<select id="findAll" resultType="com.lagou.pojo.User">
select * from user
select>
mapper>
第五步,编写MPTest类测试用例:
package com.lagou.test;
import com.lagou.mapper.UserMapper;
import com.lagou.pojo.User;
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.IOException;
import java.io.InputStream;
import java.util.List;
public class MPTest {
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAll();
for (User user : all) {
System.out.println(user);
}
}
}
查看日志,会出现测试结果:
User(id=1, name=Jone, age=18, email=[email protected])
User(id=2, name=Jack, age=20, email=[email protected])
User(id=3, name=Tom, age=28, email=[email protected])
User(id=4, name=Sandy, age=21, email=[email protected])
User(id=5, name=Billie, age=24, email=[email protected])
注:在后面操作mybatis-plus时,如果实体类名称和表名称不⼀致,可以在实体类上添加注解@TableName(“指定数据库表名”)
当然这是后话了,因为普通的mybatis是操作字段的,与类名并没有直接的关系,而mybatis-plus却与类名有关联
Mybatis+MP实现查询User:
第⼀步,将UserMapper接口继承BaseMapper接口,将可以拥有BaseMapper接口中的所有⽅法:
package com.lagou.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;
import java.util.List;
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
第二步,使用MP中的MybatisSqlSessionFactoryBuilder进程构建:
回到测试类MPTest,添加如下方法:
@Test
public void test2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.selectList(null);
for (User user : all) {
System.out.println(user);
}
}
测试:
User(id=1, name=Jone, age=18, email=[email protected])
User(id=2, name=Jack, age=20, email=[email protected])
User(id=3, name=Tom, age=28, email=[email protected])
User(id=4, name=Sandy, age=21, email=[email protected])
User(id=5, name=Billie, age=24, email=[email protected])
我们发现,结果一样,唯一的区别就是工厂那里变了,那么可以操作原来的mapper.findAll()吗,答:可以
正如前面说的无侵入,即只增强,我们也可以直观的看到,我们在对应的接口中,实际上拥有的很多的方法
其中自然包括我们自己写的,那么其他的方法是如何操作的呢:
虽然我们并没有写对应的配置文件,实际上他们是有固定的模板的
但模板通常是创建的,模板可以称为xml文件或者是操作的sql语句
一般是创建MappedStatement类信息,后面会说明,在后面的源码中会说明
包含了对应接口对应的语句和内容信息
当我们传递泛型时
通过反射得到泛型信息,好像只能是当成父类可以得到泛型信息
正好BaseMapper是UserMapper的父类(或者他子类的子类的父类,一路查找,后面的Sql 注入器会说明的),接口也是类的一种
所以可以这样说得到泛型信息,然后就可以给模板进行创建改变,将对应的泛型类名称或者地址等等放入对应位置
也就是使得操作的对应值基本都是对应的泛型类型,这里就是User,所以对应的模板
操作的表就是user(至于为什么不是User,后面会解释),对应的接口方法或者相关内容也都是User
这时,我们试着将User修改成Userr,那么可能在其他的模板内容修改时,没有报错
但是,在对应的数据库的sql语句中,却没有对应的表,那么自然报错,经过测试的确如此,那么我们在前面说过
操作mybatis-plus时,如果实体类名称和表名称不⼀致,可以在实体类上添加注解@TableName(“指定数据库表名”)
package com.lagou.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class Userr {
private Long id;
private String name;
private Integer age;
private String email;
}
简单说明:
由于使用了 MybatisSqlSessionFactoryBuilder进行了构建,继承的BaseMapper中的方法就载入到了SqlSession中
并在操作泛型时,会使得初始化,所以就可以直接使用相关的方法
如图:

在61章博客说明过,简单来说是对应存放sql语句的地方,只是当时是通过xml来得到的,而这里是模板
所以模板可以理解为创建xml然后操作,而实际上他也只是设置信息而已,就与xml一样
Spring + Mybatis + MP:
引⼊了Spring框架,数据源、构建等工作就交给了Spring管理
创建子Module,并导入对应依赖
<properties>
<spring.version>5.1.6.RELEASEspring.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${spring.version}version>
dependency>
dependencies>
对应目录如下:

实现查询User:
第一步,编写jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mp?serverTimezone=GMT%2B8&useSSL=false
jdbc.username=root
jdbc.password=123456
第二步,编写applicationContext.xml:
<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:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.lagou.mapper"/>
bean>
beans>
第三步,编写User对象以及UserMapper接口:
package com.lagou.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
package com.lagou.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;
import java.util.List;
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
第四步,编写TestSpringMp类测试用例:
package com.lagou.test;
import com.lagou.mapper.UserMapper;
import com.lagou.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestSpringMp {
@Autowired
private UserMapper userMapper;
@Test
public void test(){
List<User> all = userMapper.selectList(null);
System.out.println(all);
}
}
执行测试,发现,有对应的结果了
SpringBoot + Mybatis + MP:
使用SpringBoot将进⼀步的简化MP的整合(自动配置,就不需要编写对应的配置文件了),需要注意的是
由于使用SpringBoot需要继承parent,所以需要重新创建工程,并不是创建子Module
创建工程(不知道Spring boot可以到88章博客里学习),并导入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.1.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
对应的目录(有部分修改的,如启动类移动到lagou包下):

对应的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
编写application.properties文件:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
#密码记得要正确,不要在数后面加上#,否则#当成数,而不是注释,通常说明是不要在=后面写
编写User类:
package com.lagou.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
编写UserMapper接口:
package com.lagou.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;
import java.util.List;
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
编写启动类LagouMpSpringbootApplication:
package com.lagou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.lagou")
public class LagouMpSpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(LagouMpSpringbootApplication.class, args);
}
}
编写测试用例LagouMpSpringbootApplicationTests:
package com.lagou;
import com.lagou.mapper.UserMapper;
import com.lagou.pojo.User;
import org.junit.jupiter.api.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.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
class LagouMpSpringbootApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void testSelect() {
List<User> all = userMapper.selectList(null);
System.out.println(all);
}
}
启动后,可能找不到结果,将日志文件删除
因为操作spring boot时,再操作日志,可能会将对应打印信息覆盖了,可能都不会显示
或者修改log4j.properties文件(甚至可以看到sql语句):
log4j.rootLogger=info,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
log4j.logger.com.lagou.mapper=TRACE
删除或者修改后,继续启动,那么就找到结果了
通⽤CRUD:
通过前⾯的学习,我们了解到通过继承BaseMapper就可以获取到各种各样的单表操作,接下来我们将详细讲解这些操作

public interface BaseMapper<T> extends Mapper<T> {
int insert(T entity);
int deleteById(Serializable id);
int deleteByMap(@Param("cm") Map<String, Object> columnMap);
int delete(@Param("ew") Wrapper<T> wrapper);
int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
int updateById(@Param("et") T entity);
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
T selectById(Serializable id);
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
T selectOne(@Param("ew") Wrapper<T> queryWrapper);
Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
}
插入操作(插入一条记录):
方法定义:
int insert(T entity);
在上面的spring boot项目中的LagouMpSpringbootApplicationTests测试类里面加上如下方法:
@Test
public void testInsert(){
User user = new User();
user.setName("哈哈哈");
user.setAge(18);
user.setEmail("zimu@jajaja");
int insert = userMapper.insert(user);
System.out.println(insert);
System.out.println("id值为:"+user.getId());
}
注意:如果编写代码时,没有提示,那么一般是该插件,在编译期不起作用,而在运行期起作用
而我们需要对应注解在编译期起作用,就需要点击如下:

点击右上角的Enable开头的即可,但通常情况下,插件基本都是在编译期可以起作用的
这个只是针对于部分不在编译期起作用的插件,注意即可
至此插入数据完毕
我们可以查看数据库,可以看到,数据已经写⼊到了数据库,但是,id的值却并不好
因为我们期望的是数据库自增长,而这里是MP生成了id的值写入到了数据库
如何设置id的生成策略呢?
MP支持的id策略:
通过ctrl+n查找IdType类:
package com.baomidou.mybatisplus.annotation;
public enum IdType {
AUTO(0),
NONE(1),
INPUT(2),
ID_WORKER(3),
UUID(4),
ID_WORKER_STR(5);
private final int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
那么如何操作自增策略呢,自然需要让我们的泛型那个类进行操作,使得初始化时进行设置,即:
package com.lagou.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
}
由于该策略使得不会添加字段,那么设置后,无论我们是否设置了对应的id值
都不会当成字段来添加,所以这也使得对应的数据库字段也通常要自增,这里可能操作了last_insert_id()函数
但也可能是sql语句得到,通常是前者,总体来说就是,使用该策略,就不参与添加字段
但要注意,该注解操作谁,那么该last_insert_id()函数返回的值,就是给谁的,即会覆盖原来有的值
所以也使得,对应表id的值,返回给了不是对应的变量,注意即可
至此,我们添加后,可以看到,数据库有数据了
@TableField:
在MP中通过@TableField注解可以指定字段的⼀些属性,常常解决的问题有2个:
1:对象中的属性名和字段名不⼀致的问题
一般情况下,类和表通常是驼峰关系(因为驼峰一般会操作加上"_",前面说过的特殊小写就是驼峰),那么默认是不需要操作的
如果类和表不是驼峰关系,即操作非驼峰或者设置了不驼峰的,就需要进行解决了,即操作@TableField注解
2:对象中的属性字段在表中不存在的问题
使用:
package com.lagou.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private Long id;
@TableField(select = false)
private String name;
private Integer age;
@TableField(value = "eMail")
private String mail;
@TableField(exist = false)
private String address;
}
至此,我们测试即可
更新操作:
在MP中,更新操作有2种,⼀种是根据id更新,另⼀种是根据条件更新
根据id进行更新:
方法定义:
int updateById(@Param("et") T entity);
测试:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testUpdateById(){
User user = new User();
user.setId(6l);
user.setAge(30);
int i = userMapper.updateById(user);
System.out.println(i);
}
进行测试,发现,修改了对应的值
根据条件进行更新:
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
测试:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testUpdate(){
User user = new User();
user.setAge(35);
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.eq("name","解决");
int i = userMapper.update(user,objectQueryWrapper);
System.out.println(i);
}
@Test
public void testUpdate2(){
UpdateWrapper<User> objectUpdateWrapper = new UpdateWrapper<>();
objectUpdateWrapper.eq("id","6").set("age",40);
int i = userMapper.update(null,objectUpdateWrapper);
System.out.println(i);
}
至此,进行测试,发现修改了对应的值
上面两种操作均可达到更新的效果,其中eq方法可以看成是操作where后面的条件的,而set方法是操作set后面的条件
关于wrapper更多的用法,以后可能会继续介绍
删除操作(根据id进行删除):
int deleteById(Serializable id);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testDeleteById(){
int i = userMapper.deleteById(6l);
System.out.println(i);
}
至此,进行测试,发现删除了对应的值
删除操作(根据map集合进行删除):
int deleteByMap(@Param("cm") Map<String, Object> columnMap);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testDeleteByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","哈哈哈");
map.put("age",35);
int i = userMapper.deleteByMap(map);
System.out.println(i);
}
至此,进行测试,发现删除了对应的值
删除操作(根据wrapper实体对象进行删除):
int delete(@Param("ew") Wrapper<T> wrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testDelete(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.eq("name","33").eq("age",18);
int i = userMapper.delete(objectQueryWrapper);
System.out.println(i);
}
至此,进行测试,发现删除了对应的值
删除操作(根据id进行批量删除):
int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testdeleteBatchIds(){
int i = userMapper.deleteBatchIds(Arrays.asList(17,18));
System.out.println(i);
}
至此,进行测试,发现删除了对应的值
这里说明一下,前面说的不能是null或者实体类的,那么就不能是null或者实体类
大概是语法问题,比如in后面的括号是空的或者不识别,或者框架操作问题,操作了异常
而没有说明或者说明可以是null或者实体类的,则基本都可以是null或者实体类,因为具体是看条件的
即null基本代表没有条件(比如在查询时,就相当于查询所有了,但是更新的话,有些需要set,那么可能会报错,语法错误)
因为sql语句中"update 表名"后面基本需要指定set才可
实体类代表有他的条件
但要注意空实体类操作默认的值是否符合sql语法(会操作get方法),如根据id进行修改)
或者字段的是否添加(不操作get方法),如根据条件进行修改,等等
注意即可,后面基本也是如此
查询操作:
MP提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分页查询等操作
查询操作(根据id进行查询):
T selectById(Serializable id);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testSelectById(){
User user = userMapper.selectById(15);
System.out.println(user);
}
至此,进行测试,发现查询了对应的值
查询操作(根据id进行批量查询):
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testSelectBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(4, 5));
System.out.println(users);
}
至此,进行测试,发现查询了对应的值
查询操作(根据map集合进行查询):
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testSelectByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
List<User> users = userMapper.selectByMap(map);
System.out.println(users);
}
至此,进行测试,发现查询了对应的值
到这里,大概有个对Mybatis-plus的一定了解了,可以知道,我们并不需要编写对应的xml配置文件
他自动的帮我们进行了相关的操作,也就相当于帮我们写了xml文件了,这使得方便许多
查询操作(根据entity条件,查询⼀条记录):
T selectOne(@Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testSelectOne(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.eq("name","Jack");
User user = userMapper.selectOne(objectQueryWrapper);
System.out.println(user);
}
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,查询总记录数,即数量):
Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testSelectCount(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18");
int user = userMapper.selectCount(objectQueryWrapper);
System.out.println(user);
}
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,查询满足条件的所有记录,这里并不是记录数量):
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testSelectList(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18");
List<User> user = userMapper.selectList(objectQueryWrapper);
System.out.println(user);
}
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,查询满足条件的所有记录,并将根据条件的查询的结果放入map集合中):
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testSelectMaps(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18");
List<Map<String, Object>> user = userMapper.selectMaps(objectQueryWrapper);
for(Map m : user) {
System.out.println(m);
}
}
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,将满足条件的所有记录中,根据顺序:操作策略者-id数-查询条件的第一个,来进行返回数据):
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testSelectObjs(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18");
List<Object> list = userMapper.selectObjs(objectQueryWrapper);
for(Object m : list) {
System.out.println(m);
}
}
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,查询全部记录,并可以操作分页):
IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
在测试之前,我们需要配置分页插件:
在lagou包下,创建config包,并在里面创建MybatisPlusConfig类:
package com.lagou.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
}
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testSelectPage(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18");
Page<User> page = new Page(1,2);
IPage<User> iPage = userMapper.selectPage(page, objectQueryWrapper);
System.out.println("总条数:" + iPage.getTotal());
System.out.println("总页数:" + iPage.getPages());
System.out.println("当前页的分页数据:" + iPage.getRecords());
}
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,查询全部记录,并可以翻页,只是将map进行接收数据了,而不是类):
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testSelectMapsPage(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18");
Page<User> page = new Page(1,2);
IPage<Map<String, Object>> mapIPage = userMapper.selectMapsPage(page, objectQueryWrapper);
System.out.println("总条数:" + mapIPage.getTotal());
System.out.println("总页数:" + mapIPage.getPages());
System.out.println("当前页的分页数据:" + mapIPage.getRecords());
}
至此,进行测试,发现查询了对应的值
SQL注入的原理(不是对应的进入数据库的手段,而是mybatis-plus为什么有对应的sql注入到MappedStatement里面的解析):
前⾯我们已经知道,MP在启动后会将BaseMapper中的⼀系列的方法注册到meppedStatements(也就是MappedStatement)中
只是这里加上s,是可能对应的变量是保存所有的MappedStatement的,所以也可以这样说
那么究竟是如何注入的呢,流程又是怎么样的,下面我们将⼀起来分析下:
在MP中,ISqlInjector负责SQL的注入工作,它是⼀个接口,AbstractSqlInjector是它的实现类,实现关系如下:

回到如下:

一般会按照前面17个的顺序,依次进行执行对应的方法,但这里我们直接的点击selectById:
配置:
在MP中有大量的配置,其中有⼀部分是Mybatis原生的配置,另⼀部分是MP的配置
MP配置详情:https://mybatis.plus/config/
下面我们对常⽤的配置做讲解:
基本配置:
configLocation:
MyBatis 配置文件位置,如果有单独的 MyBatis 配置,请将其路径配置到 configLocation 中
MyBatis Configuration 的具体内容请参考MyBatis 官方文档
为了进行测试,我们继续操作前面的分页,我们在资源文件夹下,创建mybatis-config.xml文件(全局配置文件):
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
plugin>
plugins>
configuration>
一般来说,我们需要操作该配置,所以我们需要回到对应的项目的配置:
若操作Spring Boot项目,那么配置如下:
#加载全局配置文件
mybatis-plus.config-location = classpath:mybatis-config.xml
#那么运行Spring Boot时,会帮我们进行加载,并读取配置,从而操作,相当于下面的配置
#因为这里Spring Boot与Mybatis-plus是整合的,所以可以操作得到,相当于下面的配置
若操作Spring项目,那么对应的Spring的配置如下:
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
bean>
至此分页操作计算成功,可以进行测试,我们将原来的配置分页插件的类的关键注释给注释掉,发现,的确也是可以计算的
mapperLocations:
MyBatis Mapper 所对应的 XML ⽂件位置,如果您在 Mapper 中有⾃定义方法(XML 中有⾃定义实现)
需要进行该配置,告诉 Mapper 所对应的 XML ⽂件位置
在操作之前,我们回到UserMapper接口,加上如下方法:
public User findById(Integer id);
再在资源文件夹下创建mapper目录,然后创建UserMapper.xml文件:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lagou.mapper.UserMapper">
<select id="findById" resultType="com.lagou.pojo.User">
select * from user where id = #{id}
select>
mapper>
一般情况下,我们在操作时,需要将xml的对应的目录与类相对应,但是由于这里是我们来指定,所以目录可以不对应
若操作Spring Boot项目,那么配置如下:
#加载映射配置文件
mybatis-plus.mapper-locations = classpath*:mapper/*.xml
#classpath*也会扫描其他的classes目录(比如其他的jar包),而classpath只扫描当前的classes目录
若操作Spring项目,那么对应的Spring的配置如下:
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="mapperLocations" value="classpath*:mapper/*.xml"/>
bean>
他们这样的指定,就使得我们调用接口时,从原来的,我们根据接口的地址,来使得扫描配置
变成了手动的配置,即我们手动的扫描配置,即就不需要对应的接口地址了
且手动的配置会覆盖默认的根据接口的地址,即优先操作手动的配置
那么Spring Boot有没有操作操作接口的地址的,答:有
我们将对应的配置修改成如下即可:
#使得扫描接口,进而扫描配置
mybatis-plus.mapper-locations = classpath*:com.lagou.mapper
#对应的单独的mybatis,也可以是这样,测试过了,首先优先看对应的mapper是否是类,是类,则操作该类,否则操作该包
但是在操作Spring时,对应的基本只能是具体的xml,且扫描也不能操作全局配置里面的扫描了
只能是Spring配置里进行扫描(且只能是操作接口地址,而不能是具体的xml)
即工厂那里只能操作具体xml,而扫描那里只能操作具体接口
(即所在的包里面包含即可,但不能是最上层和指定的文件,其他没有说明的路径,基本也是如此,如@MapperScan注解
有说明的路径,因为说明了,那么只能是扫描当前包里面的,不会扫描子包里面的,虽然可能并没有印象,但也是有的,比如* 和* * 的对比,其中单个 * 就是没有子包的说明,而两个 * 就有子包的说明
当然可能有些框架的地方也操作了类似于单个 * 的这样的说明,比如:
< property name=“basePackage” value=“com.lagou.dao”>< /property>,这个指定的com.lagou.dao包也是没有操作子包的
只操作当前com.lagou.dao包下的接口,一般不能是类,否则可能会报错,这是mybatis映射底层规定的,具体看61章博客
其他的不操作子包的地方,具体可以百度查看)
使得分开了,而Spring Boot操作时,自然也是这样
只是只能使得只操作一个,根据结果来的,没有覆盖操作
注意:无论什么情况,对应的mybatis的全局扫描必须有
如全局配置文件的< mappers >标签或者@MapperScan的注解以及Spring操作的扫描等等
分别操作mybatis,spring boot,spring整合的,因为他们使得可以操作注入
其中spring整合的,操作对应的单独注解时,却不可以,因为我们注入只能是一个,因为spring整合的只是增加信息而已
这些Spring Boot只是增强而已,所以要注意,因为是mybatis-plus的操作
实际上只要不是全局的配置,那么基本都是增强的操作,无论是mybatis和spring boot整合还是mybatis-plus和spring boot整合等等
而正是如此,所以对应的全局扫描,需要有相同名称和路径的配置,否则会报错
但无论是spring boot操作的还是spring操作的,都是对应的同样的设置,只是加载方式不同而已
Maven 多模块项目的扫描路径通常需以classpath*: 开头 (即加载多个 jar 包下的 xml文件)
通常用在Spring Boot里面,因为Spring Boot基本是使用依赖的,所以通常使用这个
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void findById(){
User byId = userMapper.findById(1);
System.out.println(byId);
}
至此,返回数据则操作成功
typeAliasesPackage:
MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML ⽂件中可以直接使用类名
而不用使用全限定的类名(即 XML 中调⽤的时候不用包含包名)
若操作Spring Boot项目,那么配置如下:
# 起别名
mybatis-plus.type-aliases-package = com.lagou.pojo
若操作Spring项目,那么对应的Spring的配置如下:
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="typeAliasesPackage" value="com.baomidou.mybatisplus.samples.quickstart.entity"/>
bean>
当起别名后,我们写的配置,中对应的名称可以直接写类了,具体看61章博客
至此可以这样了:
<select id="findById" resultType="User">
select * from user where id = #{id}
select>
进行测试,发现,的确可以,可以删除对应的配置进行再次测试
发现启动时报错(会检查的,或者说初始化报错,即允许时报错,而不是编译期)
至此我们发现,实际上也就是操作对应mybatis原来的全局配置
只是在spring boot中,是操作他的配置文件,而spring整合的,则是操作整合的配置文件
进阶配置:
本部分(Configuration)的配置大都为 MyBatis 原生支持的配置,这意味着您可以通过 MyBatis XML 配置文件的形式进行配置
mapUnderscoreToCamelCase:
是否开启⾃动驼峰命名规则(camel case)映射
即从经典数据库列名 a_column(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
注意:
此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQL 的 select body(查询身体)
且默认值是true,即默认开启的,其中他不只是操作了表(数据库表)到类(前面说明过了,如果是false,那么自然他们就是与mybatis类似的样子,满足sql,满足类设置替换等等,61章博客有说明)
而也操作了我们生成时的类到表(在mybatis-plus中操作的,比如特殊小写,为false自然也不会操作,与mybatis类似)
而不是只有mybatis的表到类,就如前面说的只是去除下划线而已,当然也可以说是特殊小写
只是字段不区分大小写而已,所以只是去除下划线
所以如果您的数据库命名符合规则,则无需使用@TableField 注解指定数据库字段名
若操作Spring Boot项目,那么配置如下:
#关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
mybatis-plus.configuration.map-underscore-to-camel-case=false
#也操作了类到表,不只是表到类
#所以设置为false时,那么对应的就不会特殊小写了,即aGe,就是aGe,是什么就是什么了,而不是a_ge了
#那么既然不能和mybatis-plus.config-location同时存在,也就使得不能操作全局配置文件了
#那么为什么不能共存呢,因为该配置是操作mybatis-plus.config-location里面的
#即在mybatis-plus.config-location里面,我们操作spring时,是写在里面的,所以看起来是共存了
#但这里的共存,很明显是可以写两个的,所以如果对应的mybatis-plus.config-location配置里也有该配置
#即mybatis-plus.configuration.map-underscore-to-camel-case配置
#那么就可能会覆盖或者冲突,所以为了不出现这样的操作,则直接报异常即可
#如果我们需要对应的mybatis-plus.config-location配置
#我们在里面写mybatis-plus.configuration.map-underscore-to-camel-case配置即可,而不是直接的配置
操作Spring项目,那么对应的Spring的配置如下:
<settings>
<setting name="mapUnderscoreToCamelCase" value="false"/>
settings>
进行测试后,发现,的确没有操作特殊小写了
cacheEnabled:
全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true(原mybatis也默认是true)
操作Spring项目,那么对应的Spring的配置如下:
mybatis-plus.configuration.cache-enabled=false
具体可以看63章博客,因为并没有什么好说明的
DB 策略配置:
idType:
一般默认是ID_WORKER(3),全局唯⼀ID (idWorker),这个是默认的策略
我们可以设置全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置
若操作Spring Boot项目,那么配置如下:
#注意:配置后,默认使得类(基本是所有的类,但要注意是操作的泛型类)里面的id数(id数:表示该id忽略大小写)
#是设置了@TableId(type = IdType.AUTO)配置
mybatis-plus.global-config.db-config.id-type=auto
操作Spring项目,那么对应的Spring的配置如下:
<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>
至此可以进行测试,发现,操作之前的添加时,还是操作自增的,而不是使用时间戳生成的
那么@TableId(type = IdType.AUTO)和上面的设置,谁优先呢,答:@TableId(type = IdType.AUTO)优先
即若其他字段有@TableId(type = IdType.AUTO),那么以其他字段为主,所以返回的信息在其他字段那里了
tablePrefix:
表名前缀,全局配置后可省略根据表名操作的前缀,使得不需要@TableName()配置了
若操作Spring Boot项目,那么配置如下:
#给表名加上前缀"tb_"
mybatis-plus.global-config.db-config.table-prefix=tb_
操作Spring项目,那么对应的Spring的配置如下:
<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>
注意:对应的名称是有顺序的,即以表USer为例子,且操作了驼峰
那么有如下,没有@TableName()注解和没有设置前缀,那么表名是u_ser,有前缀tB_,没有注解,那么表名是tB_u_ser
即前缀不参与驼峰,若有注解(名称是user),那么无论是否有前缀,都操作注解,即表名是user
所以注解>前缀+类名>类名
至此,可以进行测试,我们经过测试,发现的确如此
条件构造器:
在MP中,Wrapper接⼝的实现类关系如下:

可以看到,AbstractWrapper和AbstractChainWrapper是重点实现,接下来我们重点学习AbstractWrapper以及其子类
说明:
QueryWrapper(是AbstractWrapper类的子类),LambdaQueryWrapper(是AbstractLambdaWrapper类的子类)
UpdateWrapper(是AbstractWrapper类的子类),LambdaUpdateWrapper(是AbstractLambdaWrapper类的子类)
他们的父类(有多个父类)作用:
用于生成 sql 的 where 条件或者set条件(部分),entity 属性也用于生成 sql 的 where 条件
其中AbstractWrapper类和AbstractLambdaWrapper类是Wrapper类的子类
我们主要操作AbstractWrapper类:
注意:entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
官网文档地址:https://mybatis.plus/guide/wrapper.html
可能是访问不了的,但并不需要在意,我们访问https://mp.baomidou.com/即可
allEq(基本是AbstractWrapper父类里面的操作):
说明:
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法(多个方法):
@Test
public void testAllEq(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
objectQueryWrapper.allEq(map);
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
@Test
public void testAllEq2(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
objectQueryWrapper.allEq(map);
map.put("agee",null);
objectQueryWrapper.allEq(map);
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
@Test
public void testAllEq3(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
objectQueryWrapper.allEq(map,false);
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
@Test
public void testAllEq4(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
objectQueryWrapper.allEq(map);
map.put("agee",null);
objectQueryWrapper.allEq(false,map,true);
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
@Test
public void testAllEq5(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
objectQueryWrapper.allEq(false,map,true);
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
至此,三个参数测试完毕
继续说明:
allEq(BiPredicate<R, V> filter, Map<R, V> params)
allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testAllEq6(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
objectQueryWrapper.allEq((k,v)->!k.equals("name"),map,true);
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
至此,过滤参数测试完毕
基本比较操作(基本是AbstractWrapper父类里面的操作):
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 , …)
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testWrapper(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.eq("name","哈哈")
.ge("age","20")
.in("name","解决","拒绝");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
至此大致操作完毕,剩余的就不进行测试了,因为并没有意义,但为了看结果,那么看如下:
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testWrapper2(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.eq("name","1")
.ne("name","2")
.gt("name","3")
.ge("name","4")
.lt("name","5")
.le("name","6")
.between("name","7","8")
.notBetween("name","9","10")
.in("name","11","12")
.notIn("name","13","14");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
至此,总的合并操作完毕
模糊查询:
like:LIKE ‘%值%’
例:like(“name”, “王”)—>name like ‘%王%’
notLike:NOT LIKE ‘%值%’
例:notLike(“name”, “王”)—>name not like ‘%王%’
likeLef:LIKE ‘%值’
例:likeLeft(“name”, “王”)—>name like ‘%王’
likeRight:LIKE ‘值%’
例:likeRight(“name”, “王”)—>name like ‘王%’
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testWrapper3() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.like("name", "jaja")
.notLike("name", "jj")
.likeLeft("name", "aa")
.likeRight("name", "bb");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
至此操作完毕
排序:
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
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testWrapper4() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.orderBy(true,false,"name","name");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
总体操作:
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testWrapper5() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.orderBy(true,false,"name","name")
.orderByAsc("name")
.orderByDesc("name");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
至此操作完毕
逻辑查询:
or:拼接 OR
主动调用or表示紧接着下⼀个方法不是用and连接(不调用or则默认为使用and连接)
and:AND 嵌套
例:and(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>and (name = ‘李白’ and status <> ‘活着’)
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法(多个):
@Test
public void testWrapper6() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.eq("name","jack").or().eq("name","jj");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
@Test
public void testWrapper7() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.eq("name","jack").and(i->i.eq("name","j"));
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
至此操作完毕
select:
在MP查询中,默认查询所有的字段,如果有需要也可以通过select方法进行指定字段
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testWrapper8() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.eq("name","jack").select("namee");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
至此操作完毕
ActiveRecord:
ActiveRecord(简称AR)⼀直广受动态语⾔( PHP 、 Ruby 等)的喜爱,而 Java 作为准静态语言
对于 ActiveRecord 往往只能感叹其优雅,但是MP(Mybatis-Plus)却可以操作,所以我们也在 AR 道路上进行了⼀定的探索
什么是ActiveRecord?
ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出
遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性
配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,⽽且简洁易懂
ActiveRecord的主要思想是:
每⼀个数据库表对应创建⼀个类,类的每⼀个对象实例对应于数据库中表的⼀行记录,通常表的每个字段在类中都有相应的Field
ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD
ActiveRecord是⼀种领域模型(Domain Model),封装了部分业务逻辑
开启AR之旅:
在MP中,开启AR⾮常简单,只需要将实体对象继承Model即可
public class User extends Model implements Serializable {
测试用例:
到测试文件里的lagou包下,创建TestAR类(包括多个测试方法):
package com.lagou;
import com.lagou.pojo.User;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
class TestAR {
@Test
public void testARSelectById(){
User user = new User();
user.setId(12l);
User user1 = (User) user.selectById();
System.out.println(user1);
}
@Test
public void testARInsert(){
User user = new User();
user.setName("哈哈");
user.setAge(18);
user.setMail("建设局");
boolean user1 = user.insert();
System.out.println(user1);
}
@Test
public void testARUpdate(){
User user = new User();
user.setName("哈哈2");
user.setAge(18);
user.setMail("建设局2");
boolean user1 = user.insertOrUpdate();
System.out.println(user1);
System.out.println(user.getId());
}
@Test
public void testARUpdateById(){
User user = new User();
user.setId(11l);
user.setName("哈哈233");
user.setAge(18);
user.setMail("建设局23");
boolean user1 = user.updateById();
System.out.println(user1);
System.out.println(user.getId());
}
@Test
public void testARDelete(){
User user = new User();
boolean user1 = user.deleteById(12);
System.out.println(user1);
System.out.println(user.getId());
}
@Test
public void testARFindById(){
User user = new User();
QueryWrapper<Object> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.ge("age",20);
List user1 = user.selectList(objectQueryWrapper);
System.out.println(user1);
}
}
上面只是给出部分的操作,因为大多数与MP的操作类似,所以就给出主要的方法了
插件:
mybatis的插件机制:
MyBatis 允许你在已映射语句执行过程中的某⼀点进行拦截调用,默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
我们看到了可以拦截Executor接口的部分方法,比如update,query,commit,rollback等方法,还有其他接口的⼀些方法等
总体概括为:
1:拦截执行器的方法
2:拦截参数的处理
3:拦截结果集的处理
4:拦截Sql语法构建的处理
拦截器示例:
在lagou包下(没有说明测试文件的,一般都是java资源文件里面,所以注意即可),创建MyInterceptor类并实现Interceptor接口:
package com.lagou;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import java.util.Properties;
@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 {
System.out.println(1);
return invocation.proceed();
}
@Override
public Object plugin(Object o) {
System.out.println(22);
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
System.out.println(3);
}
}
该插件写好后,若是操作注入的方式,即类的方式,那么如下:
注入到Spring容器(简称类操作):
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}
通过xml配置(前提是可以被读取到,操作的全局配置):
<plugins>
<plugin interceptor="com.lagou.MyInterceptor">plugin>
plugins>
至此,我们在执行时,可以看到,每次查询得到一条数据,都会打印一个22,所以的确是拦截,但这里并不深入了解
若要深入了解,可以百度,查看对应的参数信息
执行分析插件:
在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作
注意:该插件仅适用于开发环境,不适用于生产环境,因为使用他会损耗多数性能,但开发中,可以使用,进行测试等等
性能分析插件:
性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执⾏时间,超过时间会抛出异常
该插件只用于开发环境,不建议生产环境使用,与执行分析插件一样,损耗多数性能
配置:
注入到Spring容器(简称类操作):
@Bean
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
通过xml配置(前提是可以被读取到,操作的全局配置):
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
<property name="maxTime" value="100"/>
<property name="format" value="true"/>
plugin>
plugins>
对应的该插件是MP所拥有的,注意即可
先操作 性能分析插件:
回到LagouMpSpringbootApplicationTests类,运行testSelect方法,可以看看打印的结果
我们可以明显的看到,有如下:
那我们的执行分析插件的作用是什么呢:
我们回到LagouMpSpringbootApplicationTests类,找到testUpdate方法,我这里如下:
@Test
public void testUpdate() {
User user = new User();
user.setAge(35);
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.eq("name", "哈哈哈");
int i = userMapper.update(user, objectQueryWrapper);
System.out.println(i);
}
我们运行一下,发现可以运行,那么接下来修改一下该方法,修改如下:
@Test
public void testUpdate() {
User user = new User();
user.setAge(35);
int i = userMapper.update(user, null);
System.out.println(i);
}
我们直到,在没有条件的情况下,就是操作所有了,但这时,我们运行,会报错,也就是说对应的执行分析插件是拦截操作全部
即使得报错,防止我们失误的操作,造成的后果
这时,我们注释掉对应的类配置,发现,可以执行了,通过我的测试,他主要是用来防止,我们操作"删改"操作的全部类型的操作
这样可以有效的提高安全性,而正是因为对应的类配置复杂,所以,一般是没有xml配置的,可能有(可以百度看看)
乐观锁插件:
对于乐观锁,与之相对应的就是悲观锁,实际上乐观锁并不能称为锁,因为基本是没有锁的实现
只是通过程序使得降低操作相同数据的方式,如操作版本号
但在多访问的情况下,如果对应数据库没有唯一性,那么可能在判断版本号之后,会出现共同操作,并也会有多次失败的情况
但这种情况基本不会出现,因为数据库对字段来说,基本是同步的(因为我们通常会设置主键自增,所以是基本),即最后的变化基本只能是一个人
如添加(因为主键,一般主键无论多大的并发他都不会相同,特别是自增,可以认为他内部有锁,数据库在不同的事务下会自动的加锁,如增删改是写锁,而查询是读锁,在96章博客会说明,所以导致我们操作相同数据库时,会出现唯一性)和删除(因为没有,同样操作锁,所以有唯一性),即也基本有唯一性
但是虽然有唯一性,但可能也会因为高并发下,会出现多次执行失败的情况(比如删除),而多次的失败,自然会浪费更多资源
这时就需要加上锁了,这样也就称为悲观锁,使得一个数据只能被一人操作
这里我们就只说明乐观锁
主要适用场景:
意图:
当要更新⼀条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
假设,数据库有个字段,为version
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion,oldVersion原来的version
newVersion 新的version,通常是操作加1,也有可能是随机的(除了自己)
这时,在唯一性的情况下,其他人来更新数据后,我们的where version = oldVersion判断,基本不会操作,因为被改变了
所以一般会出现更新失败,即如果version不对,就更新失败(并不是报错,只是,没有更新)
图解:

插件配置:
注入到Spring容器:
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
实际上我们也可以使用xml完成注入的操作,如下
通过xml配置(前提是可以被读取到,这里并不是全局配置了,实际上这两个是一样的,都是操作容器)
虽然全局是被访问得到:
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
全局的配置文件:
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor">
plugin>
plugins>
注解实体字段:
需要为实体字段添加@Version注解
在这之前:
第⼀步,为表添加version字段,并且设置初始值为1,可以手动的去表里进行添加,也可以使用语句添加,具体sql语句如下:
ALTER TABLE USER
ADD COLUMN VERSION INT(10) NULL AFTER email;
UPDATE USER SET VERSION = 1
第二步,为User实体对象添加version字段,并且添加@Version注解:
@Version
private Integer version;
我们回到LagouMpSpringbootApplicationTests类,找到testUpdateById方法,我这里如下:
@Test
public void testUpdateById() {
User user = new User();
user.setId(6l);
user.setAge(30);
user.setName(null);
int i = userMapper.updateById(user);
System.out.println(i);
}
将该方法进行修改,修改成如下:
@Test
public void testUpdateById() {
User user = new User();
User user1 = userMapper.selectById(3);
user.setId(3l);
user.setName("哈哈哈");
user.setVersion(user1.getVersion());
int i = userMapper.updateById(user);
System.out.println(i);
System.out.println(user);
}
我们执行一下,看看对应的sql语句,我们发现执行成功,sql语句如下:
至此乐观锁操作完毕
特别说明:
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime,因为需要操作下面的加1
即整数类型下 newVersion = oldVersion + 1,当是其他类型,如String类型,那么newVersion = oldVersion,而不是加1
其中,无论是什么类型,newVersion 会回写到 entity 中,即由原来的设置原version值,变成了设置新的version值
但仅支持 updateById(entity) 与 update(entity, wrapper) 方法,实际上也只有这两个方法
那么为什么只操作更新呢,因为添加和删除,他们就算是有多次操作,那么也基本是只能一个成,如主键,或者没有
而更新,才是异步中最主要的问题
在 update(entity,wrapper)方法下,wrapper 不能复用,若使用wrapper
那么会多出一个version的判断,使得新的version总体加上了2,测试就知道了,所以说真正的方法,只有 updateById(entity)
因为update(entity, wrapper)多判断,基本使得不会更新成功,即虽然支持,但成不成功,就不知道了
Sql 注入器:
我们已经知道,在MP中,通过AbstractSqlInjector将BaseMapper中的方法注入到了Mybatis容器,且不用我们写对应的配置文件
这样这些方法才可以正常执行
那么,如果我们需要扩充BaseMapper中的方法,又该如何实现呢?
下面我们以扩展findAll方法为例进行学习
首先在lagou包下的mapper包下创建MyBaseMapper接口并继承BaseMapper接口:
package com.lagou.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;
import java.util.List;
public interface MyBaseMapper<T> extends BaseMapper<T> {
List<User> findAll();
}
然后再lagou包下,创建injector包,并再该包下创建MySqlInjector类:
package com.lagou.injector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;
import java.util.List;
public class MySqlInjector extends AbstractSqlInjector {
@Override
public List<AbstractMethod> getMethodList() {
return null;
}
}
在这之前,我们首先进入AbstractSqlInjector类里面:
即再在injector包下,创建FindAll类并继承AbstractMethod类:
package com.lagou.injector;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
public class FindAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo
tableInfo) {
String sql = "select * from " + tableInfo.getTableName();
SqlSource sqlSource = this.languageDriver.createSqlSource(
this.configuration, sql, modelClass);
return this.addSelectMappedStatement(
mapperClass, "findAll", sqlSource, modelClass, tableInfo);
}
}
创建好后,我们需要进行添加,回到MySqlInjector类,对应的代码如下:
注意:如果直接继承AbstractSqlInjector的话,原有的BaseMapper中的方法将失效
若要有效,我们可以选择继承DefaultSqlInjector进行扩展,或者加上对应的返回,后面会说明,这里就不继承了
package com.lagou.injector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;
import java.util.ArrayList;
import java.util.List;
public class MySqlInjector extends AbstractSqlInjector {
@Override
public List<AbstractMethod> getMethodList() {
List<AbstractMethod> methodList = new ArrayList<>();
methodList.add(new FindAll());
return methodList;
}
}
若要继承,则是:
package com.lagou.injector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.methods.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList() {
List<AbstractMethod> methodList = super.getMethodList();
methodList.add(new FindAll());
return methodList;
}
}
编写好后,要想生效,我们需要进行注入操作
回到MybatisPlusConfig类。添加如下代码:
@Bean
public MySqlInjector mySqlInjector(){
return new MySqlInjector();
}
接下来,我们将UserMapper接口由原来的继承BaseMapper接口,变成继承MyBaseMapper接口:
public interface UserMapper extends BaseMapper<User> {
public interface UserMapper extends MyBaseMapper<User> {
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
@Test
public void testFindAll(){
List<User> all = userMapper.findAll();
System.out.println(all);
}
至此我们扩展完毕
自动填充功能:
有些时候我们可能会有这样的需求,插入或者更新数据时,希望有些字段可以自动填充数据
比如密码、 version等,在MP中提供了这样的功能,可以实现自动填充
添加@TableField注解:
@TableField(fill = FieldFill.INSERT)
private Integer version;
FieldFill提供了多种模式选择:
package com.baomidou.mybatisplus.annotation;
public enum FieldFill {
DEFAULT,
INSERT,
UPDATE,
INSERT_UPDATE;
private FieldFill() {
}
}
但我们这里也只是进行了指定,具体操作需要我们自己编写,即需要填充什么:
在lagou包下创建handler包,并在该包下创建MyMetaObjectHandler类并实现MetaObjectHandler接口:
package com.lagou.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
Object version = getFieldValByName("version", metaObject);
if(null == version){
setFieldValByName("version",1,metaObject);
}
System.out.println(metaObject.getOriginalObject());
}
@Override
public void updateFill(MetaObject metaObject) {
}
}
这里需要注入,使得可以被使用才可操作
测试用例:
我们回到LagouMpSpringbootApplicationTests类,找到testInsert方法:
@Test
public void testInsert() {
User user = new User();
user.setName("哈哈哈");
user.setAge(18);
user.setMail("zimu@jajaja");
int insert = userMapper.insert(user);
System.out.println(insert);
System.out.println("id值为:" + user.getId());
}
修改成如下:
@Test
public void testInsert() {
User user = new User();
user.setName("哈哈哈w");
user.setAge(18);
user.setMail("zimu@jajaja");
int insert = userMapper.insert(user);
System.out.println(insert);
System.out.println("id值为:" + user.getId());
}
至此操作填充完毕
逻辑删除:
开发系统时,有时候在实现功能时,删除操作需要实现逻辑删除,所谓逻辑删除就是将数据标记为删除
而并非真正的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到
这样做的目的就是避免数据被真正的删除,因为对应的删除的数据可能也是有作用的,所以通常并不会真正的删除数据
MP就提供了这样的功能,方便我们使用,接下来我们⼀起学习下:
修改表结构:
为user表增加deleted字段,用于表示数据是否被删除, 1代表删除, 0代表未删除
对应的sql语句(当然,也可以手动的添加):
ALTER TABLE USER
ADD COLUMN deleted INT(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER VERSION
同时,也修改User实体,增加deleted属性并且添加@TableLogic注解:
@TableLogic
private Integer deleted;
配置:
Spring Boot的配置:
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0
测试用例:
我们回到LagouMpSpringbootApplicationTests类,找到testDeleteById方法:
@Test
public void testDeleteById() {
int i = userMapper.deleteById(7);
System.out.println(i);
}
至此操作逻辑删除完毕
代码生成器:
AutoGenerator 是 MyBatis-Plus 的代码生成器
通过 AutoGenerator 可以快速⽣成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率
创建工程:
对应的依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.1.1version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.1.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
对应的目录:

对应的MysqlGenerator类:
package com.lagou.generator;
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.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class MysqlGenerator {
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 + "!");
}
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("lagou");
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.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
PackageConfig pc = new PackageConfig();
pc.setModuleName(scanner("模块名"));
pc.setParent("com.lagou.mp.generator");
mpg.setPackageInfo(pc);
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
}
};
List<FileOutConfig> focList = new ArrayList<>();
focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/lagou-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.setEntityLombokModel(true);
strategy.setInclude(scanner("表名"));
strategy.setSuperEntityColumns("id");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
测试时,对应的模块名,就是controller的首路径,表名必须要与数据库对应,否则只有包,而没有类,因为没有数据表
但有对应的数据表时,对应的类与输入的表名有关
运行后,会发现,多出几个包了,里面包含了你输入的表信息,至此操作完毕,对应的代码了解即可,并不需要手动编写
对应输入的表名变成类时会根据驼峰反向操作,注意即可,输入的表名都要小写,否则只有包
多次运行,如果没有会添加,有则不会覆盖,而使用插件时,会提示是否覆盖
大概是因为代码基本手动(不在物理中断的情况下)中断不了,所以不操作覆盖,只操作添加吧
具体的目录如下:

即mapper,entity,service,controller都编写完毕
MybatisX 快速开发插件:
MybatisX 是⼀款基于 IDEA 的快速开发插件,为效率而生
安装方法:打开 IDEA,进⼊ File -> Settings -> Plugins,点击Marketplace(市场),输⼊MybatisX 搜索并安装
功能:
Java接口 与 XML 调回跳转
Mapper 方法自动生成 XML
虽然88章博客安装的mybatis插件也可以这样,只是没有图标,且该插件要优先

生成标签后,对应的sql还是需要自己编写
比如:
至此操作完毕