学习笔记:MyBtaisPlus学习文档

README

  • 大体上是照抄了视频里面的学习文档的。
  • 对文档中一些过于冗杂的部分做了删减,并加入了自己在运行代码时遇到的问题以及解决方法,对于部分细节上的操作增加了解析。
  • 对于之前我学过的,但是忘了的,个人有所迷惑的知识,我也进行了一些补充,在文章里放置了对这些知识分析的更为详细的博客链接。
  • 为什么写这篇博客?主要还是学的快,忘记的快,做点笔记巩固巩固所学知识。也方便自己在线阅览笔记。

本文最后更新于 2022-04-22,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系博主删除。


【MyBatisPlus学习笔记】


1.回顾 MyBtais


原始Jdbc操作的分析

问题

  • ①数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能。
  • sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
  • ③查询操作时,需要手动将结果集中的数据手动封装到实体中。插入操作时,需要手动将实体的数据设置到sql语句的占位符位置。

应对上述问题给出的解决方案

  • ①使用数据库连接池初始化连接资源。
  • ②将sql语句抽取到xml配置文件中。
  • ③使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射。

MyBatis的基本概念

  • 1.mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加加载驱动、创建连接、创建statement等繁杂的过程。
  • 2.mybatis通过xml注解的方式将要执行的各种statement配置起来,并通过java对象和statementsql的动态参数进行映射生成最终执行的sql语句。最后mybatis框架执行sql并将结果映射为java对象并返回。
  • 3.采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

补充:ORM的概念

对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping
ORM是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。如今已有很多免费和付费的ORM产品,而有些程序员更倾向于创建自己的ORM工具。(对象关系映射-百度百科


MyBatis的官网地址:http://www.mybatis.org/mybatis-3/
在这里插入图片描述


2.了解 MyBatisPlus


注:第2部分的信息都是引用自MyBatisPlus的官网的。


2.1.MyBatis-Plus介绍


MyBatis-Plus(简称MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

MyBatis-Plus的官网: https://baomidou.com/

MyBatis-Plus代码托管地址:https://github.com/baomidou/mybatis-plushttps://gitee.com/baomidou/mybatis-plus
在这里插入图片描述


2.2.Mybatis-Plus 特性


在这里插入图片描述


2.3.Mybatis-Plus 架构


在这里插入图片描述


2.4.作者


码云地址:https://gitee.com/organizations/baomidou
在这里插入图片描述


3.快速入门


对于Mybatis整合MP,常常有三种用法:Mybatis + MPSpring + Mybatis + MPSpring Boot + Mybatis + MP


3.1.准备数据库


注:此处我使用的数据库图形管理软件是SQLyog

在这里插入图片描述
在这里插入图片描述

SQL语句

- 创建测试表
CREATE TABLE `tb_user` (
	`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
	`user_name` varchar(20) NOT NULL COMMENT '用户名',
	`password` varchar(20) NOT NULL COMMENT '密码',
	`name` varchar(30) DEFAULT NULL COMMENT '姓名',
	`age` int(11) DEFAULT NULL COMMENT '年龄',
	`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
	PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

-- 插入测试数据
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES
('1', 'zhangsan', '123456', '张三', '18', 'test1@itcast.cn');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES
('2', 'lisi', '123456', '李四', '20', 'test2@itcast.cn');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES
('3', 'wangwu', '123456', '王五', '28', 'test3@itcast.cn');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES
('4', 'zhaoliu', '123456', '赵六', '21', 'test4@itcast.cn');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES
('5', 'sunqi', '123456', '孙七', '24', 'test5@itcast.cn');

在这里插入图片描述


3.2.创建工程


注:我这里使用的是 Idea 2019.3.3的版本。

在这里插入图片描述
在这里插入图片描述
pom.xml

注:若识别不了lombok文件,需要在Idea中下载一个同名插件

Lombok解决了我们实体在实体Bean中需要创建大量的Getter/Setter方法的问题(以及toString, hashCode等平时可能不会用到的方法。但是某些时候仍然需要复写,以期方便使用的方法)。在使用Lombok插件之后,在运行过程中,它帮你自动生成这些方法 。减少了代码量的冗余。同时还可以通过注解进一步简化代码。

<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>8.0.17</version>
    </dependency>
    <!-- 连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.11</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>

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

3.3.MyBatis 查询 User 表


注:此处操作与MyBatisPlus无关,都是MyBatis中最基础的操作。

创建如下的项目结构
在这里插入图片描述

注:itcast-mybatis-plus-simpleitcast-mybatis-plus 的子模块,如下是 itcast-mybatis-plus-simplepom.xml

此处是Maven的继承作用:通过继承可以实现在子工程中沿用父工程的配置。

关于Maven的详细介绍可见该博客:https://kisugitakumi.com/2022/01/21/Maven学习笔记/#9-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">
    <parent>
        <artifactId>itcast-mybatis-plus</artifactId>
        <groupId>cn.itcast.mp</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>itcast-mybatis-plus-simple</artifactId>
</project>

User

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;
}

这里是对Use类中的使用的Lombok的注解介绍

  • @Data是一个方便的快捷方式批注。自动生成set/get方法,toString方法,equals方法,hashCode方法,不带参数的构造方法。
  • @NoArgsConstructor 为该类产生无参的构造方法和包含所有参数的构造方法。
    但如果字段由final修饰,则将导致编译器错误。除非使用@NoArgsConstructor(force = true),否则所有final字段都将初始化为0/false/null。对于具有约束的字段(例如@NonNull字段),不会生成任何检查。
  • @AllArgsConstructor为类中的每个字段生成一个带有1个参数的构造函数。标有@NonNull的字段将对这些参数进行空检查。
  • 注:参考文章:lombok插件,让代码更简洁】【史上最全面的Lombok注解介绍】

UserMapper接口

package cn.itcast.mp.simple.mapper;

import cn.itcast.mp.simple.pojo.User;

import java.util.List;

public interface UserMapper {
    List<User> findAll();
}

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

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="cn.itcast.mp.simple.mapper.UserMapper">
    <resultMap id="userMap" type="cn.itcast.mp.simple.pojo.User">
        <result column="id" property="id"/>
        <result column="user_name" property="userName"/>
        <result column="password" property="password"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <result column="email" property="email"/>
    </resultMap>
    <select id="findAll" resultMap="userMap">
        select * from tb_user
    </select>
</mapper>

这里使用了<resultMap>标签给数据库中的表的列设置了别名。
具体情况可以参考下面这篇博客,个人感觉写的还不错。
链接:【什么是MyBatis?手把手教你用Mybatis创建第一个入门程序!、ResultMap 结果集映射、Lombok介绍】

关于 Mapper.xml 文件的一些解析(此处也算是对 MyBatis 知识的回顾 )
在这里插入图片描述

详细情况请参考这篇博客:MyBatis之XML映射文件详解(上图也引用了这里面的话)

Mybatis-config.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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=UTC&amp;autoReconnect=true&amp;allowMultiQueries=true&amp;useSSL=false"/>
                <property name="username" value="一般都是root"/>
                <property name="password" value="你的密码"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

注:MySQL8MySQL5略有不同,此处我用的是MySQL8

TestMybatis测试类

package cn.itcast.mp.simple;

import cn.itcast.mp.simple.mapper.UserMapper;
import cn.itcast.mp.simple.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 TestMybatis {
    @Test
    public void testFindAll() throws IOException {
        String config = "mybatis-config.xml";
        //加载核心配置文件
        InputStream inputStream = Resources.getResourceAsStream(config);
        //获得sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获得sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //测试查询
        List<User> users = userMapper.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }
}

输出结果
在这里插入图片描述


3.4.MyBatis + MP 查询 User 表


现在结合MP进行MyBatis的基础操作:查询User表,仍然使用上面的项目结构。


  • 第一步:在User实体类添加注释@TableName
import com.baomidou.mybatisplus.annotation.TableName;

在这里插入图片描述

  • 第二步:将UserMapper继承BaseMapper,这样就拥有了BaseMapper中的所有方法
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

在这里插入图片描述

  • 第三步:将UserMapper.xml中的<resultMap>删除掉同时改用属性resultType
    在这里插入图片描述
  • 第四步,在test.java.cn.itcast.mp.simple下新建TestMyBatisPlus,并使用MP中的MybatisSqlSessionFactoryBuilder进程构建
import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;

在这里插入图片描述
测试结果
在这里插入图片描述

  • 1.为什么在User类中添加注释@TableName
  • @TableName作用:用于类名与数据库相对应。
  • UserMapper extends BaseMapper<User>中,默认 User 对象在 BaseMapper 泛型作为一个泛型类去做一个传递。
    此时其会默认读取 user 表,但是这张表是不存在的。所以此处我们需要指定表名。

示例:当类名与数据库表名不同时,如本项目中的 user 类和数据库中的 tb_user 表一样。不加该注解报如下错误。
在这里插入图片描述

  • 2.简单说明
  • 由于使用了 MybatisSqlSessionFactoryBuilder 进行了构建,继承的 BaseMapper 中的方法就载入到了SqlSession中,所以就可以直接使用相关的方法。
  • 例:User 类中的 usernametb_user 中的 user_name 列名虽然不一致,但是依旧可以查询出列的数据。这里用的是MP中的方法。

Idea 中调出 structure
在这里插入图片描述
打个断点,查看 SqlSession 所调用的方法的情况,找到其所继承的 BaseMapper 的方法。
在这里插入图片描述
sqlSessionFactory 下的 configuration 中找到 BaseMapper
在这里插入图片描述
xm中定义了的 mapper(其中的SQL语句),解析到MyBatis容器中,就会变成一个个 Statement
在这里插入图片描述


3.5.Spring + Mybatis + MP


在这里项目引入了Spring框架,数据源、构建等工作就交给了Spring管理。

下图为项目结构。其中 java 包与 test 包中的同名配置文件相同,log4j.properties 依旧使用上面的数据。
在这里插入图片描述

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>itcast-mybatis-plus</artifactId>
        <groupId>cn.itcast.mp</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.实现查询User

  • 第一步:编写jdbc.properties

注:MySQL5 的驱动:jdbc.driver=com.mysql.jdbc.Driver,我这里使用的是MySQL8

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&autoReconnect=true&allowMultiQueries=true&useSSL=false
jdbc.username=root
jdbc.password=root
  • 第二步,编写applicationContext.xm
<?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="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <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="cn.itcast.mp.simple.mapper"/>
    </bean>

</beans>
  • 第三步:编写User对象以及UserMapper接口

User

package cn.itcast.mp.simple.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
    private Long id;
    private String userName;
    private String password;
    private String name;
    private Integer age;
    private String email;
}

UserMapper接口

package cn.itcast.mp.simple.mapper;

import cn.itcast.mp.simple.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface UserMapper extends BaseMapper<User> {}
  • 第四步,编写测试用例
package cn.itcast.mp.simple;

import cn.itcast.mp.simple.mapper.UserMapper;
import cn.itcast.mp.simple.pojo.User;
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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestMybatisSpring {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelectList() {
        List<User> users = this.userMapper.selectList(null);
        for (User user : users) {
            System.out.println(user);
        }
    }
}

3.测试结果
在这里插入图片描述


3.6.SpringBoot + Mybatis + MP


使用SpringBoot将进一步的简化MP的整合。

需要注意的是,因为使用SpringBoot需要继承parent,所以需要重新创建工程,而不是创建子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">
    <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-springboot</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>8.0.17</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.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n

编写application.properties

spring.application.name = itcast-mp-springboot

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&autoReconnect=true&allowMultiQueries=true&useSSL=false
spring.datasource.username=一般角色都是root,除非你又设置了其他角色
spring.datasource.password=该角色的密码

编写pojo

package cn.itcast.mp.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
    private Long id;
    private String userName;
    private String password;
    private String name;
    private Integer age;
    private String email;
}

编写mapper

package cn.itcast.mp.mapper;
import cn.itcast.mp.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {}

编写启动类

package cn.itcast.mp;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("cn.itcast.mp.mapper")//设置 mapper 接口的扫描包
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

编写测试用例

package cn.itcast.mp;

import cn.itcast.mp.mapper.UserMapper;
import cn.itcast.mp.pojo.User;
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.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect() {
        List<User> userList = userMapper.selectList(null);
        for (User user : userList) {
            System.out.println(user);
        }
    }
}

测试结果
在这里插入图片描述

补充:该项目中 Idea会报错:Could not autowire. No beans of 'UserMapper' type found.,这里是Idea软件的问题,此时我们不用管它。

在这里插入图片描述


4.通用的 CRUD


通过前面的学习,我们了解到:通过继承 BaseMapper 就可以获取到各种各样的单表操作。接下来我们将详细讲解这些操作。

BaseMapper 接口结构图
在这里插入图片描述

BaseMapper 接口代码

package com.baomidou.mybatisplus.core.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import org.apache.ibatis.annotations.Param;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/* Talk is cheap. Show me the code. */

/**
 * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得 CRUD 功能
 * <p> 这个 Mapper 支持 id 泛型 </p>
 *
 * @author hubin
 * @since 2016-01-23
 */
public interface BaseMapper<T> extends Mapper<T> {

    /**
     * 插入一条记录
     *
     * @param entity 实体对象
     */
    int insert(T entity);

    /**
     * 根据 ID 删除
     *
     * @param id 主键ID
     */
    int deleteById(Serializable id);

    /**
     * 根据实体(ID)删除
     *
     * @param entity 实体对象
     * @since 3.4.4
     */
    int deleteById(T entity);

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,删除记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 删除(根据ID或实体 批量删除)
     *
     * @param idList 主键ID列表或实体列表(不能为 null 以及 empty)
     */
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<?> idList);

    /**
     * 根据 ID 修改
     *
     * @param entity 实体对象
     */
    int updateById(@Param(Constants.ENTITY) T entity);

    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象 (set 条件值,可以为 null)
     * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    T selectById(Serializable id);

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表(不能为 null 以及 empty)
     */
    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap 表字段 map 对象
     */
    List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,查询一条记录
     * <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
        List<T> ts = this.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(ts)) {
            if (ts.size() != 1) {
                throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records");
            }
            return ts.get(0);
        }
        return null;
    }

    /**
     * 根据 Wrapper 条件,判断是否存在记录
     *
     * @param queryWrapper 实体对象封装操作类
     * @return 是否存在记录
     */
    default boolean exists(Wrapper<T> queryWrapper) {
        Long count = this.selectCount(queryWrapper);
        return null != count && count > 0;
    }

    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录
     * <p>注意: 只返回第一个字段的值</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件
     * @param queryWrapper 实体对象封装操作类
     */
    <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}

4.1.插入操作


1.方法定义

/**
 * 插入一条记录
 *
 * @param entity 实体对象
 */
int insert(T entity);

2.测试用例

package cn.itcast.mp;

import cn.itcast.mp.mapper.UserMapper;
import cn.itcast.mp.pojo.User;
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.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestMyBatisSpringBoot {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testInsert() {
        User user = new User();

        user.setEmail("test_1@ItCast.cn");
        user.setAge(33);
        user.setUserName("CaoCao_1");
        user.setName("曹操_1");
        user.setPassword("123456");
        
        int result = this.userMapper.insert(user);
        System.out.println("result => " + result);//返回的 result 是受影响的行数,并不是自增后的id

        //获取自增长后的 id 的值,自增长后的 id 值会回填到 user 对象中
        System.out.println("id => " + user.getId());
    }
}

3.测试情况
在这里插入图片描述
在这里插入图片描述

数据已经写入了数据库,但是 id 的值不正确。

我们期望的是数据库自增长,但是这里是MP生成了 id 的值写入到了数据库。

4.纠错

如何设置 id 的生成策略呢?

我们只需要在 User 实体类中的属性 id 上添加 @TableId 这个注解即可

@TableId(type = IdType.AUTO)
private Long id;//指定 id 类型是自增长

删除之前在数据表中插入的一条记录,再次运行Idea
在这里插入图片描述
在这里插入图片描述
显然,数据库自增长成功。

5.MP支持的id策略

MyBatis-Plus 自定义的 @TableId 中的 IdType 定义

package com.baomidou.mybatisplus.annotation;

/**
 * 生成ID类型枚举类
 */
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;

    private IdType(int key) {
        this.key = key;
    }

    public int getKey() {
        return this.key;
    }
}

6.@TableField 注解详解

MP 中通过 @TableField 注解可以指定字段的一些属性

  • 1.对象中的属性名和字段名不一致的问题(非驼峰)
  • 2.对象中的属性字段在表中不存在的问题
  • 3.查询时设置对象的属性字段不返回

实体类 User 的情况
在这里插入图片描述

在测试类中添加这样一个方法

 @Test
 public void testSelectById() {
     User user = this.userMapper.selectById(2L);
     System.out.println(user);
 }

查询结果
在这里插入图片描述


4.2.更新操作


MP中,更新操作有2种,一种是根据 id 更新,另一种是根据条件更新。


4.2.1.根据 id 更新


1.方法定义

/**
 * 根据 ID 修改
 *
 * @param entity 实体对象
 */
int updateById(@Param(Constants.ENTITY) T entity);

2.在测试类中添加方法

@Test
public void testUpdateById() {
    User user = new User();
    user.setId(1L); //条件,根据id更新
    user.setAge(19); //更新的字段
    user.setPassword("999999");

    int result = this.userMapper.updateById(user);
    System.out.println("result => " + result);
}

3.测试结果
在这里插入图片描述
在这里插入图片描述


4.2.2.根据条件更新


1.方法定义

/**
 * 根据 whereEntity 条件,更新记录
 *
 * @param entity 实体对象 (set 条件值,可以为 null)
 * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
 */
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

2.1.在测试类中添加方法(QueryWrapper<T>()

//在测试类中添加方法
@Test
public void testUpdateByCondition_1() {
    User user = new User();
    user.setAge(20); //更新的字段
    user.setPassword("8888888");

    QueryWrapper<User> wrapper = new QueryWrapper<User>();
    wrapper.eq("user_name", "CaoCao_1"); //匹配 user_name = CaoCao_1 的用户数据

    //根据条件做更新
    int result = this.userMapper.update(user, wrapper);
    System.out.println("result => " + result);
}

2.2.结果
在这里插入图片描述
在这里插入图片描述

2.3.1.在测试类中添加方法(UpdateWrapper<T>();

//在测试类中添加方法
@Test
public void testUpdateByCondition_2() {
    UpdateWrapper<User> wrapper = new UpdateWrapper<User>();
    wrapper.set("age", 21).set("password", "999999") //更新的字段
            .eq("user_name", "zhangsan"); //更新的条件

    //根据条件做更新
    int result = this.userMapper.update(null, wrapper);
    System.out.println("result => " + result);
}

2.3.2.结果
在这里插入图片描述
在这里插入图片描述


4.3.删除操作


4.3.1.deleteById


1.方法定义

/**
 * 根据 ID 删除
 *
 * @param id 主键ID
 */
int deleteById(Serializable id);

2.在测试类中添加方法

@Test
public void testDeleteById() {
    // 根据id删除数据
    int result = this.userMapper.deleteById(5L);
    System.out.println("result => " + result);
}

3,测试结果
在这里插入图片描述
在这里插入图片描述
显然,id 为5的数据已经被删除


4.3.2.deleteByMap


1.方法定义

/**
 * 根据 columnMap 条件,删除记录
 *
 * @param columnMap 表字段 map 对象
 */
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

2.在测试类中加入方法

@Test
public void testDeleteByMap() {
    Map<String, Object> map = new HashMap<>();
    map.put("user_name", "zhangsan");
    map.put("password", "999999");

    // 根据map删除数据,多条件之间是and关系
    int result = this.userMapper.deleteByMap(map);
    System.out.println("result => " + result);
}

3.测试结果
在这里插入图片描述

在这里插入图片描述
显然 user_namezhangsanpassword999999 的用户已经被删除了。


4.3.3.delete


1.方法定义

/**
 * 根据 entity 条件,删除记录
 *
 * @param wrapper 实体对象封装操作类(可以为 null)
 */
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);

2.1.在测试类中添加方法

@Test
public void testDelete_1() {
    QueryWrapper<User> wrapper = new QueryWrapper<User>();

    wrapper.eq("user_name", "CaoCao_1")
            .eq("password", "888888");

    // 根据包装条件做删除
    int result = this.userMapper.delete(wrapper);
    System.out.println("result => " + result);
}

2.2.测试结果
在这里插入图片描述
在这里插入图片描述

事实上,关于MP中的delete方法,我们还有另一种操作方式。

3.1.在测试类中添加方法

@Test
public void testDelete_2() {
    User user = new User();
    user.setPassword("666666");
    user.setUserName("CaoCao_Plus");
    
    QueryWrapper<User> wrapper = new QueryWrapper<User>(user);
    
    int result = this.userMapper.delete(wrapper);
    System.out.println("result => " + result);
}

3.2.测试结果
在这里插入图片描述
在这里插入图片描述


4.3.4.deleteBatchlds


1.方法定义

/**
 * 删除(根据ID 批量删除)
 *
 * @param idList 主键ID列表(不能为 null 以及 empty)
 */
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable>idList);

2.在测试类中添加方法

@Test
public void  testDeleteBatchIds(){
    // 根据id批量删除数据
    int result = this.userMapper.deleteBatchIds(Arrays.asList(2L, 4L));
    System.out.println("result => " + result);
}

3.测试结果

在这里插入图片描述
在这里插入图片描述


4.4.查询操作


MP提供了多种查询操作,包括根据 id 查询、批量查询、查询单条数据、查询列表、分页查询等操作。


之前表中的记录删的太多了,故更新下表,添加几条数据
在这里插入图片描述


4.4.1.selectById


1.方法定义

/**
 * 根据 ID 查询
 *
 * @param id 主键ID
 */
T selectById(Serializable id);

2.测试类中的方法

@Test
public void testSelectById(){
	//注:数据表中并没有 id 为9的数据
    User user = this.userMapper.selectById(9L);
    System.out.println(user);
}

3.测试结果
在这里插入图片描述


4.4.2.selectBatchIds


1.方法定义

/**
 * 查询(根据ID 批量查询)
 *
 * @param idList 主键ID列表(不能为 null 以及 empty)
 */
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

2.测试类中的方法

@Test
public void testSelectBatchIds() {
    // 根据id批量查询数据
    List<User> users = this.userMapper.selectBatchIds(Arrays.asList(2L, 3L, 4L, 100L));
    for (User user : users) {
        System.out.println(user);
    }
}

3.测试结果
在这里插入图片描述


4.4.3.selectOne


1.方法定义

/**
 * 根据 entity 条件,查询一条记录
 *
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 */
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

2.测试类中的方法

@Test
public void testSelectOne() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //查询条件
    wrapper.eq("password", "123456");
    User user = this.userMapper.selectOne(wrapper);
    System.out.println(user);
}

注:查询的数据超过一条时,会抛出异常

3.测试结果
在这里插入图片描述


4.4.4.selectCount


1.方法定义

/**
 * 根据 Wrapper 条件,查询总记录数
 *
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 */
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

2.测试类中的方法

@Test
public void testSelectCount() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.gt("age", 20); // 条件:年龄大于20岁的用户

    //根据条件查询数据条数
    Integer count = this.userMapper.selectCount(wrapper);
    System.out.println("count => " + count);
}

3.测试结果
在这里插入图片描述


4.4.5.selectList


1.方法定义

/**
 * 根据 entity 条件,查询全部记录
 *
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 */
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

2.测试类中的方法

@Test
public void testSelectList() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //设置查询条件
    wrapper.like("email", "itcast");

    List<User> users = this.userMapper.selectList(wrapper);
    for (User user : users) {
        System.out.println(user);
    }
}

3.测试结果
在这里插入图片描述


4.4.6.selectPage


1.方法定义

/**
 * 根据 entity 条件,查询全部记录(并翻页)
 *
 * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 */
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

2.配置分页插件

将之前在 MyApplication 中的 @MapperScan 注解放到 MyBatisPlusConfig 中,统一优化配置类
在这里插入图片描述

package cn.itcast.mp;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("cn.itcast.mp.mapper")//设置 mapper 接口的扫描包
public class MyBatisPlusConfig {
    @Bean// 配置分页插件
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }
}

3.测试类中的方法

//测试分页查询
@Test
public void testSelectPage() {
    Page<User> page = new Page<>(3, 1); //查询第一页,查询1条数据

    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //设置查询条件
    wrapper.like("email", "itcast");

    IPage<User> iPage = this.userMapper.selectPage(page, wrapper);
    System.out.println("数据总条数: " + iPage.getTotal());
    System.out.println("数据总页数: " + iPage.getPages());
    System.out.println("当前页数: " + iPage.getCurrent());

    List<User> records = iPage.getRecords();
    for (User record : records) {
        System.out.println(record);
    }
}

4.测试结果
在这里插入图片描述


4.5.SQL 注入的原理


前面我们已经知道,MP在启动后会将 BaseMapper 中的一系列的方法注册到 mappedStatements 中。

那么究竟是如何注入的呢?流程又是怎么样的?下面我们将一起来分析下。

已知在MP中,ISqlInjector 负责SQL的注入工作。

以下是喜闻乐见的分析源码的环节

  • 首先我们使用Idea的快捷键:Ctrl + N快速查找类,找到 ISqlInjector

在这里插入图片描述
在这里插入图片描述

  • 显然 ISqlInjector 是个接口,此时我们需要找出它的实现类。使用Idea调出hierarchy,快速查看类层级关系图

在这里插入图片描述
在这里插入图片描述

  • 显然 AbstractSqlInjectorISqlInjector 的实现类,如上图。

*接下来我们Ctrl + B

在这里插入图片描述

  • 如上图,在AbstractSqlInjector中,主要是由 inspectInject() 方法进行的。

  • methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));是关键,循环遍历方法,进行注入。

  • 最终调用抽象方法 injectMappedStatement 进行真正的注入,如下图。

在这里插入图片描述

  • 此时我们依旧使用Idea调出hierarchy(其实鼠标按住该字段,Ctrl + H也行),查看该方法的实现。

在这里插入图片描述

  • 上图中有许多我们使用过的方法,这里我用 SelectById 测试,在以下两个类中Debug一下

在这里插入图片描述
在这里插入图片描述

  • 可以看到,生成了SqlSource对象,再将SQL通过 addSelectMappedStatement 方法添加到 mappedStatements 中。

在这里插入图片描述

若诸位觉得文字描述过于枯燥无味,可以看看这篇博客:【图解mybatis-plus注入原理和工作流程】


5.配置


MP中有大量的配置,其中有一部分是Mybatis原生的配置,另一部分是MP的配置,这里我们只对一些常用的配置做讲解。


5.1.基本配置


5.1.1.configLocation


  • configLocationMyBatis配置文件的位置。
  • 如果您有单独的MyBatis配置,请将其路径配置到 configLocation 中。
  • MyBatis-Configuration* 的具体内容请参考 MyBatis 官方文档。

  • SpringBoot

application.properties

# 指定全局的配置文件
mybatis-plus.config-location = classpath:mybatis-config.xml
  • SpringMVC

applicationContext.xml

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

5.1.2.mapperLocations


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

  • SpringBoot

application.properties

# 指定 mapper.xml 文件的路径
mybatis-plus.mapper-locations = classpath*:mybatis/*.xml
  • SpringMVC

applicationContext.xml

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

Maven多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)

测试

1.添加UserMapper.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="cn.itcast.mp.mapper.UserMapper">
    <select id="findById" resultType="cn.itcast.mp.pojo.User">
        select * from tb_user where id = #{id}
    </select>
</mapper>

UserMapper

package cn.itcast.mp.mapper;

import cn.itcast.mp.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface UserMapper extends BaseMapper<User> {
    User findById(Long id);
}

在测试类 TestMyBatisSpringBoot 中添加的方法

/**
 * 自定义的方法
 */
@Test
public void testFindById() {
    User user = this.userMapper.findById(2L);
    System.out.println(user);
}

结果
在这里插入图片描述

注:如果此时控制台报错说找不到 mybatis-config.xml ,可以 ReImport 一下 pom.xml 文件和 target文件夹。


5.1.3.typeAliasesPackage


  • typeAliasesPackageMyBaits别名包扫描路径。
  • 通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML 文件中可以直接使用类名。
    而不用使用全限定的类名(即 XML 中调用的时候不需要包含包名)。

  • SpringBoot

application.properties

# 实体对象的扫描包
mybatis-plus.type-aliases-package = cn.itcast.mp.pojo
  • SpringMVC

applicationContext.xml

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

5.2.进阶配置


本部分(Configuration)的配置大都为 MyBatis 原生支持的配置。这意味着我们可以通过 MyBatis XML 配置文件的形式进行配置。


5.2.1.mapUnderscoreToCamelCase


  • 类型: boolean
  • 默认值: true

是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名)到经典Java属性名aColumn(驼峰命名) 的类似映射。

注意

  • 此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQLselect body
  • 如果数据库命名符合规则无需使用 @TableField 注解指定数据库字段名。

示例(SpringBoot

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

5.2.2.cacheEnabled


  • 类型: boolean
  • 默认值: true

全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true。

# 全局地开启(true)或关闭配置文件(false)中的所有映射器已经配置的任何缓存
mybatis-plus.configuration.cache-enabled=false

5.3.DB策略配置


5.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>

5.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>

6.条件构造器


MP中,Wrapper 接口的实现类关系如下图所示
Wrapper接口实现类关系图
显然,AbstractWrapperAbstractChainWrapper 是重点实现,接下来我们重点学习 AbstractWrapper 及其子类。

QueryWrapperLambdaQueryWrapper)和 UpdateWrapperLambdaUpdateWrapper)的父类用于生成 sqlwhere 条件,entity 属性也用于生成 sqlwhere 条件。值得注意的是:entity 生成的 where 条件与 使用各个 api生成的 where 条件没有任何关联行为。


注:请根据测试的效果适当更改表的数据,以下的表的数据都有所更改,但也不做说明了。


6.1.allEq


1.说明

allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
  • 全部 eq (或个别 isNull

个别参数说明

  • params : key 为数据库字段名,value 为字段值
  • null2IsNull : 为 true ,则在 mapvaluenull 时调用 isNull 方法;为 false 时,则忽略 valuenull 的。
    • 例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<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)

个别参数说明

  • filter:过滤函数,是否允许字段传入比对条件中 paramsnull2IsNull :同上。
    • 例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 = ‘老王’

2.测试用例中添加的方法

@Test
public void testAllEq() {
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("name", "李四");
    params.put("age", "33");
    params.put("password", null);

    QueryWrapper<User> wrapper = new QueryWrapper<User>();
    //生成的SQL语句:SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE password IS NULL AND name = ? AND age = ?
    //wrapper.allEq(params);

    //生成的SQL语句:SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? AND age = ?
    //wrapper.allEq(params, false);

    //生成的SQL语句:SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE age = ?
    //wrapper.allEq((k, v) -> (k.equals("age") || k.equals("id")) , params);

    //生成的SQL语句:SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? AND age = ?
    wrapper.allEq((k, v) -> (k.equals("age") || k.equals("id") || k.equals("name")), params);

    List<User> users = this.userMapper.selectList(wrapper);
    for (User user : users) {
        System.out.println(user);
    }
}

6.2.基本比较操作


1.说明

  • 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, …)

2.测试用例中添加的方法

@Test
public void testEq() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();

    //SELECT id,user_name,password,name,age,email FROM tb_user WHERE password = ? AND age >= ? AND name IN (?,?)
    wrapper.eq("password", "123456")
            .ge("age", 20)
            .in("name", "曹操", "刘备");

    List<User> users = this.userMapper.selectList(wrapper);

    for (User user : users) {
        System.out.println(user);
    }
}

6.3.模糊查询


1.说明

  • 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 ‘王%’

2.测试用例中添加的方法

@Test
public void testLike() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name LIKE ?
    // 参数:%五(String)
    wrapper.likeLeft("name", "五");

    List<User> users = this.userMapper.selectList(wrapper);
    for (User user : users) {
        System.out.println(user);
    }
}

6.4.排序


1.说明

  • 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

2.测试用例在添加的方法

@Test
public void testOrderByAgeDesc() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //按照年龄倒序排序
    // SELECT id,user_name,name,age,email AS mail FROM tb_user ORDER BY age DESC
    wrapper.orderByDesc("age");

    List<User> users = this.userMapper.selectList(wrapper);
    for (User user : users) {
        System.out.println(user);
    }
}

6.5.逻辑查询


1.说明

  • or :拼接 OR
    • 主动调用 or 表示紧接着下一个方法不是用 and 连接!(不调用 or 则默认为使用 and 连接)
  • and :AND 嵌套
    • 例: and(i -> i.eq(“name”, “李白”).ne(“status”, “活着”)) —> and (name = ‘李白’ and status<> ‘活着’)

2.测试用例中添加的方法

@Test
public void testOr() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? OR age = ?
    wrapper.eq("name", "王五").or().eq("age", 22);

    List<User> users = this.userMapper.selectList(wrapper);
    for (User user : users) {
        System.out.println(user);
    }
}

6.6.select


1.说明

MP查询中,默认查询所有的字段,如果有需要也可以通过 select 方法进行指定字段。

2.测试用例中添加的方法

@Test
public void testSelect() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //SELECT id,name,age FROM tb_user WHERE name = ? OR age = ?
    wrapper.eq("name", "王五")
            .or()
            .eq("age", 22)
            .select("id", "name", "age"); //指定查询的字段

    List<User> users = this.userMapper.selectList(wrapper);
    for (User user : users) {
        System.out.println(user);
    }
}

7.ActiveRecord


ActiveRecord(简称AR)一直广受动态语言( PHPRuby 等)的喜爱;
Java 作为准静态语言,对于ActiveRecord 往往只能感叹其优雅;
所以我们也在 AR 道路上进行了一定的探索,希望大家能够喜欢。

什么是ActiveRecord

  • 标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。
  • ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型。
  • 配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。

ActiveRecord的主要思想

  • 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段在类中都有相应的 Field
  • ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD
  • ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑。

7.1.开启AR之旅


MP中,开启AR非常简单,只需要将实体对象继承Model即可。

package cn.itcast.mp.pojo;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User extends Model<User> {
    private Long id;
    private String userName;
    private String password;
    private String name;
    private Integer age;
    @TableField(value = "email") //指定数据库表中的字段名
    private String mail;
}

7.2.根据主键查询


Maven工程的test目录下的cn.itcast.mp下新建一个测试类,之后的测试方法都加入该类。

package cn.itcast.mp;

import cn.itcast.mp.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestUserMapper2 {
    @Test
    public void testSelectById_AR() {
        User user = new User();
        user.setId(8L);

        User user_1 = user.selectById();
        System.out.println(user_1);
    }
}

结果

[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] ==>  Preparing: SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE id=? 
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] ==> Parameters: 8(Long)
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] <==      Total: 1
[main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@659a2455]
User(id=8, userName=LiuBei, password=123456, name=刘备, age=99, mail=test_8@ItCast.cn, address=null)

7.3.新增数据


测试方法

@Test
public void testInsert_AR(){
    User user = new User();

    user.setUserName("Dio");
    user.setPassword("123456");
    user.setAge(20);
    user.setName("迪奥");
    user.setMail("dio@itcast.cn");

    // 调用AR的insert方法进行插入数据
    boolean insert = user.insert();
    System.out.println("result => " + insert);
}

结果

[main] [cn.itcast.mp.mapper.UserMapper.insert]-[DEBUG] ==>  Preparing: INSERT INTO tb_user ( user_name, password, name, age, email ) VALUES ( ?, ?, ?, ?, ? ) 
[main] [cn.itcast.mp.mapper.UserMapper.insert]-[DEBUG] ==> Parameters: Dio(String), 123456(String), 迪奥(String), 20(Integer), dio@itcast.cn(String)
[main] [cn.itcast.mp.mapper.UserMapper.insert]-[DEBUG] <==    Updates: 1
[main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@b9dfc5a]
result => true

7.4.更新操作


测试方法

@Test
public void testUpdate_AR(){
    User user = new User();
    user.setId(3L);// 查询条件
    user.setAge(333); // 更新的数据

    boolean result = user.updateById();
    System.out.println("result => " + result);
}

结果

[main] [cn.itcast.mp.mapper.UserMapper.updateById]-[DEBUG] ==>  Preparing: UPDATE tb_user SET age=? WHERE id=? 
[main] [cn.itcast.mp.mapper.UserMapper.updateById]-[DEBUG] ==> Parameters: 333(Integer), 3(Long)
[main] [cn.itcast.mp.mapper.UserMapper.updateById]-[DEBUG] <==    Updates: 1
[main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4b2a30d]
result => true

7.5.删除操作


测试方法

@Test
public void testDelete_AR() {
    User user = new User();
    user.setId(6L);
    boolean delete = user.deleteById();
    System.out.println(delete);
}

结果

[main] [cn.itcast.mp.mapper.UserMapper.deleteById]-[DEBUG] ==>  Preparing: DELETE FROM tb_user WHERE id=? 
[main] [cn.itcast.mp.mapper.UserMapper.deleteById]-[DEBUG] ==> Parameters: 6(Long)
[main] [cn.itcast.mp.mapper.UserMapper.deleteById]-[DEBUG] <==    Updates: 1
[main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7dd00705]
true

7.6.根据条件查询


@Test
public void testSelectByCondition_AR() {
    User user = new User();
    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
    userQueryWrapper.le("age", "20");//le :小于等于 <=
    List<User> users = user.selectList(userQueryWrapper);
    for (User user1 : users) {
        System.out.println(user1);
    }
}

结果

[main] [cn.itcast.mp.mapper.UserMapper.selectList]-[DEBUG] ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE age <= ? 
[main] [cn.itcast.mp.mapper.UserMapper.selectList]-[DEBUG] ==> Parameters: 20(String)
[main] [cn.itcast.mp.mapper.UserMapper.selectList]-[DEBUG] <==      Total: 2
[main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fe875be]
User(id=1, userName=WangEr, password=111111, name=王二, age=11, mail=test1@itcast.cn, address=null)
User(id=9, userName=Dio, password=123456, name=迪奥, age=20, mail=dio@itcast.cn, address=null)

8.Oracle 主键Sequence


MySQL 中,主键往往是自增长的,这样使用起来是比较方便的。

如果使用的是 Oracle 数据库,那么就不能使用自增长了,就得使用 Sequence 序列生成 id 值了。


8.1.部署Oracle环境


  • 为了简化环境部署,这里使用Docker环境进行部署安装Oracle

我是在VMware上的CentOS7安装Docker的,详细情况可以参考我之前写的两篇文章。这里就不做过多赘述。


  • 1.拉取镜像
docker pull sath89/oracle-12c
  • 2.创建容器
docker create --name oracle -p 1521:1521 sath89/oracle-12c
  • 3.启动
docker start oracle && docker logs -f oracle

因为是第一次使用oracle,故会先自动初始化数据库。以下是启动过程。

oracle
Database not initialized. Initializing database.
Starting tnslsnr
Copying database files
1% complete
3% complete
11% complete
18% complete
26% complete
37% complete
Creating and starting Oracle instance
40% complete
45% complete
50% complete
55% complete
56% complete
60% complete
62% complete
Completing Database Creation
66% complete
70% complete
73% complete
85% complete
96% complete
100% complete
Look at the log file "/u01/app/oracle/cfgtoollogs/dbca/xe/xe.log" for further details.
Configuring Apex console
Database initialized. Please visit http://#containeer:8080/em http://#containeer:8080/apex for extra configuration if needed
Starting web management console

PL/SQL procedure successfully completed.

Starting import from '/docker-entrypoint-initdb.d':
ls: cannot access /docker-entrypoint-initdb.d/*: No such file or directory
Import finished

Database ready to use. Enjoy! ;)
  • 4.使用数据库图形化界面连接 oracle(我这里使用的是PLSQL Developer

在这里插入图片描述
在这里插入图片描述

  • 注意:使用docker的镜像创建的容器的用户名和密码为:system/oracle,服务名为xe,端口是1521

8.2.创建表以及序列


--创建表,表名以及字段名都要大写
CREATE TABLE "TB_USER" (
	"ID" NUMBER(20) VISIBLE NOT NULL ,
	"USER_NAME" VARCHAR2(255 BYTE) VISIBLE ,
	"PASSWORD" VARCHAR2(255 BYTE) VISIBLE ,
	"NAME" VARCHAR2(255 BYTE) VISIBLE ,
	"AGE" NUMBER(10) VISIBLE ,
	"EMAIL" VARCHAR2(255 BYTE) VISIBLE
)
--创建序列
CREATE SEQUENCE SEQ_USER START WITH 1 INCREMENT BY 1;

在这里插入图片描述


8.3.jdbc驱动包


  • 将文件解压,直接在解压目录下输入以下命令。之后就可以在maven的本地仓库中找到。
mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc8 -Dversion=12.1.0.1 -Dpackaging=jar -Dfile=ojdbc8.jar

在这里插入图片描述

  • pom文件中添加坐标
<!-- oracle驱动包 -->
<dependency>
	<groupId>com.oracle</groupId>
	<artifactId>ojdbc8</artifactId>
	<version>12.1.0.1</version>
</dependency>

在这里插入图片描述


8.4.修改application.properties


  • 对于application.properties的修改,需要修改2个位置,分别是:
# 数据库连接配置
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@192.168.2.212:1521:xe
spring.datasource.username=system
spring.datasource.password=oracle

# id 生成策略
mybatis-plus.global-config.db-config.id-type=input

8.5.配置序列


使用Oracle的序列需要做2件事情:

  • 1.需要配置MP的序列生成器到Spring容器
package cn.itcast.mp;

import com.baomidou.mybatisplus.extension.incrementer.OracleKeyGenerator;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("cn.itcast.mp.mapper")//设置 mapper 接口的扫描包
public class MyBatisPlusConfig {
    @Bean// 配置分页插件
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    @Bean //配置Oracle 序列生成器
    public OracleKeyGenerator oracleKeyGenerator() {
        return new OracleKeyGenerator();
    }
}
  • 2.在实体对象中指定序列的名称
@KeySequence(value = "SEQ_USER", clazz = Long.class)
public class User{
	......
}

8.6.测试


  • 测试类
package cn.itcast.mp;

import cn.itcast.mp.pojo.User;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
	@Autowired
    private UserMapper userMapper;
    
    @Test
    public void testInsert() {
        User user = new User();

        user.setMail("DaioChan@ItCast.cn");
        user.setAge(22);
        user.setUserName("Dio");
        user.setName("迪奥");
        user.setPassword("123456");
        
        int result = this.userMapper.insert(user);
        
        System.out.println("result => " + result);
        System.out.println("id => " + user.getId());
    }
}
  • 结果

这里我们插入了两次数据,显然OracleTB_USER 表中的 id 实现了自增长。
在这里插入图片描述


9.Mybatis-Plus的插件


9.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接口的部分方法,比如 updatequery,commitrollback 等方法,还有其他接口的一些方法等。
总体概括为:

  1. 拦截执行器的方法
  2. 拦截参数的处理
  3. 拦截结果集的处理
  4. 拦截SQL语法构建的处理
  • 拦截器示例
package cn.itcast.mp.plugins;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;

import java.util.Properties;

@Intercepts({@Signature(
		//通过 type 和 method 即可以指定拦截的类型和方法,此处即是可以拦截 执行器中的更新方法 。
        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配置,mybatis-config.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>

9.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 testUpdate(){
	User user = new User();
	user.setAge(20);
	int result = this.userMapper.update(user, null);
	System.out.println("result = " + result);
}
  • 报错
org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation

	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
	......
Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
	at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:49)
	......
	... 32 more

9.3.性能分析插件


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

该插件只用于开发环境,不建议生产环境使用。(性能问题)


注:此处需要在 application.properties 中开启全局映射:mybatis-plus.config-location=classpath:mybatis-config.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 执行性能分析,开发环境使用,线上不推荐。 -->
        <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
        	<!-- 最大的执行时间,单位为毫秒 -->
            <property name="maxTime" value="100" />
            <!-- SQL是否格式化,默认false -->
            <property name="format" value="true" />
        </plugin>
    </plugins>
</configuration>
  • 执行结果
Time:4 ms - ID:cn.itcast.mp.mapper.UserMapper.selectById
Execute SQL:
    SELECT
        id,
        user_name,
        password,
        name,
        age,
        email AS mail 
    FROM
        tb_user 
    WHERE
        id=8 
  • 可以看到,执行时间为11ms。如果将maxTime设置为1,那么,该操作会抛出异常
Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: The SQL execution time is too large, please optimize !
	at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:49)
	at com.baomidou.mybatisplus.core.toolkit.Assert.isTrue(Assert.java:38)
	................

9.4.乐观锁插件


9.4.1.主要适用场景


  • 意图:当要更新一条记录的时候,希望这条记录没有被别人更新
  • 乐观锁实现方式
    • 取出记录时,获取当前 version
    • 更新时,带上这个 version
    • 执行更新时, set version = newVersion where version = oldVersion
    • 如果 version不对 ,就更新失败

9.4.2.插件配置


插件配置有以下三种方式

  • spring.xml
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
  • springboot
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
	return new OptimisticLockerInterceptor();
}
  • mybatis-config.xml
  <!--乐观锁插件-->
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>

9.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;

9.4.4.测试


  • 测试用例
@Test
public void testUpdate(){
	User user = new User();
	user.setAge(30);
	user.setId(2L);
	user.setVersion(1); //获取到version为1
	int result = this.userMapper.updateById(user);
	System.out.println("result = " + result);
}
  • 执行日志
[main] [com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser]-[DEBUG] Original SQL: UPDATE tb_user SET age=?,version=?WHERE id=? AND version=?
[main] [com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser]-[DEBUG]parser sql: UPDATE tb_user SET age = ?, version = ? WHERE id = ? AND version = ?
[main] [org.springframework.jdbc.datasource.DataSourceUtils]-[DEBUG] Fetching JDBC Connection from DataSource
[main] [org.mybatis.spring.transaction.SpringManagedTransaction]-[DEBUG] JDBC Connection [HikariProxyConnection@540206885 wrapping com.mysql.jdbc.JDBC4Connection@27e0f2f5] will not be managed by Spring
[main] [cn.itcast.mp.mapper.UserMapper.updateById]-[DEBUG] ==> Preparing: UPDATEtb_user SET age=?, version=? WHERE id=? AND version=?
[main] [cn.itcast.mp.mapper.UserMapper.updateById]-[DEBUG] ==> Parameters:30(Integer), 2(Integer), 2(Long), 1(Integer)
[main] [cn.itcast.mp.mapper.UserMapper.updateById]-[DEBUG] <== Updates: 1
[main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@30135202]
result = 1

可以看到,更新的条件中有version条件,并且更新的 version 为2。

如果再次执行,更新则不成功。这样就避免了多人同时更新时导致数据的不一致。


9.4.5.特别说明


  • 支持的数据类型只有:intIntegerlongLongDateTimestampLocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity
  • 仅支持 updateById(id)update(entity, wrapper) 方法
  • update(entity, wrapper) 方法下,,wrapper 不能复用!

10.Sql 注入器实现自定义全局操作


  • 我们已经知道,在MP 中,通过 AbstractSqlInjectorBaseMapper 中的方法注入到了Mybatis容器,这样这些方法才可以正常执行。
  • 那么,如果我们需要扩充 BaseMapper 中的方法,又该如何实现呢?下面我们以扩展 findAll 方法为例进行学习。

10.1.编写 MyBaseMapper


package cn.itcast.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;

public interface MyBaseMapper<T> extends BaseMapper<T> {
	List<T> findAll();
}
  • 其他的Mapper都可以继承该Mapper,这样实现了统一的扩展。如下:
package cn.itcast.mp.mapper;
import cn.itcast.mp.pojo.User;

public interface UserMapper extends MyBaseMapper<User> {
	User findById(Long id);
}

10.2.编写 MySqlInjector


如果直接继承 AbstractSqlInjector 的话,原有的 BaseMapper 中的方法将失效,所以我们选择继承 DefaultSqlInjector 进行扩展。

package cn.itcast.mp.sqlInjector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import java.util.List;

public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList() {
	List<AbstractMethod> methodList = super.getMethodList();
	methodList.add(new FindAll());
	// 再扩充自定义的方法
	list.add(new FindAll());
	return methodList;
	}
}

10.3.编写 FindAll


package cn.itcast.mp.sqlInjector;
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 sqlMethod = "findAll";
		String sql = "select * from " + tableInfo.getTableName();
		SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql,modelClass);
		return this.addSelectMappedStatement(mapperClass, sqlMethod, sqlSource,modelClass, tableInfo);
	}
}

10.4.注册到 Spring容器


/**
* 自定义SQL注入器
*/
@Bean
public MySqlInjector mySqlInjector(){
	return new MySqlInjector();
}

10.5.测试


@Test
public void testFindAll(){
	List<User> users = this.userMapper.findAll();
	for (User user : users) {
	System.out.println(user);
	}
}
  • 输出
[main] [cn.itcast.mp.mapper.UserMapper.findAll]-[DEBUG] ==>  Preparing: select * from tb_user 
[main] [cn.itcast.mp.mapper.UserMapper.findAll]-[DEBUG] ==> Parameters: 
[main] [cn.itcast.mp.mapper.UserMapper.findAll]-[DEBUG] <==      Total: 8
 Time:11 ms - ID:cn.itcast.mp.mapper.UserMapper.findAll
Execute SQL:
    select
        * 
    from
        tb_user

至此,我们实现了全局扩展SQL注入器。


11.自动填充功能


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


11.1.添加 @TableField 注解


@TableField(fill = FieldFill.INSERT) //插入数据时进行填充
private String password;
  • password 添加自动填充功能,在新增数据时有效。其中 FieldFill 提供了多种模式选择
public enum FieldFill {
	/**
	* 默认不处理
	*/
	DEFAULT,
	/**
	* 插入时填充字段
	*/
	INSERT,
	/**
	* 更新时填充字段
	*/
	UPDATE,
	/**
	* 插入和更新时填充字段
	*/
	INSERT_UPDATE
}

11.2.编写 MyMetaObjectHandler


package cn.itcast.mp.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 {

    /**
     * 插入数据时填充
     *
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        // 先获取到 password 的值,再进行判断:如果为空,就进行填充;如果不为空,就不做处理
        Object password = getFieldValByName("password", metaObject);
        if (null == password) {
            setFieldValByName("password", "888888", metaObject);
        }
    }

    /**
     * 更新数据时填充
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {

    }
}

11.3.测试


  • 此处测试添加方法
@Test
public void testInsert() {
    User user = new User();

    user.setMail("GuanYu_3@ItCast.cn");
    user.setAge(33);
    user.setUserName("GaunYu_3");
    user.setName("关羽_2");
    user.setPassword("123456");
    user.setAddress("山西_3");

    int result = this.userMapper.insert(user);
    System.out.println("result t==> " + result);

    System.out.println("id ==> " + user.getId());
}
  • 结果
    在这里插入图片描述

12.逻辑删除


开发系统时,有时候在实现功能时,删除操作需要实现逻辑删除,所谓逻辑删除就是将数据标记为删除,而并非真正的物理删除(非 DELETE 操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免数据被真正的删除。MP就提供了这样的功能,方便我们使用,接下来我们一起学习下。



12.1.修改表结构


  • SQL语句
ALTER TABLE `tb_user` 
  ADD COLUMN `deleted` INT (1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER `version` ;
  • 同时,也修改 User 实体,增加 deleted 属性并且添加 @TableLogic 注解
@TableLogic
private Integer deleted;

12.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

12.3.测试


  • 删除方法
@Test
public void testDeleteById(){
	this.userMapper.deleteById(2L);
}
  • 测试结果
[main] [cn.itcast.mp.mapper.UserMapper.deleteById]-[DEBUG] ==>  Preparing: UPDATE tb_user SET deleted=1 WHERE id=? AND deleted=0 
[main] [cn.itcast.mp.mapper.UserMapper.deleteById]-[DEBUG] ==> Parameters: 2(Long)
[main] [cn.itcast.mp.mapper.UserMapper.deleteById]-[DEBUG] <==    Updates: 1
 Time:0 ms - ID:cn.itcast.mp.mapper.UserMapper.deleteById
Execute SQL:
    UPDATE
        tb_user 
    SET
        deleted=1 
    WHERE
        id=2 
        AND deleted=0

在这里插入图片描述

  • 查询方法
@Test
public void testSelectById(){
	User user = this.userMapper.selectById(2L);
	System.out.println(user);
}
  • 输出结果
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] ==>  Preparing: SELECT id,user_name,name,age,email AS mail,version,deleted FROM tb_user WHERE id=? AND deleted=0 
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] ==> Parameters: 2(Long)
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] <==      Total: 0
 Time:14 ms - ID:cn.itcast.mp.mapper.UserMapper.selectById
Execute SQL:
    SELECT
        id,
        user_name,
        name,
        age,
        email AS mail,
        version,
        deleted 
    FROM
        tb_user 
    WHERE
        id=2 
        AND deleted=0

[main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@15fdd1f2]
null

可见,已经实现了逻辑删除。


13.通用枚举


解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!


13.1.修改表结构


ALTER TABLE `tb_user` 
  ADD COLUMN `sex` INT (1) NULL DEFAULT 1 COMMENT '1-男,2-女' AFTER `deleted` ;

13.2.定义枚举


package cn.itcast.mp.enums;

import com.baomidou.mybatisplus.core.enums.IEnum;

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;
    }
}

13.3.配置


# 枚举包扫描
mybatis-plus.type-enums-package=cn.itcast.mp.enums

13.4.修改实体


 private SexEnum sex;

13.5.测试


  • 测试插入数据
 @Test
public void testInsert() {
    User user = new User();

    user.setMail("HotCocoa@ItCast.cn");
    user.setAge(15);
    user.setUserName("HotCocoa");
    user.setName("保登心爱");
    user.setPassword("123456");
    user.setAddress("日本");
    user.setVersion(1);
    user.setSex(SexEnum.WOMAN);//使用的是枚举

    Boolean result = user.insert();//调用 AR 的 insert 方法插入数据
    System.out.println("result t==> " + result);

    System.out.println("id ==> " + user.getId());
}
  • 结果
[main] [com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser]-[DEBUG] parser sql: INSERT INTO tb_user (id, user_name, password, name, age, email, version, sex) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
[main] [org.springframework.jdbc.datasource.DataSourceUtils]-[DEBUG] Fetching JDBC Connection from DataSource
[main] [org.mybatis.spring.transaction.SpringManagedTransaction]-[DEBUG] JDBC Connection [HikariProxyConnection@16196099 wrapping com.mysql.cj.jdbc.ConnectionImpl@26d10f2e] will not be managed by Spring
[main] [cn.itcast.mp.mapper.UserMapper.insert]-[DEBUG] ==>  Preparing: INSERT INTO tb_user ( id, user_name, password, name, age, email, version, sex ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? ) 
[main] [cn.itcast.mp.mapper.UserMapper.insert]-[DEBUG] ==> Parameters: null, HotCocoa(String), 123456(String), 保登心爱(String), 15(Integer), HotCocoa@ItCast.cn(String), 1(Integer), 2(Integer)
[main] [cn.itcast.mp.mapper.UserMapper.insert]-[DEBUG] <==    Updates: 1
 Time:11 ms - ID:cn.itcast.mp.mapper.UserMapper.insert
Execute SQL:
    INSERT INTO tb_user ( id, user_name, password, name, age, email, version, sex ) 
    VALUES ( null, 'HotCocoa', '123456', '保登心爱', 15, 'HotCocoa@ItCast.cn', 1, 2 )

在这里插入图片描述

  • 测试查询
@Test
public void testSelectById(){
	User user = this.userMapper.selectById(13L);
	System.out.println(user);
}
  • 结果
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] ==>  Preparing: SELECT id,user_name,name,age,email AS mail,version,deleted,sex FROM tb_user WHERE id=? AND deleted=0 
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] ==> Parameters: 13(Long)
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] <==      Total: 1
 Time:6 ms - ID:cn.itcast.mp.mapper.UserMapper.selectById
Execute SQL:
    SELECT
        id,
        user_name,
        name,
        age,
        email AS mail,
        version,
        deleted,
        sex 
    FROM
        tb_user 
    WHERE
        id=13 
        AND deleted=0
        
[main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1949309d]
User(id=13, userName=HotCocoa, password=null, name=保登心爱, age=15, mail=HotCocoa@ItCast.cn, address=null, version=1, deleted=0, sex=)
  • 查询条件时也是有效的
@Test
public void testSelectBySex() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("sex", SexEnum.WOMAN);
    List<User> users = this.userMapper.selectList(wrapper);
    for (User user : users) {
        System.out.println(user);
    }
}
  • 结果
[main] [cn.itcast.mp.mapper.UserMapper.selectList]-[DEBUG] ==>  Preparing: SELECT id,user_name,name,age,email AS mail,version,deleted,sex FROM tb_user WHERE deleted=0 AND sex = ? 
[main] [cn.itcast.mp.mapper.UserMapper.selectList]-[DEBUG] ==> Parameters: 2(Integer)
[main] [cn.itcast.mp.mapper.UserMapper.selectList]-[DEBUG] <==      Total: 1
 Time:19 ms - ID:cn.itcast.mp.mapper.UserMapper.selectList
Execute SQL:
    SELECT
        id,
        user_name,
        name,
        age,
        email AS mail,
        version,
        deleted,
        sex 
    FROM
        tb_user 
    WHERE
        deleted=0 
        AND sex = 2

[main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3b0ee03a]
User(id=13, userName=HotCocoa, password=null, name=保登心爱, age=15, mail=HotCocoa@ItCast.cn, address=null, version=1, deleted=0, sex=)

14.代码生成器


  • AutoGeneratorMyBatis-Plus 的代码生成器。
  • 通过 AutoGenerator 可以快速生成 EntityMapperMapper XMLServiceController 等各个模块的代码,极大的提升了开发效率。
  • 注意mybatis-plus-generator 3.5.1 及其以上版本,对历史版本不兼容!
  • 详细情况请参考官网,这里只是做个演示。

14.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.itacst.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>8.0.17</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>

14.2.代码


package cn.itcast.mp.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;

/**
 * <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&characterEncoding=utf8&serverTimezone=UTC&autoReconnect=true&allowMultiQueries=true&useSSL=false");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");//MySQL5使用的是:com.mysql.jdbc.Driver,我这里使用的是MySQL8
        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.BaseEntity");
        strategy.setEntityLombokModel(true);
//        strategy.setSuperControllerClass("com.baomidou.mybatisplus.samples.generator.common.BaseController");
        strategy.setInclude(scanner("表名"));
        strategy.setSuperEntityColumns("id");
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        // 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

}

14.3.测试


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


15.MybatisX 快速开发插件


  • 简介MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
  • 安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。
  • 功能JavaXML 调回跳转;Mapper 方法自动生成 XML

总结


本博客主要讲述了

  • MyBatis-Plus的基本操作,通用的 CRUD、基本配置、条件构造器;
  • ActiveRecord 动态语言、Oracle主键 Sequence 的自增长,MP的插件、SQL注入器;
  • 自动填充功能、逻辑删除、通用的枚举、代码生成器、MyBatisX 插件。

关于MyBatis-Plus的更多信息还请访问官网:https://baomidou.com/


  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值