MybatisPlus

MybatisPlus介绍

官方网站:https://mp.baomidou.com/

Mybatis-Plus是一个Mybatis的增强工具,在Myabtis的基础上只做增强不做改变,为简化开发,提高效率存在。

特性:

架构:

MybatisPlus快速入门

环境准备

Mybatis-plus3.0版本是基于JDK8,提供了lambda形式调用,要安装JDK8+,Maven。

复制代码
<dependency>
 <groupId>com.baomidou</groupId>
 <artifactId>mybatis-plus-boot-starter</artifactId>
 <version>3.4.0</version>
</dependency>

<dependency>
 <groupId>com.baomidou</groupId>
 <artifactId>mybatis-plus</artifactId>
 <version>3.4.0</version>
</dependency>

在引入mybatisPlus之后不需要再引入mybatis和mybatis-spring,不如会出现依赖问题。

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等⽅法,还有其他接⼝
    的⼀些⽅法等。
    总体概括为:
  5. 拦截执⾏器的⽅法
  6. 拦截参数的处理
  7. 拦截结果集的处理
  8. 拦截Sql语法构建的处理

对于Mybatis整合MP有三个方法:Mybatis+MP,Spring+Mybatis+MP,SpringBoot+Mybatis+MP。

创建数据库表User

复制代码
-- 创建测试表
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, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

配置项目依赖

复制代码
<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>com.guslegend</groupId>
  <artifactId>MybatisPlusQuickStart</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
    <java.version>11</java.version>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>

    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.4.0</version>
    </dependency>

    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus</artifactId>
      <version>3.4.0</version>
    </dependency>
    <!--mysql驱动坐标-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.0.11</version>
    </dependency>
    <!--单元测试坐标-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!--⽇志坐标-->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.6.4</version>
    </dependency>
    <!--Lombok 坐标-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.10</version>
    </dependency>

  </dependencies>
</project>

Myabtis+MP

创建子Module

复制代码
<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>com.guslegend</groupId>
        <artifactId>MybatisQucickStart</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>simple</artifactId>

</project>

配置日志文件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

原生Myabtis查询用户信息

实体类

复制代码
@Data
@TableName("user")
public class UserDO {

    private Long id;

    private String name;

    private Integer age;

    private String email;
}

mapper

复制代码
public interface UserMapper {

    List<UserDO> findAll();
}

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

    <properties resource="jdbc.properties"></properties>

    <typeAliases>
        <typeAlias alias="UserDO" type="com.guslegend.model.UserDO"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <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>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>

</configuration>

public class MPTest {

    @Test
    public void test1() throws Exception{
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<UserDO> userDOList = userMapper.findAll();
        for (UserDO userDO : userDOList){
            System.out.println(userDO);
        }
    }
}

Mybatis+MP查询用户信息

我们首先要将UserMapper集成BaseMapper,使用BaseMapper里面的方法

复制代码
public interface UserMapper extends BaseMapper<UserDO> {

}

    @Test
    public void test2() throws Exception{
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new
                MybatisSqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        List<UserDO> userDOList = userMapper.selectList(null);
        for (UserDO userDO : userDOList){
            System.out.println(userDO);
        }

    }

注意:MybatisSqlSessionFactoryBuilder(),MP里面使用的是这个工厂。

SpringBoot+Mybatis+MP

配置maven依赖

复制代码
<?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>
        <!-- 降级到 3.2.x 版本(与 MyBatis-Plus 3.5.7 兼容性最优) -->
        <version>3.2.10</version>
        <relativePath/>
    </parent>

    <groupId>com.guslegend</groupId>
    <artifactId>MybatisPlus</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>MybatisPlus</name>

    <properties>
        <java.version>17</java.version>
        <lombok.version>1.18.38</lombok.version>
        <mybatis-plus.version>3.5.7</mybatis-plus.version>
        <mybatis-spring.version>3.0.3</mybatis-spring.version> <!-- 适配 Spring 6 的版本 -->
        <mysql.version>8.0.33</mysql.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Web 核心 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!-- MyBatis-Plus 核心依赖(适配 Spring Boot 3.x) -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
            <!-- 排除默认的低版本 mybatis-spring -->
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis-spring</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 手动引入适配 Spring 6 的 mybatis-spring 版本 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>${mybatis-spring.version}</version>
        </dependency>

        <!-- MySQL 驱动(适配 Java 17 + Spring Boot 3.x) -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>${mysql.version}</version>
            <scope>runtime</scope>
        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- 构建配置:确保编译时 Lombok 生效 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <!-- 阿里云镜像仓库,保证依赖下载稳定 -->
    <repositories>
        <repository>
            <id>aliyunmaven</id>
            <name>阿里云中央仓库</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
</project>

启动类

复制代码
@SpringBootApplication
@MapperScan("com.guslegend.mapper")
public class MybatisPlusApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
    }
}

实体类

复制代码
@Data
public class UserDO {

    private Long id;

    private String name;

    private String email;

    private Integer age;
}

mapper层

复制代码
@Mapper
public interface UserMapper extends BaseMapper<UserDO> {
}

CRUD

增加

增加:

复制代码
    @Test
    public void test2() {
        UserDO userDO = new UserDO();
        userDO.setName("guslegend");
        userDO.setAge(18);
        userMapper.insert(userDO);
        System.out.println(userDO.getId());
        userMapper.updateById(userDO);

    }

我们发现写入数据库中的数据,id是不正确的,其并不是数据库自增长,实际是MP生成的id的值写入了数据库之中。

复制代码
@TableName("user")
@Data
public class UserDO {

    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

@TableId(type = IdType.AUTO)即可为自增ID。

@TableFieid注解

可以解决对象中的属性名和字段名不一致的问题(非驼峰),对象中的属性字段在表中不存在的问题。

复制代码
@TableName("user")
@Data
public class UserDO {

    @TableField("id")
    private Integer id;

    @TableField("name")
    private String name;

    @TableField(value = "age",select = false) //不加入查询
    private Integer age;

    @TableField("email")    // 字段名不一样
    private String mail;

    @TableField(exist = false)  // 数据库不存在
    private String address;
}

更新

根据ID更新

复制代码
    @Test
    public void test3() {
        UserDO userDO = new UserDO();
        userDO.setName("guslegend2");
        userDO.setId(1L);
        userMapper.updateById(userDO);

    }

根据条件更新

QueryWrapper:

复制代码
    @Test
    public void test4() {
        UserDO userDO = new UserDO();
        userDO.setMail("guslegend");

        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", 1);
        userMapper.update(userDO, queryWrapper);

    }

UpdateWrapper:

复制代码
    @Test
    public void test5() {
        int update = userMapper.update(null, new UpdateWrapper<UserDO>().eq("id", 1).set("name", "guslegend2"));
        System.out.println(update);

    }

删除操作

deleteById

复制代码
    @Test
    public void test6() {
       userMapper.deleteById(1995853989370589186L);

    }

deleteByMap

复制代码
    @Test
    public void test7() {
        Map<String, Object> map = new HashMap<>();
        // map中的删除条件要全部满足才可以删除
        map.put("name", "guslegend");
        map.put("age", 18);
        userMapper.deleteByMap(map);

    }

delete

复制代码
    @Test
    public void test8() {
    userMapper.delete(new QueryWrapper<UserDO>().eq("id", 1).eq("name", "guslegend"));

    }

deleteByIds

复制代码
    @Test
    public void test9() {
        userMapper.deleteByIds(Arrays.asList(1L, 2L));

    }

查询操作

selectById

复制代码
    @Test
    public void test10() {
        userMapper.selectById(1L);

    }

selectBatchIds

复制代码
    @Test
    public void test11() {
        userMapper.selectBatchIds(Arrays.asList(1L, 2L));

    }

selectOne

复制代码
    @Test
    public void test12() {
        // 查询单个,如果超过一个结果会抛出异常
        userMapper.selectOne(new QueryWrapper<UserDO>().eq("id", 1));

    }

selectList

复制代码
    @Test
    public void test13() {
        //gt 意思是 大于
        userMapper.selectList(new QueryWrapper<UserDO>().gt("age", 18));
    }

分页selectPage

首先我们需要设置分页插件

复制代码
package com.guslegend.config;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.guslegend.mapper")
public class MybatisPlusConfig {

    /**
     * 分页设置
     */
    @Bean
    public PaginationInnerInterceptor paginationInnerInterceptor() {
        return new PaginationInnerInterceptor();
    }

}

然后编写测试代码

复制代码
    @Test
    public void test14() {
        Page<UserDO> page = new Page<>();
        IPage<UserDO> iPage = userMapper.selectPage(page, new QueryWrapper<UserDO>().gt("age", 20));
        System.out.println("数据总数"+iPage.getTotal());
        System.out.println("总页数"+iPage.getPages());
        List<UserDO> userDOList = iPage.getRecords();
        for (UserDO userDO : userDOList) {
            System.out.println(userDO);
        }
    }

Sql注入原理

MP启动后端BaseMapper通过一系列方法注册到mappedStatements中,我们接下来分析究竟是如何完成SQL注入的。

首先我们找到了ISqlInjector负责SQL注入,其是一个接口,有两个实现类。

找到其inspectInject()方法

实现方式的关键是,循环变量方法,进行依赖注入

复制代码
methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));

这里以SelectById为例子,生成的SqlSource对象,再将SQL通过addSelectMappedStatementForTable注入sql

配置

官网网站:https://mybatis.plus/config/

基本配置

configLocation

Mybatis配置文件位置,如果有单独的Mybatis配置,请将其路径配置到configLocation中。

springBoot:

复制代码
mybatis-plus.config-location = classpath:mybatis-config.xml

springMVC:

复制代码
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
 <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

mapperLocations

如果在Mapper中有自定义方法(XML中自定义实现),需要进行配置,告诉mapper所对应的XML文件位置。

springBoot:

复制代码
mybatis-plus.mapper-locations = classpath*:mybatis/*.xml

springMVC:

复制代码
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
 <property name="mapperLocations" value="classpath*:mybatis/*.xml"/>
</bean>

测试用例

复制代码
<?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.guslegend.mapper.UserMapper">

    <select id="findByIds" resultType="com.guslegend.model.UserDO">
        select * from user where id = #{id}
    </select>

</mapper>

@Mapper
public interface UserMapper extends BaseMapper<UserDO> {

    UserDO findByIds(Long id);
}

@SpringBootTest(classes = MybatisPlusApplication.class)
@RunWith(SpringRunner.class)
public class MPTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test1() {

       UserDO userDO = userMapper.findByIds(1L);
       System.out.println(userDO);
    }
}

typeAliasesPackage

MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML ⽂件中可以直接 使⽤类名,⽽不⽤使⽤全限定的类名(即 XML 中调⽤的时候不⽤包含包名)。

springBoot:

复制代码
mybatis-plus.type-aliases-package = com.lagou.mp.pojo

springMVC:

复制代码
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
 <property name="typeAliasesPackage"
value="com.baomidou.mybatisplus.samples.quickstart.entity"/>
</bean>

进阶配置

mapUnderscoreToCamelCase

是否开启自动驼峰命名规则,默认值为Ture

复制代码
#关闭⾃动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
mybatis-plus.configuration.map-underscore-to-camel-case=false

cacheEnabled

全局开启或者关闭配置文件中的所以映射器中的如何缓冲,默认为true。

复制代码
mybatis-plus.configuration.cache-enabled=false

DB策略配置

idType

全局默认主键类型,设置后,可以省略实体类对象中的@TableIf(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>

tablePrefix

表名前缀,全局配置后可以省略@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>

条件构造器

基本操作

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)

模糊查询

like:LIKE'%值%',例:like("name","老王")

notLike:NOTLIKE'%值%',例:notLike("name","老王")

likeLeft:LIKELEFT'%值%',例:likeLeft("name","老王")

likeRight:LIKERIGHT'%值%',例:likeRight("name","老王")

排序

orderBy:排序 ORDER BY 字段

orderByAsc:排序 ORDER BY 字段,...ASC

orderByDesc:排序 ORDER BY 字段,...DESC

逻辑查询

or:或者

and:一起

Slelect

默认查询所以字段,也可以使用select查询指定字段

复制代码
    @Test
    public void test2() {
      UserDO userDO =  userMapper.selectOne(new QueryWrapper<UserDO>().eq("id", 1).select("name"));
        System.out.println(userDO);

    }

插件

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等⽅法,还有其他接⼝
    的⼀些⽅法等。
    总体概括为:
  5. 拦截执⾏器的⽅法
  6. 拦截参数的处理
  7. 拦截结果集的处理
  8. 拦截Sql语法构建的处理

拦截器示例:

复制代码
package com.guslegend.interceptor;

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 plugin(Object target) {
        //创建target对象的代理对象,⽬的是将当前拦截器加⼊到该对象中
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        //属性设置
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 拦截方法,具体业务逻辑编写的位置
        System.out.println("拦截方法");
        return invocation.proceed();
    }
}

注入到spring容器中

复制代码
    @Bean
    public MyInterceptor myInterceptor() {
        return new MyInterceptor();
    }

执行分析插件

MP中提供了对SQL执行的分析的插件,可用于阻断全表更新,删除的操作,此插件仅仅适用于开发环境中。

复制代码
@Bean
public SqlExplainInterceptor sqlExplainInterceptor(){
 SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
 List<ISqlParser> sqlParserList = new ArrayList<>();
 // 攻击 SQL 阻断解析器、加⼊解析链
 sqlParserList.add(new BlockAttackSqlParser());
 sqlExplainInterceptor.setSqlParserList(sqlParserList);
 return sqlExplainInterceptor;
}

性能分析插件

性能分析拦截器,用于输出每条SQL语句以及器执行时间,可以设置最大执行时间,超过会抛出异常。

复制代码
@Bean
public PerformanceInterceptor performanceInterceptor(){
 PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
 performanceInterceptor.setMaxTime(100);
 performanceInterceptor.setFormat(true);
 return performanceInterceptor;
}

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>
 <!-- SQL 执⾏性能分析,开发环境使⽤,线上不推荐。 maxTime 指的是 sql 最⼤执⾏时⻓ -->
 <plugin
interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
 <property name="maxTime" value="100" />
 <!--SQL是否格式化 默认false-->
 <property name="format" value="true" />
 </plugin>
 </plugins>
</configuration>

乐观锁插件

我们想要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  1. 取出记录时,获取当前version
  2. 更新时,带上这个version
  3. 执行更新时,set version = newVersion where version = OldVersion
  4. 如果version不对,就会更新失败

xml:

复制代码
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>

springBoot:

复制代码
    /**
     * 乐观锁设置
     */
    @Bean
    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
        return new OptimisticLockerInnerInterceptor();
    }

更改表结果添加version字段

实体类添加version,并添加@Version注解

复制代码
    @TableField("version")
    @Version
    private Integer version;

    @Test
    public void test3() {
        int rows =  userMapper.update(null,new UpdateWrapper<UserDO>().eq("id",1).eq("version",1).set("name","guslegend"));
        System.out.println(rows);

    }

特别说明

  1. ⽀持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  2. 整数类型下 newVersion = oldVersion + 1
  3. newVersion 会回写到 entity 中
  4. 仅⽀持 updateById(id) 与 update(entity, wrapper) ⽅法
  5. 在 update(entity, wrapper) ⽅法下, wrapper 不能复⽤!!!

SQL注入器

通过上面的学习,我们知道MP中,通过AbstractSqlInjector将BaseMapper中的方法注入到Mybatis容器中,这些方法才可以正常执行。那么我们需要扩充BaseMapper该怎么实现?

  1. 编写自己的MyMapper

    public interface MyMapper<T> extends BaseMapper<T> {

    复制代码
     List<T> findAll();

    }

  2. 将UserMapper集成自己的MyMapper

    @Mapper
    public interface UserMapper extends BaseMapper<UserDO>,MyMapper<UserDO> {

    复制代码
     UserDO findByIds(Long id);
    
     List<UserDO> findAll();

    }

  3. 编写MySqlInjector(如果直接继承AbstractSqlInjector的话,原有的BaseMapper中的方法会失效,所以我们选择继承DefaultSqlInjector进行扩展)

    public class MySqlInjector extends DefaultSqlInjector {

    复制代码
     @Override
     public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {
         System.out.println("MySqlInjector,自定义sql拦截器");
         List<AbstractMethod> methodList = super.getMethodList(configuration, mapperClass, tableInfo);
         methodList.add(new FindAll(mapperClass.getName()));
         return methodList;
     }

    }

  4. 编写findAll

    public class FindAll extends AbstractMethod {

    复制代码
     /**
      * @param methodName 方法名
      * @since 3.5.0
      */
     public FindAll(String methodName) {
         super(methodName);
     }
    
     @Override
     public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?>
             modelClass, TableInfo tableInfo) {
         String sqlMethod = "findAll";
         String sql = "select * from " + tableInfo.getTableName();
         SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql,
                 modelClass);
         return this.addSelectMappedStatementForTable(mapperClass, sqlMethod, sqlSource,
                 tableInfo);
     }

    }

  5. 注册到spring容器

    复制代码
     @Bean
     public MySqlInjector mySqlInjector() {
         return new MySqlInjector();
     }

测试代码

复制代码
    @Test
    public void test4() {
        List<UserDO> userDOList =  userMapper.findAll() ;
        for (UserDO userDO : userDOList){
            System.out.println(userDO);
        }

    }

自动填充功能

在有些时候我们可能会有这样的需求,当我们插入或者更新数据时,希望有些字段可以自动填充数据,比如密码,version等,在MP中提供了这样的功能,可以实现自动填充。

@TableField注解

复制代码
    @TableField(fill = FieldFill.INSERT)
    private Integer version;

为email添加自动填充功能,在新增数据时有效。

编写MyMetaObjectHandler

复制代码
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {


    @Override
    public void insertFill(MetaObject metaObject){
        Object password = getFieldValByName("version",metaObject);
        if(password==null){
            setFieldValByName("version",1,metaObject);
        }
    }

    @Override
    public void updateFill(MetaObject metaObject) {
    }
}

逻辑删除

开发系统的时候,有时候实现删除功能时候,删除需要逻辑删除。在查询数据的时候要携带状态,确保被标记的数据不被查询到,这样做的目的就是被避免被真正的删除。

首先我们需要修改表结构如下:

然后修改UserDO实体类

复制代码
    @TableLogic
    @TableField("del")
    private Integer del;

添加配置文件

复制代码
mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1
      logic-not-delete-value: 0

测试代码

复制代码
    @Test
    public void test7() {
        UserDO userDO  = userMapper.selectOne(new QueryWrapper<UserDO>().eq("id", 4).eq("del",0));
        System.out.println( userDO);
    }

    @Test
    public void test6() {
       int rows = userMapper.deleteById(4l);
        System.out.println( rows);
    }

代码生成器

首先我们需要添加一个maven依赖

复制代码
        <!--        代码自动生成依赖 begin-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!-- velocity -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>
        <!-- 代码自动生成依赖 end-->

package com.guslegend;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
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.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

public class MybatisPlusGenerator {

    public static void main(String[] args) {
        //1. 全局配置
        GlobalConfig config = new GlobalConfig();
        // 是否支持AR模式
        config.setActiveRecord(true)
                // 作者
                .setAuthor("com/guslegend")
                // 生成路径,最好使用绝对路径,window路径是不一样的
                //TODO TODO TODO TODO
                //相对路径
                .setOutputDir("generation")
                // 文件覆盖
                .setFileOverride(true)
                // 主键策略
                .setIdType(IdType.AUTO)

                .setDateType(DateType.ONLY_DATE)
                // 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的
                .setServiceName("%sService")

                //实体类结尾名称
                .setEntityName("%sDO")

                //生成基本的resultMap
                .setBaseResultMap(true)

                //不使用AR模式
                .setActiveRecord(false)

                //生成基本的SQL片段
                .setBaseColumnList(true);

        //2. 数据源配置
        DataSourceConfig dsConfig = new DataSourceConfig();
        // 设置数据库类型
        dsConfig.setDbType(DbType.MYSQL)
                .setDriverName("com.mysql.cj.jdbc.Driver")
                //TODO TODO TODO TODO
                .setUrl("jdbc:mysql://127.0.0.1:3307/test1?useSSL=false")
                .setUsername("root")
                .setPassword("123456");

        //3. 策略配置globalConfiguration中
        StrategyConfig stConfig = new StrategyConfig();

        //全局大写命名
        stConfig.setCapitalMode(true)
                // 数据库表映射到实体的命名策略
                .setNaming(NamingStrategy.underline_to_camel)

                //使用lombok
                .setEntityLombokModel(true)

                //使用restcontroller注解
                .setRestControllerStyle(true)

                // 生成的表,支持多表一起生成,以数组形式填写
                //TODO TODO TODO TODO
                .setInclude("product");

        //4. 包名策略配置
        PackageConfig pkConfig = new PackageConfig();
        pkConfig.setParent("com.guslegend")
                .setMapper("mapper")
                .setService("service")
                .setController("controller")
                .setEntity("model")
                .setXml("mapper");

        //5. 整合配置
        AutoGenerator ag = new AutoGenerator();
        ag.setGlobalConfig(config)
                .setDataSource(dsConfig)
                .setStrategy(stConfig)
                .setPackageInfo(pkConfig);

        //6. 执行操作
        ag.execute();
        System.out.println("======= 相关代码生成完毕 =======");
    }
}

MyabtisX快速开发插件

这个插件其功能:

  1. java与xml调回跳转
  2. Mapper方法自动生成Xml