MyBatis-Plus简介
Mybaits_plus
MyBatis-Plus简介
简介
MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为 简化开发、提高效率而生。
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分
- CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由
- 配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强
- 大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、
- Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等
- 同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、
- Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出
- 慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防
- 误操作
支持数据库
- MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb
- 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库
创建数据库及表
创建表
CREATE DATABASE `mybatis_plus`;
use `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
添加数据
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
创建Spring Boot工程


pom.xml添加依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>8.0.20</scope>
</dependency>
idea中安装lombok插件

编写代码
配置application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
添加实体
在com.imooc文件夹下创建pojo文件夹,再创建User实体类
@Data //lombok注解
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
添加mapper
在com.imooc文件夹下创建mapper文件夹,再创建UserMapper接口
BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型为操作的 实体类型
package com.imooc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.imooc.pojo.User;
public interface UserMapper extends BaseMapper<User> {
}
启动类
在Spring Boot启动类中添加@MapperScan注解,扫描mapper包
@SpringBootApplication
//扫描接口所在的包
@MapperScan("com.imooc.mapper")
public class MybaisSdutyApplication {
public static void main(String[] args) {
SpringApplication.run(MybaisSdutyApplication.class, args);
}
}
测试
在test=》java=》com.imooc=》创建MybatisPlusTest测试类
userMapper报下划线错误不用管
package com.imooc;
import com.imooc.mapper.UserMapper;
import com.imooc.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class MybatisPlusTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectList(){
//selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
List<User> list = userMapper.selectList(null);
list.forEach(System.out::println);
}
}
启动测试类,结果

IDEA在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确 的执行。 为了避免报错,可以在mapper接口上添加 @Repository 注解
加入日志功能
在application.yml中配置日志输出
# 配置MyBatis日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

基本CRUD
BaseMapper
MyBatis-Plus中的基本CRUD在内置的BaseMapper中都已得到了实现,我们可以直接使用,接口如 下:
按住Ctrl键然后点击BaseMapper即可看到对应的接口实现

这里可查看对应实现的方法

package com.baomidou.mybatisplus.core.mapper;
/**
* 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);
}
测试MaseMapper的新增功能
在test=》java=》com.imooc=》MybatisPlusTest测试类中添加新增方法 insert方法
@Test
public void testInsert(){
User user = new User();
user.setName("张三");
user.setAge(22);
user.setEmail("zadasdas@qq.com");
//INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
int result = userMapper.insert(user);
System.out.println("受影响行数:"+result);
//1475754982694199298
System.out.println("id自动获取:"+user.getId());
}
运行测试方法,结果
最终执行的结果,所获取的id为1550726301798699010
这是因为MyBatis-Plus在实现插入数据时,会默认基于雪花算法的策略生成id
执行该测试类后,数据库中就有新增一条记录了

测试MaseMapper的删除功能
通过id删除记录
deleteById方法里面增加一个L表示这是null数据
@Test
public void testDeleteById(){
//通过id删除用户信息
// DELETE FROM user WHERE id=?
int result = userMapper.deleteById(1550726301798699010L);
System.out.println("受影响行数:"+result);
}
运行测试方法,结果

通过id批量删除记录
@Test public void testDeleteBatchIds(){
//通过多个id批量删除
// DELETE FROM user WHERE id IN ( ? , ? , ? )
List<Long> idList = Arrays.asList(1L, 2L, 3L);
int result = userMapper.deleteBatchIds(idList);
System.out.println("受影响行数:"+result);
}
运行测试方法,结果

通过map条件删除记录
@Test public void testDeleteByMap(){
//根据map集合中所设置的条件删除记录
// DELETE FROM user WHERE name = ? AND age = ?
Map<String, Object> map = new HashMap<>();
map.put("age", 21);
map.put("name", "Sandy");
int result = userMapper.deleteByMap(map);
System.out.println("受影响行数:"+result);
}
运行测试方法,结果

测试MaseMapper的修改功能
@Test public void testUpdateById(){
User user = new User();
user.setId(5L);
user.setName("修改成李四");
user.setAge(99);
user.setEmail("11111@qq.ccc");
//UPDATE user SET name=?, age=? WHERE id=?
int result = userMapper.updateById(user);
System.out.println("受影响行数:"+result);
}
运行测试方法,结果

测试MaseMapper的查询功能
根据id查询用户信息
@Test public void testSelectById(){
//根据id查询用户信息
// SELECT id,name,age,email FROM user WHERE id=?
User user = userMapper.selectById(5L);
System.out.println(user);
}
运行测试方法,结果

根据多个id查询多个用户信息
@Test public void testSelectBatchIds(){
//根据多个id查询多个用户信息
// SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
List<Long> idList = Arrays.asList(4L, 5L);
List<User> list = userMapper.selectBatchIds(idList);
list.forEach(System.out::println);
}
运行测试方法,结果

通过map条件查询用户信息
@Test public void testSelectByMap(){
//通过map条件查询用户信息
// SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
Map<String, Object> map = new HashMap<>();
map.put("age", 20);
map.put("name", "Jack");
List<User> list = userMapper.selectByMap(map);
list.forEach(System.out::println);
}
运行测试方法,结果

查询所有数据
@Test
public void testSelectList(){
//selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
List<User> list = userMapper.selectList(null);
list.forEach(System.out::println);
}
运行测试方法,结果

通过观察BaseMapper中的方法,大多方法中都有Wrapper类型的形参,此为条件构造器,可针 对于SQL语句设置不同的条件,若没有条件,则可以为该形参赋值null,即查询(删除/修改)所 有数据
自定义查询(多表操作)
mapper生成的只适用于单表的增删改查,如果要多表增删改查就不满足需求了
在yml文件中进行配置,如果是要自定义就需要单独配置,这里mybatis_plus默认配置了一个,所以yml中就不需要配置了

mybatis_plus只做增强不做改变,所以mybatis中怎么使用,那么在mybatis_plus就怎么使用
创建UserMapper.xml

代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.imooc.mapper.UserMapper">
<select id="selectMapById" resultType="map">
select id,name,age,email from user where id = #{id}
</select>
</mapper>
然后在UserMapper文件中编写方法
package com.imooc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.imooc.pojo.User;
import org.springframework.stereotype.Repository;
import java.util.Map;
@Repository
public interface UserMapper extends BaseMapper<User> {
Map<String,Object> selectMapById(Long id);
}
在测试类中新增方法
@Test public void testSelectMapById(){
//通过map条件查询用户信息
// SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
Map<String, Object> map = userMapper.selectMapById(1L);
System.out.println(map);
}
运行测试方法,结果

通用Service
说明:
通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删
除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
泛型 T 为任意实体对象
建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承
Mybatis-Plus 提供的基类
官网地址:https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%
A3
IService
MyBatis-Plus中有一个接口 IService和其实现类 ServiceImpl,封装了常见的业务层逻辑 详情查看源码IService和ServiceImp
按住Ctrl键然后点击BaseMapper即可看到对应的接口实现

这里可查看对应实现的方法

创建Service接口和实现类

UserService接口代码
package com.imooc.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.imooc.pojo.User;
//UserService继承IService模板提供的基础功能
public interface UserService extends IService<User> {
}
UserServiceImpl接口代码
package com.imooc.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.imooc.mapper.UserMapper;
import com.imooc.pojo.User;
import com.imooc.service.UserService;
import org.springframework.stereotype.Service;
/**
* ServiceImpl实现了IService,提供了IService中基础功能的实现 * 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
测试查询记录数
新建测试类MybatisPlusServiceTest中新增testGetCount方法
package com.imooc;
import com.imooc.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MybatisPlusServiceTest {
@Autowired
private UserService userService;
@Test
public void testGetCount(){
long count = userService.count();
System.out.println("总记录数:" + count);
}
}
运行测试方法,结果

测试批量插入
@Test
public void testSaveBatch(){
// SQL长度有限制,海量数据插入单条SQL无法实行,
// 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("ybc" + i);
user.setAge(20 + i);
users.add(user);
}
//SQL:INSERT INTO user ( name, age ) VALUES ( ?, ? )
userService.saveBatch(users);
}
运行测试方法,结果

常用注解
@TableName
经过以上的测试,在使用MyBatis-Plus实现基本的CRUD时,我们并没有指定要操作的表,只是在 Mapper接口继承BaseMapper时,设置了泛型User,而操作的表为user表 由此得出结论,MyBatis-Plus在确定操作的表时,由BaseMapper的泛型决定,即实体类型决 定,且默认操作的表名和实体类型的类名一致
问题
若实体类类型的类名和要操作的表的表名不一致,会出现什么问题?
我们将表user更名为t_user,测试查询功能 程序抛出异常,Table 'mybatis_plus.user' doesn't exist,因为现在的表名为t_user,而默认操作 的表名和实体类型的类名一致,即user表

通过@TableName解决问题
在实体类类型上添加@TableName("t_user"),标识实体类对应的表,即可成功执行SQL语句

通过全局配置解决问题
在开发的过程中,我们经常遇到以上的问题,即实体类所对应的表都有固定的前缀,例如t_或tbl_ 此时,可以使用MyBatis-Plus提供的全局配置,为实体类所对应的表名设置默认的前缀,那么就 不需要在每个实体类上通过@TableName标识实体类对应的表
# 配置MyBatis日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默认前缀
table-prefix: t_
@TableId
经过以上的测试,MyBatis-Plus在实现CRUD时,会默认将id作为主键列,并在插入数据时,默认 基于雪花算法的策略生成id
问题
若实体类和表中表示主键的不是id,而是其他字段,例如uid,MyBatis-Plus会自动识别uid为主 键列吗? 我们实体类中的属性id改为uid,将表中的字段id也改为uid,测试添加功能
程序抛出异常,Field 'uid' doesn't have a default value,说明MyBatis-Plus没有将uid作为主键 赋值

通过@TableId解决问题
在实体类中uid属性上通过@TableId将其标识为主键,即可成功执行SQL语句

@TableId的value属性
若实体类中主键对应的属性为id,而表中表示主键的字段为uid,此时若只在属性id上添加注解 @TableId,则抛出异常Unknown column 'id' in 'field list',即MyBatis-Plus仍然会将id作为表的 主键操作,而表中表示主键的是字段uid 此时需要通过@TableId注解的value属性,指定表中的主键字段,**@TableId("uid")**或 @TableId(value="uid")

@TableId的type属性
type属性用来定义主键策略
常用的主键策略(更多策略请查看官方地址)

配置全局策略
# 配置MyBatis日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默认前缀
table-prefix: t_
# 配置MyBatis-Plus的主键策略
id-type: auto
@TableField
经过以上的测试,我们可以发现,MyBatis-Plus在执行SQL语句时,要保证实体类中的属性名和 表中的字段名一致 如果实体类中的属性名和字段名不一致的情况,会出现什么问题呢?
情况1
若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格 例如实体类属性userName,表中字段user_name 此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格 相当于在MyBatis中配置
情况2
若实体类中的属性和表中的字段不满足情况1 例如实体类属性name,表中字段username 此时需要在实体类属性上使用@TableField("username")设置属性所对应的字段名

@TableLogic
逻辑删除
物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库
中仍旧能看到此条数据记录
使用场景:可以进行数据恢复
实现逻辑删除
step1:数据库中创建逻辑删除状态列,设置默认值为0

step2:实体类中添加逻辑删除属性

step3:测试
测试删除功能,真正执行的是修改 UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0 测试查询功能,被逻辑删除的数据默认不会被查询 SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0
条件构造器和常用接口
**具体使用请查看条件构造器这一篇文章**
查询方式 说明
setSqlSelect 设置 SELECT 查询字段
where WHERE 语句,拼接 + WHERE 条件
and AND 语句,拼接 + AND 字段=值
andNew AND 语句,拼接 + AND (字段=值)
or OR 语句,拼接 + OR 字段=值
orNew OR 语句,拼接 + OR (字段=值)
eq 等于=
allEq 基于 map 内容等于=
ne 不等于<>
gt 大于>
ge 大于等于>=
lt 小于<
le 小于等于<=
like 模糊查询 LIKE
notLike 模糊查询 NOT LIKE
in IN 查询
notIn NOT IN 查询
isNull NULL 值查询
isNotNull IS NOT NULL
groupBy 分组 GROUP BY
having HAVING 关键词
orderBy 排序 ORDER BY
orderAsc ASC 排序 ORDER BY
orderDesc DESC 排序 ORDER BY
exists EXISTS 条件语句
notExists NOT EXISTS 条件语句
between BETWEEN 条件语句
notBetween NOT BETWEEN 条件语句
addFilter 自由拼接 SQL
last 拼接在最后,例如:last(“LIMIT 1”)
wapper介绍

- Wrapper : 条件构造抽象类,最顶端父类
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
- QueryWrapper : 查询条件封装
- UpdateWrapper : Update 条件封装
- AbstractLambdaWrapper : 使用Lambda 语法
- LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper : Lambda 更新封装Wrapper
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper
例1:组装查询条件
新建测试类MybatisPlusServiceTest中新增方法 test01
package com.imooc;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imooc.mapper.UserMapper;
import com.imooc.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class MybatisPlusServiceTest {
@Autowired
private UserMapper userMapper;
@Test
public void test01(){
//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
// SELECT id,name AS name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a") .between("age", 20, 30) .isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
}
运行测试方法,结果

例2:组装排序条件
在测试类MybatisPlusServiceTest中新增方法 test02
@Test
public void test02(){
//按年龄降序查询用户,如果年龄相同则按id升序排列
// SELECT id,name AS name,age,email,is_deleted FROM user WHERE is_deleted=0 ORDER BY age DESC,id ASC
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper .orderByDesc("age") .orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
运行测试方法,结果

例3:组装删除条件
在测试类MybatisPlusServiceTest中新增方法 test03
@Test
public void test03(){
//删除email为空的用户
// DELETE FROM user WHERE (email IS NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
//条件构造器也可以构建删除语句的条件
int result = userMapper.delete(queryWrapper);
System.out.println("受影响的行数:" + result);
}
运行测试方法,结果

例4:条件的优先级
方式一:在测试类MybatisPlusServiceTest中新增方法 test04
@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
// UPDATE user SET age=?, email=? WHERE (name LIKE ? AND age > ? OR email IS NULL)
queryWrapper
.like("name", "a")
.gt("age", 20)
.or()
.isNull("email");
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}
方式二:在测试类MybatisPlusServiceTest中新增方法 test04
@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
// UPDATE user SET age=?, email=? WHERE (name LIKE ? AND (age > ? OR email IS NULL))
// lambda表达式内的逻辑优先运算 queryWrapper
queryWrapper
.like("name", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}
运行测试方法,结果

例5:组装select子句
在测试类MybatisPlusServiceTest中新增方法 test05
@Test
public void test05() {
//查询用户信息的name和age字段
// SELECT name,age FROM user
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name", "age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值 为null
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
运行测试方法,结果

例6:实现子查询
在测试类MybatisPlusServiceTest中新增方法 test06
@Test
public void test06() {
//查询id小于等于3的用户信息
// SELECT id,name AS name,age,email,is_deleted FROM user WHERE (id IN (select id from user where id <= 3))
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from user where id <= 3");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
运行测试方法,结果

UpdateWrapper
在测试类MybatisPlusServiceTest中新增方法 test07
@Test
public void test07() {
//将(年龄大于20或邮箱为null)并且用户名中包含有a的用户信息修改
// 组装set子句以及修改条件
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
// lambda表达式内的逻辑优先运算
updateWrapper
.set("age", 18)
.set("email", "12343545@atguigu.com")
.like("name", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
//这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
// UPDATE user SET user=?, age=?,email=? WHERE (user LIKE ? AND (age > ? OR email IS NULL))
// User user = new User();
// user.setName("张三");
//int result = userMapper.update(user, updateWrapper);
// UPDATE user SET age=?,email=? WHERE (user LIKE ? AND (age > ? OR email IS NULL))
int result = userMapper.update(null, updateWrapper);
System.out.println(result);
}
运行测试方法,结果

condition
在真正开发的过程中,组装条件是常见的功能,而这些条件数据来源于用户输入,是可选的,因 此我们在组装这些条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若 没有选择则一定不能组装,以免影响SQL执行的结果
思路一:
在测试类MybatisPlusServiceTest中新增方法 test08
@Test
public void test08() {
//定义查询条件,有可能为null(用户未输入或未选择)
String name = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace) 构成
if(StringUtils.isNotBlank(name)){
queryWrapper.like("username","a");
}
if(ageBegin != null){
queryWrapper.ge("age", ageBegin);
}
if(ageEnd != null){
queryWrapper.le("age", ageEnd);
}
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >= ? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
运行测试方法,结果

思路二:
上面的实现方案没有问题,但是代码比较复杂,我们可以使用带condition参数的重载方法构建查 询条件,简化代码的编写
在测试类MybatisPlusServiceTest中新增方法 test08UseCondition
@Test
public void test08UseCondition() {
//定义查询条件,有可能为null(用户未输入或未选择)
String name = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace) 构成
queryWrapper
.like(StringUtils.isNotBlank(name), "username", "a")
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >= ? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
运行测试方法,结果

LambdaQueryWrapper
在测试类MybatisPlusServiceTest中新增方法 test09
@Test
public void test09() {
//定义查询条件,有可能为null(用户未输入)
String name = "a";
Integer ageBegin = 10;
Integer ageEnd = 24;
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
//避免使用字符串表示字段,防止运行时错误
queryWrapper
.like(StringUtils.isNotBlank(name), User::getName, name)
.ge(ageBegin != null, User::getAge, ageBegin)
.le(ageEnd != null, User::getAge, ageEnd);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
运行测试方法,结果

LambdaUpdateWrapper
在测试类MybatisPlusServiceTest中新增方法 test10
@Test
public void test10() {
//组装set子句
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper
.set(User::getAge, 18)
.set(User::getEmail, "bbbbbbbbbb@atguigu.com")
.like(User::getName, "a")
.and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail));
//lambda 表达式内的逻辑优先运算
User user = new User();
int result = userMapper.update(user, updateWrapper);
System.out.println("受影响的行数:" + result);
}
运行测试方法,结果

插件
分页插件
MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
添加配置类
创建config文件夹,再创建MybatisPlusConfig配置类

代码如下
package com.imooc.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.imooc.mapper")//可以将主类中的注解移到此处
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
测试
新建测试类MybatisPlusTest中新增方法 testPage
package com.imooc;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.imooc.mapper.UserMapper;
import com.imooc.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class MybatisPlusTest {
@Autowired
private UserMapper userMapper;
@Test
public void testPage(){
//设置分页参数
Page<User> page = new Page<>(1, 5);
userMapper.selectPage(page, null);
//获取分页数据
List<User> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
}
}
运行测试方法,结果

xml自定义分页
UserMapper中定义接口方法
package com.imooc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.imooc.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper<User> {
Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
}
UserMapper.xml中编写SQL
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.imooc.mapper.UserMapper">
<select id="selectPageVo" resultType="User">
SELECT * FROM user WHERE age > #{age}
</select>
</mapper>
测试
新建测试类MybatisPlusTest中新增方法 MybatisPlusTest
package com.imooc;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.imooc.mapper.UserMapper;
import com.imooc.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class MybatisPlusTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectPageVo(){
//设置分页参数
Page<User> page = new Page<>(1, 5);
userMapper.selectPageVo(page, 20);
//获取分页数据
List<User> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
}
}
在配置类中新增路径

运行测试方法,结果

乐观锁
场景
一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小 李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太 高,可能会影响销量。又通知小王,你把商品价格降低30元。 此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王 也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据 库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就 完全被小王的覆盖了。 现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1 万多。
乐观锁与悲观锁
上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过 了,则重新取出的被修改后的价格,150元,这样他会将120元存入数据库。 如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证 最终的价格是120元。
模拟修改冲突
数据库中增加商品表
在mybatis_plus数据库中添加t_product表
CREATE TABLE t_product
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
price INT(11) DEFAULT 0 COMMENT '价格',
VERSION INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id)
);
添加数据
INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
添加实体
在pojo文件夹新增Product实体类

package com.imooc.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
//因为没有配置全局数据表前缀,所以这里用到注解,因为表名是t_produce而不是produce
@TableName("t_product")
public class Product {
private Long id;
private String name;
private Integer price;
private Integer version;
}
添加mapper

package com.imooc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.imooc.pojo.Product;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductMapper extends BaseMapper<Product> {
}
测试
新建测试类MybatisPlusPro中新增方法 testConcurrentUpdate
package com.imooc;
import com.imooc.mapper.ProductMapper;
import com.imooc.pojo.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MybatisPlusPro {
@Autowired
private ProductMapper productMapper;
@Test
public void testConcurrentUpdate(){
//1、小李
Product p1 = productMapper.selectById(1L);
System.out.println("小李取出的价格:" + p1.getPrice());
//2、小王
Product p2 = productMapper.selectById(1L);
System.out.println("小王取出的价格:" + p2.getPrice());
//3、小李将价格加了50元,存入了数据库
p1.setPrice(p1.getPrice() + 50);
int result1 = productMapper.updateById(p1);
System.out.println("小李修改结果:" + result1);
//4、小王将商品减了30元,存入了数据库
p2.setPrice(p2.getPrice() - 30);
int result2 = productMapper.updateById(p2);
System.out.println("小王修改结果:" + result2);
//最后的结果
Product p3 = productMapper.selectById(1L);
//价格覆盖,最后的结果:70
System.out.println("最后的结果:" + p3.getPrice());
}
}
运行测试方法,结果

乐观锁实现流程
数据库中添加version字段
取出记录时,获取当前version
SELECT id,`name`,price,`version` FROM t_product WHERE id=1
更新时,version + 1,如果where语句中的version版本不对,则更新失败
UPDATE t_product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND `version`=1
Mybatis-Plus实现乐观锁

修改实体类
修改pojo文件夹中Product实体类 @Version
package com.imooc.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
@TableName("t_product")
public class Product {
private Long id;
private String name;
private Integer price;
@Version
private Integer version;
}
添加乐观锁插件配置
在config文件夹MybatisPlusConfig类中添加乐观锁插件
package com.imooc.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.imooc.mapper")//可以将主类中的注解移到此处
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
测试修改冲突
小李查询商品信息: SELECT id,name,price,version FROM t_product WHERE id=? 小王查询商品信息: SELECT id,name,price,version FROM t_product WHERE id=? 小李修改商品价格,自动将version+1 UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=? Parameters: 外星人笔记本(String), 150(Integer), 1(Integer), 1(Long), 0(Integer) 小王修改商品价格,此时version已更新,条件不成立,修改失败 UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=? Parameters: 外星人笔记本(String), 70(Integer), 1(Integer), 1(Long), 0(Integer) 最终,小王修改失败,查询价格:150 SELECT id,name,price,version FROM t_product WHERE id=?
优化流程
在测试类MybatisPlusPro中新增方法 testConcurrentVersionUpdate
package com.imooc;
import com.imooc.mapper.ProductMapper;
import com.imooc.pojo.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MybatisPlusPro {
@Autowired
private ProductMapper productMapper;
@Test
public void testConcurrentVersionUpdate(){
//小李取数据
Product p1 = productMapper.selectById(1L);
//小王取数据
Product p2 = productMapper.selectById(1L);
//小李修改 + 50
p1.setPrice(p1.getPrice() + 50);
int result1 = productMapper.updateById(p1);
System.out.println("小李修改的结果:" + result1);
//小王修改 - 30
p2.setPrice(p2.getPrice() - 30);
int result2 = productMapper.updateById(p2);
System.out.println("小王修改的结果:" + result2);
if(result2 == 0){
//失败重试,重新获取version并更新
p2 = productMapper.selectById(1L);
p2.setPrice(p2.getPrice() - 30);
result2 = productMapper.updateById(p2);
}
System.out.println("小王修改重试的结果:" + result2);
//老板看价格
Product p3 = productMapper.selectById(1L);
System.out.println("老板看价格:" + p3.getPrice());
}
}
运行测试方法,结果

通用枚举
表中的有些字段值是固定的,例如性别(男或女),此时我们可以使用MyBatis-Plus的通用枚举 来实现
数据库表添加字段sex

创建通用枚举类型
创建enums文件夹,再创建SexEnum枚举

package com.imooc.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;
@Getter
public enum SexEnum {
MALE(1, "男"),
FEMALE(2, "女");
@EnumValue//将注解所标识的属性的值存储到数据库中(如果要存sexName的值,那边就在sexName这里添加这个注解)
private Integer sex;
private String sexName;
SexEnum(Integer sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
}
然后在application.yml配置类中配置扫描枚举文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
# 配置MyBatis日志
mybatis-plus:
type-aliases-package: com.imooc.pojo
# 配置扫描通用枚举
type-enums-package: com.imooc.enums
然后在User实体类中引入枚举字段

新建测试类MybatisTestSexEnum中新增方法 testSexEnum
package com.imooc;
import com.imooc.enums.SexEnum;
import com.imooc.mapper.UserMapper;
import com.imooc.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MybatisTestSexEnum {
@Autowired
private UserMapper userMapper;
@Test
public void testSexEnum(){
User user = new User();
user.setName("Enum");
user.setAge(20);
//设置性别信息为枚举项,会将@EnumValue注解所标识的属性值存储到数据库
user.setSex(SexEnum.MALE);
//INSERT INTO t_user ( username, age, sex ) VALUES ( ?, ?, ? )
// Parameters: Enum(String), 20(Integer), 1(Integer)
int result = userMapper.insert(user);
System.out.println("result:"+result);
}
}
运行测试方法,结果

代码生成器
引入依赖
官网上有,直接看代码生成器(新)
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
快速生成
新建测试类FastAutoGeneratorTest

package com.imooc;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
public class FastAutoGeneratorTest {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis_plus? characterEncoding=utf-8&userSSL=false", "root", "root")
.globalConfig(builder -> {
builder.author("作者") // 设置作者
// .enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://mybatis_plus"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com") // 设置父包名
.moduleName("imooc") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://mybatis_plus")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("t_user") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker 引擎模板,默认的是Velocity引擎模板
.execute();
}
}
根据上面代码,然后在D盘创建mybatis_plus目录,数据库中的表名是t_user
运行测试方法,结果

在D判断打开mybatis_plus可以查看生成的目录文件

更多方式请在官网查看
多数据源
适用于多种场景:纯粹多库、 读写分离、 一主多从、 混合模式等 目前我们就来模拟一个纯粹多库的一个场景,其他场景类似 场景说明: 我们创建两个库,分别为:mybatis_plus(以前的库不动)与mybatis_plus_1(新建),将 mybatis_plus库的product表移动到mybatis_plus_1库,这样每个库一张表,通过一个测试用例 分别获取用户数据与商品数据,如果获取到说明多库模拟成功
创建数据库及表
创建数据库mybatis_plus_1和表product
CREATE DATABASE `mybatis_plus_1`;
use `mybatis_plus_1`;
CREATE TABLE product (
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
price INT(11) DEFAULT 0 COMMENT '价格',
version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id)
);
添加测试数据
INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
删除mybatis_plus库product表
use mybatis_plus; DROP TABLE IF EXISTS t_product;
最终结构

引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>8.0.20</scope>
</dependency>
//这个才是引入的依赖,上面都是上面案例需要的
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
配置多数据源
说明:注释掉之前的数据库连接,添加新配置
spring:
# 配置数据源信息
datasource:
dynamic:
# 设置默认的数据源或者数据源组,默认值即为master
primary: master
# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
strict: false
datasource:
master:
url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
slave_1:
url: jdbc:mysql://localhost:3306/mybatis_plus_1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
# 配置MyBatis日志
mybatis-plus:
type-aliases-package: com.imooc.pojo
创建pojo实体类

创建User实体类
package com.imooc.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data //lombok注解
@TableName("user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
创建Product实体类
package com.imooc.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("product")
public class Product {
private Integer id;
private String name;
private Integer price;
private Integer version;
}
创建mapper接口

创建UserMapper接口
package com.imooc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.imooc.pojo.User;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper<User> {
}
创建ProductMapper接口
package com.imooc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.imooc.pojo.Product;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductMapper extends BaseMapper<Product> {
}
入口配置扫描mapper
package com.imooc;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.imooc.mapper")//可以将主类中的注解移到此处,配置mapper扫描
public class MybaisSdutyApplication {
public static void main(String[] args) {
SpringApplication.run(MybaisSdutyApplication.class, args);
}
}
创建service

创建UserService接口
package com.imooc.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.imooc.pojo.User;
//UserService继承IService模板提供的基础功能
public interface UserService extends IService<User> {
}
创建ProductService接口
package com.imooc.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.imooc.pojo.Product;
//UserService继承IService模板提供的基础功能
public interface ProductService extends IService<Product> {
}
在impl文件夹下创建UserServiceImpl类
package com.imooc.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.imooc.mapper.UserMapper;
import com.imooc.pojo.User;
import com.imooc.service.UserService;
import org.springframework.stereotype.Service;
/**
* ServiceImpl实现了IService,提供了IService中基础功能的实现 * 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
*/
@DS("master") //指定所操作的数据源
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
在impl文件夹下创建ProductServiceImpl类
package com.imooc.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.imooc.mapper.ProductMapper;
import com.imooc.pojo.Product;
import com.imooc.service.ProductService;
import org.springframework.stereotype.Service;
/**
* ServiceImpl实现了IService,提供了IService中基础功能的实现 * 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
*/
@DS("slave_1")
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}
在测试类中创建testDynamicDataSource方法
package com.imooc;
import com.imooc.service.ProductService;
import com.imooc.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MybatissdutyApplicationTests {
@Autowired
private UserService userService;
@Autowired
private ProductService productService;
@Test
public void testDynamicDataSource(){
System.out.println(userService.getById(1L));
System.out.println(productService.getById(1L));
}
}
运行测试方法,结果

结果: 1、都能顺利获取对象,则测试成功 2、如果我们实现读写分离,将写操作方法加上主库数据源,读操作方法加上从库数据源,自动切 换,是不是就能实现读写分离?
MyBatisX插件
MyBatis-Plus为我们提供了强大的mapper和service模板,能够大大的提高开发效率 但是在真正开发过程中,MyBatis-Plus并不能为我们解决所有问题,例如一些复杂的SQL,多表 联查,我们就需要自己去编写代码和SQL语句,我们该如何快速的解决这个问题呢,这个时候可 以使用MyBatisX插件 MyBatisX一款基于 IDEA 的快速开发插件,为效率而生。
MyBatisX插件用法:https://baomidou.com/pages/ba5b24/