Mybatis-Plus的概念
MyBatis-Plus (opens new window)
(简称
MP
)是⼀个
MyBatis (opens new window)
的增强⼯具,在
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
操作智能分析阻断,也可⾃定义拦截规则,预防误操作。
快速入门
引⼊依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>最新版本</version>
</dependency>
配置数据源
在
application.yml
配置⽂件中添加
MySQL
数据库的相关配置:
# DataSource Config
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: 1234
实体类
我们以经典的 Emp 表作为实例,进⾏讲解,编写对应的实体 Emp 类。
@Data
public class Emp implements Serializable {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private String hiredate;
private Double sal;
private Double comm;
private Integer deptno;
private Integer empstate;
}
Mapper 类
编写
mapper
包下的
EmpMapper
接⼝
@Mapper
public interface EmpMapper extends BaseMapper<Emp>{
}
业务层
编写 service 包下的 EmpService 接⼝
public interface EmpService extends IService<Emp>{
}
编写
service.impl
包下的
EmpServiceImpl
实现类
@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements EmpService {
}
开始使⽤
添加测试类,进⾏功能测试
@SpringBootTest
public class SampleTest {
@Resource
private EmpService empService;
@Test
void findTest() {
List<Emp> list = empService.list();
list.forEach(System.out::println);
}
}
通过以上⼏个简单的步骤,我们就实现了
Emp
表的
CRUD
功能,甚⾄连
XML
⽂件都不⽤编写!
从以上步骤中,我们可以看到集成
MyBatis-Plus
⾮常的简单,只需要引⼊
starter
⼯程,并配置
mapper
扫描路径
即可。
但
MyBatis-Plus
的强⼤远不⽌这些功能,想要详细了解
MyBatis-Plus
的强⼤功能?那就继续往下看吧!
MyBatis-Plus 注解
@TableName
描述:表名注解,标识实体类对应的表
使⽤位置:实体类
@Data
@TableName(value = "emp")
public class Emp implements Serializable {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private String hiredate;
private Double sal;
private Double comm;
private Integer deptno;
private Integer empstate;
}
注意
:
@TableName
注解可省略,如省略对应类名的表
属性列表:
![](https://i-blog.csdnimg.cn/blog_migrate/519ae185a0bdf14b6f829fe77f2d5e54.png)
关于 autoResultMap 的说明:
MP
会⾃动构建⼀个
resultMap
并注⼊到
MyBatis
⾥(⼀般⽤不上),请注意以下内容:
因为
MP
底层是
MyBatis
,所以
MP
只是帮您注⼊了常⽤
CRUD
到
MyBatis
⾥,注⼊之前是动态的(根据您的 Entity 字段以及注解变化⽽变化),但是注⼊之后是静态的(等于
XML
配置中的内容)。
⽽对于
typeHandler
属性,
MyBatis
只⽀持写在
2
个地⽅
:
1.
定义在
resultMap
⾥,作⽤于查询结果的封装
2.
定义在
insert
和
update
语句的
#{property}
中的
property
后⾯(例:
#
{property,typehandler=xxx.xxx.xxx}
),并且只作⽤于当前
设置值。
除了以上两种直接指定
typeHandler
的形式,
MyBatis
有⼀个全局扫描⾃定义
typeHandler
包的配置,原理是
根据您的
property
类型去找其对应的
typeHandler
并使⽤。
@TableId
描述:主键注解
使⽤位置:实体类主键字段
@Data
@TableName(value = "emp")
public class Emp implements Serializable {
@TableId(value = "empno",type = IdType.AUTO)
private Integer empno;
// 其他省略
}
属性列表
![](https://i-blog.csdnimg.cn/blog_migrate/065a85840fbf625fee4af8bdef09e501.png)
type
属性概述:
![](https://i-blog.csdnimg.cn/blog_migrate/e485d2e842b8d6bdaff7c7612561762f.png)
@TableField
描述:字段注解(⾮主键)
@Data
@TableName(value = "emp")
public class Emp implements Serializable {
@TableField(value = "ename")
private String ename;
@TableField(value = "hiredate")
private String hireDate;
// 其他省略
}
属性列表:
![](https://i-blog.csdnimg.cn/blog_migrate/c2a8b9ee8c1db2c99faec27642ae4411.png)
关于
jdbcType
和
typeHandler
以及
numericScale
的说明
:
numericScale
只⽣效于
update
的
sql. jdbcType
和
typeHandler
如果不配合
@TableName#autoResultMap = true⼀起使⽤
,
也只⽣效于
update
的
sql.
对于
typeHandler
如果你的字段类型和
set
进去的类型为
equals
关系,
则只需要让你的
typeHandler
让
Mybatis
加载到即可
,
不需要使⽤注解。
@TableLogic
描述:表字段逻辑处理注解(逻辑删除)
逻辑删除
我们在实际使⽤中的删除操作,其实并没有删除表中的数据,仅仅是将表的数据
隐藏
掉,查询时不查询这些
隐藏的数据。
全局⽅式
在 application.yml 中进⾏配置:
mybatis-plus:
global-config:
db-config:
logic-delete-field: state # 全局逻辑删除的实体字段名
logic-delete-value: 0 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 1 # 逻辑未删除值(默认为 0)
注意:配置好后,所有实体不需额外操作,但必须保证存在 state 字段
局部⽅式
需要在每个所需实体类中引⼊ @TableLogic 注解。
package com.jiazhong.bean;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName(value = "emp") // 对应的数据库的名称
public class Emp implements Serializable {
// 主键对应 AUTO:自增
@TableId(value = "empno", type = IdType.AUTO)
private Integer empNo; // 默认情况下对应数据库表中的名称为:emp_no
@TableField(value = "ename") // 自动映射数据库表中的列名为ename
private String ename;
private String job;
private Integer mgr;
@TableField(value = "hiredate",fill = FieldFill.INSERT)
private String hireDate; // 当save时自动填充内容
private Double sal;
private Double comm;
@TableField("deptno")
private Integer deptNo;
@TableLogic(delval = "0",value = "1") // 非全局控制逻辑删除
private Integer state;
// 这个列在数据库中不存在
@TableField(exist = false)
private String allCount;
}
说明
只对⾃动注⼊的
sql
起效:
插⼊
:
不作限制
查找
:
追加
where
条件过滤掉已删除数据
,
如果使⽤
wrapper.entity
⽣成的
where
条件也会⾃动追加该字段.
更新
:
追加
where
条件防⽌更新到已删除数据
,
如果使⽤
wrapper.entity
⽣成的
where
条件也会⾃动追加该字段
删除
:
转变为 更新
例如
:
删除
:
update user set deleted=1 where id = 1 and deleted=0
查找
:
select id,name,deleted from user where deleted=0
字段类型⽀持说明:
⽀持所有数据类型
(
推荐使⽤
Integer,Boolean,LocalDateTime)
如果数据库字段使⽤
datetime ,
逻辑未删除值和已删除值⽀持配置为字符串
null,
另⼀个值⽀持配置为函数来获取值如now()
逻辑删除是为了⽅便数据恢复和保护数据本身价值等等的⼀种⽅案,但实际就是删除。
如果你需要频繁查出来看就不应使⽤逻辑删除,⽽是以⼀个状态去表示。
⾃动填充功能
在数据表的设计中,经常需要加⼀些字段,如:创建时间,最后修改时间等,此时可以使⽤ MyBatis-Plus 来帮我 们进⾏⾃动维护。
⾃动填充⽅式有两种,分别是:
通过数据库完成⾃动填充
使⽤程序完成⾃动填充
数据库⾃动填充
这⾥没什么好说的,就是正常书写就⾏,在添加时,有默认值的列所对应的属性不赋值,数据库会⾃动赋予默认值.
程序⾃动填充
Emp.java
package com.jiazhong.bean;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName(value = "emp") // 对应的数据库的名称
public class Emp implements Serializable {
// 主键对应 AUTO:自增
@TableId(value = "empno", type = IdType.AUTO)
private Integer empNo; // 默认情况下对应数据库表中的名称为:emp_no
@TableField(value = "ename") // 自动映射数据库表中的列名为ename
private String ename;
private String job;
private Integer mgr;
@TableField(value = "hiredate",fill = FieldFill.INSERT)
private String hireDate; // 当save时自动填充内容
private Double sal;
private Double comm;
@TableField("deptno")
private Integer deptNo;
@TableLogic(delval = "0",value = "1") // 非全局控制逻辑删除
private Integer state;
// 这个列在数据库中不存在
@TableField(exist = false)
private String allCount;
}
创建util类,在创建 NowDateHandler.java
package com.jiazhong.util;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class NowDateHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "hireDate", this::getNowDate, String.class);
}
@Override
public void updateFill(MetaObject metaObject) {
}
private String getNowDate() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(new Date());
}
}
当使用save()方法时,会自动的将hiredate设为现在的时间。
@Test
public void d() {
Emp emp = new Emp();
emp.setComm(980.0);j
emp.setSal(9876.0);
emp.setJob("程序员");
emp.setEname("火鳞飞");
emp.setMgr(8787);
emp.setDeptNo(10);
emp.setState(1);
boolean b = service.save(emp);
System.out.println(b);
}
而在 NowDateHandler.java 文件中还有一个 updateFill()方法,则对应的是update()方法。
说明
填充原理是直接给
entity
的属性设置值
!!!
注解则是指定该属性在对应情况下必有值
,
如果⽆值则⼊库会是
null
MetaObjectHandler
提供的默认⽅法的策略均为
:
如果属性有值则不覆盖
,
如果填充值为
null
则不填充
字段必须声明
TableField
注解
,
属性
fill
选择对应策略
,
该声明告知
Mybatis-Plus
需要预留注⼊
SQL
字段
填充处理器
NowDateHandler
在
Spring Boot
中需要声明
@Component
或
@Bean
注⼊
要想根据注解
FieldFill.xxx
和字段名以及字段类型来区分必须使⽤⽗类的
strictInsertFill
或者
strictUpdateFill
⽅法
不需要根据任何来区分可以使⽤⽗类的
fillStrategy
⽅法
update(T t,Wrapper updateWrapper)
时
t
不能为空
,
否则⾃动填充失效
条件构造器
在
Mybatis-Plus
中提了构造条件的类
Wrapper
,
它可以根据⾃⼰的意图定义我们需要的条件。
Wrapper
是⼀个抽象
类,⼀般情况下我们⽤它的⼦类
QueryWrapper
来实现⾃定义条件查询。
API
Test.java
package com.jiazhong.test;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.jiazhong.bean.Emp;
import com.jiazhong.mapper.EmpMapper;
import com.jiazhong.service.EmpService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
@SpringBootTest
public class a {
@Resource
private EmpService service;
@Test
public void f1() {
// 按照职位进行检索
// 书写条件构造器
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
// 设置条件 eq ne lt le gt ge
wrapper.eq("job", "程序员");
// 查询
List<Emp> list = service.list(wrapper);
list.forEach(System.out::println);
}
@Test
public void f2() {
// 按照职位进行探索
// 书写条件构造器
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
// 设置条件 eq ne lt le gt ge
// wrapper.eq("job", "程序员");
// 查询 and
wrapper.ge("sal", 3000);
wrapper.le("sal", 5000);
// wrapper.between("sal", 3000, 5000);
// 查询
List<Emp> list = service.list(wrapper);
list.forEach(System.out::println);
}
@Test
public void f3() {
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
wrapper.like("ename", "三");
// wrapper.likeLeft("ename","三");
// wrapper.likeRight("ename","三");
List<Emp> list = service.list(wrapper);
list.forEach(System.out::println);
}
@Test
public void f4() {
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
// wrapper.notIn("job", "歌手", "程序员", "页面设计");
wrapper.in("job", "歌手", "程序员", "页面设计");
List<Emp> list = service.list(wrapper);
list.forEach(System.out::println);
}
@Test
public void f5() {
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
wrapper.eq("job", "程序员")
.or()
.eq("job", "页面设计")
.or()
.eq("job", "歌手");
List<Emp> list = service.list(wrapper);
list.forEach(System.out::println);
}
@Test
public void f6() {
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
// wrapper.orderByAsc("sal");
// wrapper.orderByDesc("comm"); // 两者之间为and关系
wrapper.orderByDesc("sal", "comm"); // 两者效果一样
List<Emp> list = service.list(wrapper);
list.forEach(System.out::println);
}
@Test
public void f7() {
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
// 查询哪些列
wrapper.select("sal", "job");
List<Emp> list = service.list(wrapper);
list.forEach(System.out::println);
}
@Test
public void f8() {
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
wrapper.select("count(*)", "sum(sal)", "avg(sal)", "max(sal)", "min(sal)");
// 查询
List<Map<String, Object>> maps = service.listMaps(wrapper); // 键值对形式封装
System.out.println(maps);
}
@Test
public void f9() {
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
wrapper.groupBy("job");
wrapper.select("job", "count(*) as allCoount");
// 查询
List<Emp> list = service.list(wrapper);
list.forEach(System.out::println);
}
@Test
public void f10() {
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
wrapper.groupBy("job");
wrapper.select("job", "count(*) as alLCount");
wrapper.gt("sal", 3000);
wrapper.gt("sal", 1000);
wrapper.having("allCount>3");
// 查询
List<Emp> list = service.list(wrapper);
list.forEach(System.out::println);
}
@Test
public void f11() {
QueryWrapper<Emp> wrapper = new QueryWrapper<>();
// 拼接到sql语句之后
wrapper.last("and sal>1000");
// 查询
List<Emp> list = service.list(wrapper);
list.forEach(System.out::println);
}
@Test
public void g() {
UpdateWrapper<Emp> wrapper = new UpdateWrapper<>();
wrapper.set("job", "歌手");
wrapper.eq("empno", "7369");
service.update(wrapper);
}
@Test
public void h() {
EmpMapper mapper = (EmpMapper) service.getBaseMapper();
List<Emp> emp = mapper.findEmp();
emp.forEach(System.out::println);
}
}
AbstractWrapper
说明
:
QueryWrapper(LambdaQueryWrapper)
和
UpdateWrapper(LambdaUpdateWrapper)
的⽗类
⽤于⽣成
sql
的
where
条件
, entity
属性也⽤于⽣成
sql
的
where
条件
注意
: entity
⽣成的
where
条件与 使⽤各个
api
⽣成的
where
条件
没有任何关联⾏为
QueryWrapper
select(String... sqlSelect)
select(Predicate<TableFieldInfo> predicate)
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
设置查询字段
说明
:
以上⽅法分为两类
.
第⼆类⽅法为
:
过滤查询字段
(
主键除外
),
⼊参不包含
class
的调⽤前需要
wrapper
内的
entity
属性有值
!
这两类⽅法重复调⽤以最后⼀次为准
例
:
select("id", "name", "age")
例
:
select(i -> i.getProperty().startsWith("test"))
UpdateWrapper
说明
:
继承⾃
AbstractWrapper
,
⾃身的内部属性
entity
也⽤于⽣成
where
条件
及
LambdaUpdateWrapper
,
可以通过
new UpdateWrapper().lambda()
⽅法获取
!
set
set
(
String
column
,
Object
val
)
set
(
boolean
condition
,
String
column
,
Object
val
)
SQL SET
字段
例
:
set("name", "
⽼李头
")
例
:
set("name", "")
--->
数据库字段值变为
空字符串
例
:
set("name", null)
--->
数据库字段值变为
null
使⽤ XML 配置(结合Mybatis使用)
有的时候对于⼀些复杂 SQL 语句,尤其是动态SQL,使⽤注解⽐较麻烦,我们可以通过 XML 来书写。
application.yml 引⼊
mybatis-plus:
mapper-locations: classpath*:/mapper/EmpMapper.xml # mapper ⽂件位置
根据接⼝⽣成xml配置⽂件
所对应的EmpMapper.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.jiazhong.mapper.EmpMapper">
<select id="findEmp" resultType="com.jiazhong.bean.Emp">
select *
from emp
where state = 1
</select>
</mapper>
Test.java
@SpringBootTest
public class a {
@Resource
private EmpService service;
@Test
public void h() {
EmpMapper mapper = (EmpMapper) service.getBaseMapper();
List<Emp> emp = mapper.findEmp();
emp.forEach(System.out::println);
}
}