Mybatis-plus


1.了解Mybatis-plus

1.1代码及文档

文档地址:https://mybatis.plus/guide/

源码地址:https://github.com/baomidou/mybatis-plus

1.2特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响
  • 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作
  • 强大的CRUD操作:内置通用Mapper、通用Service,仅仅通过少量配置即可实现单表大部分CURD操作,更有强大的构造器,满足各类使用需求
  • 支持Lamba形式调用:通过Lambda表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持多种数据库:支持MySQL、MarlaDB、Orcale、DB、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer等多种数据库
  • 支持主键自动生成:支持多达4种主键策略(内含分布式唯一ID生成器:Sequence),可自由配置,完美解决主键问题
  • 支持XML热加载:Mapper对应的XML支持热加载,对于简单的CURD操作,甚至可以无XML启动
  • 支持ActiveRecord模式:支持ActiveRecord形式调用,实体类只需继承Model类即可进行强大的CURD操作
  • 支持自定义全局通用操作:支持全局通用方法注入(Write once,use anywhere)
  • 支持关键词自动转义:支持数据库关键词(order、key…)自动转义,还可自定义关键词
  • 内置代码生成器:采用代码或者Maven插件可快速生成Mapper、Model、Service、Controller层代码,支持模板引擎,更有超多自定义配置
  • 内置分页插件:基于MyBatis物理分页,开发者无需关心具体操作,配置好插件后,写分页等同于普通List查询
  • 内置性能分析插件:可输出Sql语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表delete、update操作智能分析阻断,也可自定义拦截规则,预防误操作
  • 内置Sql注入剥离器:支持Sql注入剥离,有效放置Sql注入攻击

1.3架构

image-20220503210849284

2.快速开始

对于MyBatis整合MP常常有三种用法,分别是Mybatis+MP、Spring+Mybatis+MP、SpringBooMybatis+MP

2.1新建数据库

2.2创建工程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>cn.itcast.mp</groupId>
<artifactId>itcast-mybatis-plus</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<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.28</version>
</dependency>
<!-- 连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</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.7.7</version>
</dependency>
</dependencies>

<!-- 指定jdk版本-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1,8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

</project>

2.3Mybaris + MP

2.3.1创建子Module

log4j配置文件

1
2
3
4
5
log4j.rootLogger=DEBUG,A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n

2.3.2Mybatis实现查询User

第一步,编写mybatis-config.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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文件-->
<properties resource="jdbc.properties"></properties>

<!-- 环境-->
<environments default="developement">
<environment id="developement">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>

</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml" />
</mappers>
</configuration>

第二步,编写User实体对象:(这里使用lombok进行了优化bean操作)

lombok省略了getter和setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.itcast.mp.simple.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String email;
}

第三步,编写UserMapper接口

1
2
3
4
5
6
7
8
9
package cn.itcast.mp.simple.mapper;

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

import java.util.List;

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

第四步,编写UserMapper.xml文件

1
2
3
4
5
6
7
8
9
10
<?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">
<select id="findAll" resultType="cn.itcast.mp.simple.pojo.User">
select * from tb_user
</select>

</mapper>

第五步,编写测试用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

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

2.3.3Mybatis+MP实现查询User

第一步,将UserMapper继承BaseMapper,将拥有了BaseMapper中的所有方法:

1
2
3
4
5
6
7
8
9
10
package cn.itcast.mp.simple.mapper;

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

import java.util.List;

public interface UserMapper extends BaseMapper<User> { // 这里的泛型也指表名
List<User> findAll();
}

第二步,使用MP中的MybatisSqlSessionFactoryBuilder进程创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package cn.itcast.mp.simple;

import cn.itcast.mp.simple.mapper.UserMapper;
import cn.itcast.mp.simple.pojo.User;
import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
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 TestMybatisPlus {
@Test
public void testFindAll() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

// 测试查询
// List<User> userList = userMapper.findAll();
List<User> userList = userMapper.selectList(null);
for (User user : userList) {
System.out.println(user);
}
}
}
注意:记得在User类中添加注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}

2.4Spring + Mybatis + MP

引入了Spring框架、数据源、构建等工作就交给了Spring管理

2.4.1创建子Module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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>

<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>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.version>5.0.5.RELEASE</spring.version>
</properties>

</project>

2.4.2实现查询User

第一步,编写jdbc.properties

1
2
3
4
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mp?characterEncoding=UTF8
jdbc.username=root
jdbc.password=root

第二步,编写applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<context:property-placeholder location="classpath:*.properties"/>

<!-- 定义数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="10" />
<property name="minIdle" value="5" />
</bean>

<!-- 这里使用MP提供的sqlSessionFactory,完成了Spring与MP的整合-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>

<!-- 扫描mapper接口,使用的依然是Mybatis原生的扫描器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.itcast.mp.simple.mapper" />
</bean>
</beans>

第三步,编写User对象以及UserMapper接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}

2.4.3测试用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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> userList = this.userMapper.selectList(null);
for (User user : userList) {
System.out.println(user);
}
}
}
注意:期间可能会报错

Invalid bean definition with name ‘dataSource’ defined in class path resource [applicationContext.xml]: Could not resolve placeholder ‘jdbc.driver’ in value “${jdbc.driver}”; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder ‘jdbc.driver’ in value “${jdbc.driver}”


两种解决方法

  • 1.将${}里的内容写死
  • 2.在Test文件夹下创建一个resource文件夹,将文件复制到这里(记得将resource文件夹声明为资源文件夹)

2.5SpringBoot + Mybatis + MP

使用SpringBoot将进一步的简化MP的整合,需要注意的是,由于使用SpringBoot需要继承parent,所以需要重新创建工程,而不是创建子Module

3.通用CRUD

3.1插入操作

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestUserMapper {

@Autowired
private UserMapper userMapper;

@Test
public void testInsert() {
User user = new User();
user.setEmail("itcast.cn");
user.setAge(78);
user.setUserName("caocao");
user.setName("曹操");
user.setPassword("123");

int result = this.userMapper.insert(user); // result数据库受影响的行数
System.out.println("result=>" + result);

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

User类设置表主键,并设置自增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.itcast.mp.simple.pojo;

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

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {

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

@TableField

在MP中通过@TableField注解可以指定字段的一些属性,常常解决的问题有两个:

1.对象中的属性名和字段名不一致的问题(非驼峰)

2.对象中的属性字段在表中不存在的问题

字段名不一致
1
2
@TableField(value = "email") //指定数据库表中的字段名
private String mail;
表中不存在
1
2
@TableField(exist = false) // 该字段在数据库中不存在
private String address;
查询时不返回该字段的值
1
2
@TableField(select = false) // 查询时不返回该字段的值
private String password;

3.2更新操作

在MP中,更新操作有两种,另一种是根据条件更新

3.2.1根据id更新

1
2
3
4
5
6
7
8
9
10
@Test
public void testUpdateById() {
User user = new User();
user.setId(1L); // 条件,根据id更新
user.setAge(19); // 更新的字段
user.setPassword("333");

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

3.2.2根据条件更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testUpdate() {
User user = new User();
user.setAge(20); // 更新的字段
user.setPassword("888");
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("user_name","张三"); //匹配user_name为张三的用户
int result = this.userMapper.update(user,wrapper);
}

@Test
public void testUpdate2() {
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.set("age",21).set("password",123123) // 更新的字段
.eq("user_name","caocao");
int result = this.userMapper.update(null,wrapper);
}

3.3删除操作

3.3.1deleteById

1
2
3
4
5
    @Test
public void testDeleteById() {
// 根据id删除数据
int result = this.userMapper.deleteById(2);
}

3.3.2deleteByMap

方法定义:

1
int deleteByMap(@param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

测试类:

1
2
3
4
5
6
7
8
9
    @Test
public void testDeleteByMap() {
Map<String,Object> map = new HashMap<>();
map.put("user_name","张三");
map.put("password","333");
// 根据Map删除数据,多条件之间是and关系
int result = this.userMapper.deleteByMap(map);
System.out.println(result);
}

3.3.3delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    @Test
public void testDelete() {

// 用法一:
// QueryWrapper<User> wrapper = new QueryWrapper<>();
// wrapper.eq("user_name","caocao")
// .eq("password","123");

// 用法二:
User user = new User();
user.setUserName("caocao");
user.setPassword("123456");

QueryWrapper<User> wrapper = new QueryWrapper<>(user);

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

3.3.4deleteBatchIds

方法定义:

1
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList)

测试类:

1
2
3
4
5
6
    @Test
public void testDeleteBatchIds() {
// 根据id做批量删除数据
int result = this.userMapper.deleteBatchIds(Arrays.asList(10L, 11L));
System.out.println(result);
}

3.4查询操作

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

3.4.1selectById

3.4.2selectBatchIds

方法定义:

1
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList)

测试类:

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

3.4.3selectOne

方法定义:

1
T selectOne(@Param(Constants.Wrapper<T> queryWrapper));

测试用例:

1
2
3
4
5
6
7
8
    @Test
public void testSelectOne() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("user_name","张三");
// 查询数据超过一条时,会抛出异常
User user = this.userMapper.selectOne(wrapper);
System.out.println(user);
}

3.4.4selectCount

方法定义:

1
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

测试用例:

1
2
3
4
5
6
7
8
    @Test
public void testSelectCount() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age",20);// 条件:年龄大于20岁的用户
// 根据条件查询数据的数量
Integer count = this.userMapper.selectCount(wrapper);
System.out.println(count);
}

3.4.5selectList

方法定义:

1
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper)

3.4.6selectPage

方法定义:

1
2
3
4
5
6
// 根据条件,查询全部记录

//@Param page分页查询条件(可以为RowBounds.DEFAULT)
//@Param queryWrapper 实例对象封装类(可以为null)

IPage<T> selectPage(IPage<T> page,@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

配置分页插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
}
}

测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    @Test
public void testSelectPage() {

Page<User> page = new Page<>(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);
}
}

3.5SQL注入原理

前面我们已经知道,MP在启动后会将BaseMapper中的一系列的方法注册到MappedStatement中,那么究竟如何注入?

在MP中,ISqlInjector负责SQL的注入工作,它是一个接口,AbstractSqlInjector是它的实现类

在AbstractSqlInjector中,主要是由inspectInject()方法进行注入的


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

最终调用抽象方法injectMappedStatement进行真正的注入,生成了SqlSource对象,再将SQL通过addSelectMappedStatement方法添加到mappedStatements中

4.配置

在MP中有大量的配置,其中有一部分是Mybatis原生的配置,另一部分是MP的配置,详情:https://mybatis.plus/config/

4.1基本配置

4.1.1configLocation

Mybatis配置文件位置,如果你有单独的Mybatis配置,请将其路径配置到configLocation中,MybatisConfiguration的具体内容请参考Mybatis官方文档

SpringBoot:

1
mybatis-plus.config-location = classpath:mybatis-config.xml

SpringMVC:

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

4.1.2mapperLocations

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

SpringBoot:

1
mybatis-plus.mapper-locations = classpath*:mybatis/*.xml

SpringMVC:

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

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

测试

UserMapper.xml:

1
2
3
4
5
6
7
8
9
10
<?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>
1
2
3
4
5
6
7
8
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> {
User findById(long id);
}

测试用例:

跟上面的没啥区别,偷懒省略了

4.1.3typeAliasesPackage

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

SpringBoot:

1
mybatis-plus.type-aliases-package = cn.itcast.mp.pojo

SpringMVC:

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

4.2进阶配置

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

4.2.1mapUnderscoreToCamelCase

  • 类型:boolean
  • 默认值:true

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


注意:

此属性在Mybatis中原默认值为false,在Mybatis-plus中,此属性也将用于生成最终的SQL的select body

如果你的数据库命名符合规则无需使用@TableField注解指定数据库字段名

示例(SpringBoot):

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

4.2.2cacheEnabled

  • 类型:boolean
  • 默认值:true

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

示例:

1
mybatis-plus.configuration.cache-enabled=false

4.3DB策略

4.3.1idType

  • 类型:com.baomidou.mybatisplus.annotation.IdType
  • 默认值:ID_WORKER

全局默认主键类型,设置后,既可省略实体对象中的@TableId(type = IdType.AUTO)配置

示例:

SpringBoot:

1
mybatis-plus.global-config.db-config.id-type=auto

SpringMVC:

1
2
3
4
5
6
7
8
9
10
11
12
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="globalConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<property name="idType" value="AUTO" />
</bean>
</property>
</bean>
</property>
</bean>

4.3.2tablePrefix

  • 类型:Stringq
  • 默认值:null

表明前缀,全局配置后可以省略@TableName()配置

SpringBoot:

1
2
# 全局的表名前缀
mybatis-plus.global-config.db-config.table-prefix=tb_

SpringMVC:

1
2
3
4
5
6
7
8
9
10
11
12
13
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="globalConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<property name="idType" value="AUTO" />
<property name="tablePrefix" value="tb_" />
</bean>
</property>
</bean>
</property>
</bean>

5.条件构造器

在MP中,Wrapper接口的实现类关系如下:

image-20220511110837637

可以看到,AbstractWrapper和AbstractChainWrapper是重点实现,接下来我们重点学习AbstractWrapper以及其子类

说明:

QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper)的父类

用于生成sql的where条件,entity属性也用于生成sql的where条件

注意:entity生成的where条件与使用各个api生成的where条件没有任何关联行为

官网文档地址:https://mybatis.plus/guide/wrapper.html

5.1allEq

5.1.1说明

1
2
3
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则在map的value为null时调用isNull方法,为false时刻忽略value为null的

  • 例1:allEq({id:1,name:”老王”,age:null})->id = 1 and name = ‘老王’ and age is null
  • 例2:allEq({id:1,name:”老王”,age:null}, false)->id = 1 and name = ‘老王’
1
2
3
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:过滤函数,是否允许字段传入对比条件中

params与null2IsNull:同上

  • 例1:allEq(k,v) -> k.indexOf(“a”) > 0, ({id:1,name:”老王”,age:null}) -> name = ‘老王’ and age is null
  • 例2:allEq(k,v) -> k.indexOf(“a”) > 0, ({id:1,name:”老王”,age:null}, false)->name = ‘老王’

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void testAllEq() {

Map<String,Object> params = new HashMap<>();
params.put("name","李四");
params.put("age","20");
params.put("password",null);

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

// SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? AND age = ?
// wrapper.allEq(params,false);

// 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);

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

5.2基本操作

  • eq
    • 等于=
  • ne
    • 不等于!=
  • gt
    • 大于>
  • ge
    • 大于等于>=
  • lt
    • 小于<
  • le
    • 小于等于<=
  • between
    • BETWEEN 值1AND 值2
  • notBetween
    • NOT BETWEEN 值1 AND 值2
  • in
    • 字段 IN(value.get(0),value.get(1),…)
  • notIn
    • 字段 NOT IN(v0,v1)

测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testEq() {
QueryWrapper<User> wrapper = new QueryWrapper<>();

// SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE password = ? AND age >= ? AND name IN (?,?)
wrapper.eq("password","123456")
.ge("age",20)
.in("name","张三","李四");
List<User> userList = this.userMapper.selectList(wrapper);
for (User user : userList) {
System.out.println(user);
}
}

5.3模糊查询

  • like

    • LIKE ‘%值%’
    • 例:like(“name”,”王”) —> name like ‘%王%’
  • notLike

    • NOT LIKE ‘%值%’

    • 例:notLike(“name”,”王”) —> name not like ‘%王%’

  • likeLeft

    • LIKE ‘%值’

    • 例:likeLeft(“name”,”王”) —> name not like ‘王%’

  • likeRight

    • LIKE ‘值%’
    • 例:likeRight(“name”,”王”) —> name like ‘王%’

5.4排序

  • order by
    • 排序: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 DECS ,name DESC
1
2
3
4
5
6
7
8
9
10
11
12
    @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> userList = this.userMapper.selectList(wrapper);
for (User user : userList) {
System.out.println(user);
}
}

5.5逻辑查询

  • or
    • 拼接OR
    • 主动调用or表示紧接着下一个办法不是用and连接!(不调用or则默认为使用and连接)
  • and
    • AND嵌套
    • 例:and(i -> i.eq(“name”,”李白”).ne(“status”,”活着”)) —> and(name = ‘李白’ and status = ‘活着’)
1
2
3
4
5
6
7
8
9
10
11
    @Test
public void testOr() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? OR name = ?
wrapper.eq("name","曹操1").or().eq("name",20);

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

5.6select

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

1
2
3
4
5
6
7
8
9
10
11
    @Test
public void testSelect() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// SELECT id,name,age FROM tb_user WHERE name = ? OR name = ?
wrapper.eq("name","曹操1").or().eq("name",20).select("id","name","age");

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

6.ActiveRecord

ActiveRecord(简称AR)一直广受动态语言(PHP、Ruby等)的喜爱,而java作为标准静态语言,对于ActiveRecord往往只能赞叹其优雅

什么是ActiveRecord?

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

AcitveRecord的主要思想是:

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

6.1开启AR之旅

在MP中,开启AR非常简单,只要将实体类继承Model即可

1
public class User extends Model<User> {}

6.2根据主键查询

测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.itcast.mp.simple;

import cn.itcast.mp.simple.pojo.User;
import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestUserMapper2 {
@Test
public void selectById() {
User user = new User();
user.setId(1L);
// 依然要使用userMapper
User user1 = user.selectById();
System.out.println(user1);
}
}

6.3新增数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    @Test
public void testInsert() {
User user = new User();
user.setUserName("刘备");
user.setPassword("123456");
user.setAge(30);
user.setName("刘备呗");
user.setMail("liubei@qq.com");

// 调用AR的insert
// INSERT INTO tb_user ( user_name, password, name, age, email ) VALUES ( ?, ?, ?, ?, ? )
boolean insert = user.insert();
System.out.println(insert);
}

6.4更新操作

1
2
3
4
5
6
7
8
9
10
    @Test
public void testUpdate() {
User user = new User();
user.setId(7L); // 查询条件
user.setAge(31);

// UPDATE tb_user SET age=? WHERE id=?
boolean result = user.updateById();
System.out.println(result);
}

6.5 删除操作

1
2
3
4
5
6
7
8
@Test
public void testDelete() {
User user = new User();
user.setId(6L);

boolean result = user.deleteById();
System.out.println(result);
}

6.6根据条件查询

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testSelect() {
User user = new User();

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age",30); //大于等于30岁
List<User> userList = user.selectList(wrapper);
for (User user1 : userList) {
System.out.println(user1);
}
}

7.插件

7.1 mybatis的插件机制

Mybatis允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,Mybatis允许使用插件来拦截的方法调用包括:

  • 1.Exceutor(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
  • 2.ParameterHandler(getParameterObject,setParameters)
  • 3.ResultSetHandler(handleResultSets,handleOutputParameters)
  • 4.StatementHandler(prepare,parameterize,batch,update,query)

我们看到了可以拦截Exceutor接口的部分方法,比如update,query,commit,rollback等方法,还有其他接口的一些方法等。

总体概括为:

  • 1.拦截执行器的方法
  • 2.拦截参数的处理
  • 3.拦截结果集的处理
  • 4.拦截Sql语法构建的处理

具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package cn.itcast.mp.plugins;

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

import java.lang.reflect.Executable;
import java.util.Properties;

@Intercepts({@Signature(
type = Executor.class,// 指定拦截类型
method = "update", // 指定拦截器中的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容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.itcast.mp;

import cn.itcast.mp.plugins.MyInterceptor;
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 // 注入自定义的拦截器(插件)
public MyInterceptor myInterceptor() {
return new MyInterceptor();
}
}

7.2执行分析插件

在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作,注意:该插件仅使用于开发环境,不适用于生产环境

SpringBoot配置:


文章作者: Water monster
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Water monster !
评论
  目录