MyBatis-Plus的官方网站:
MyBatis-PlusMyBatis-Plus 官方文档https://baomidou.com/
MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变(无侵入,不修改源代码也无妨),为简化开发,提高效率而生。
MyBatis-Plus的愿景是成为MyBatis最好的搭档,就像魂斗罗中的1P、2P,基友搭配,效率翻倍!
大家在日常开发当中应该能够发现,单表的CRUD功能代码重复度很高,也没有什么难度,而这部分代码量往往比较大,开发起来比较费时。
因此,目前企业中都会使用一些组件来简化或省略单表的CRUD开发工作,目前在国内使用较多的一个组件就是MyBatis-Plus。
当然,MyBatis-Plus不仅仅可以简化单表的操作,而且还对MyBatis的功能有很多的增强,可以让我们的开发更加的简单、高效。
我们要实现单表的CRUD,只需要下面几步:
MyBatis-Plus官方提供了starter,其中集成了MyBatis和MyBatis-Plus的所有功能,并且实现了自动装配效果。
因此我们可以用MyBatis-Plus的starter代替MyBatis的starter:
com.baomidou
mybatis-plus-boot-starter
3.5.3.1
application.yml配置文件:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mp
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
logging:
level:
com.itheima: debug
pattern:
dateformat: HH:mm:ss
mybatis:
mapper-locations: classpath*:mapper/*.xml
为了简化单表操作的CRUD,MyBatis-Plus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD:
以后操作就不用再自定义mapper.xml映射文件写一大堆语句了,直接调API即可,会自动生成对应的SQL语句!
所以MyBatis它可以帮助我们自动生成SQL语句实现单表的增删改查,只需要继承BaseMapper就能省去所有的单表的CRUD,是不是非常简单~!
在刚才的入门案例当中,我们仅仅引入了依赖,继承了BaseMapper就能使用MyBatis-Plus,非常简单,但是问题来了,MyBatis-Plus如何知道我们要查询的是哪张表,表中有哪些字段呢?
回忆,自定义的UserMapper接口在继承BaseMapper接口时指定了一个泛型,也就是我们对应实体类的类型:
泛型接口BaseMapper所指定的泛型中的User类型就是与数据库对应的PO(POJO)实体类的类型:
MyBatis-Plus通过扫描PO实体类,并基于反射获取实体类信息作为数据表信息,从而生成增删改查的SQL语句,默认情况下是有一些约定的:
但很多情况下,如果我们的实体类不符合这些约定,默认的实现与实际场景不符,因此MyBatis-Plus提供了一些注解便于我们声明表信息:
如果实体类与表中字段的对应关系不符合MP的约定,此时就需要用到上面的注解来去标记了!
说明:
说明:
@TableId注解支持两个属性:
说明:
一般情况下我们并不需要给字段添加@TableField注解,一些特殊情况除外,使用@TableFileld的常见场景:
MyBatis-Plus也支持基于yaml文件的自定义配置,详见官方文档:
使用配置 | MyBatis-PlusMyBatis-Plus 官方文档https://www.baomidou.com/pages/56bac0/#%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AEMyBatis-Plus的配置项集成了MyBatis的原生配置和一些自己特有的配置,例如:
global-config:全局配置
全局配置的优先级是不如注解的优先级高的~!
上面大多数的配置都有默认值,因此我们都无需配置,但还有一些是没有默认值的,例如:
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,默认值
configuration:
map-underscore-to-camel-case: true # 是否开启下划线和驼峰的映射
cache-enabled: false # 是否开启二级缓存
global-config:
db-config:
id-type: assign_id # id为雪花算法生成
update-strategy: not_null # 更新策略:只更新非空字段
MyBatis-Plus使用的基本流程:
MyBatis-Plus支持各种复杂的where条件,可以满足日常开发的所有需求!
利用Wrapper来实现复杂查询~!
参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:
Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:
而QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:
而UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:
无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件,接下来看一些例子:
查询:查询出名字username中带o的,存储balance大于等于1000元的人的id、username、info、balance字段,代码如下:
select id, username, info, balance
from table
where username like '%o%'
and balance >= 1000;
/**
* 条件查询
* select id,username,info,balance where username like '%o%' and balance >= 1000;
*/
@Test
void testQueryWrapper() {
// 1.创建QueryWrapper对象
QueryWrapper wrapper = new QueryWrapper<>();
// 2.链式编程,构建查询条件
wrapper.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 2000);
// 3.查询数据
List userList = userMapper.selectList(wrapper);
// 4.方法引用遍历输出查询返回的List集合
userList.forEach(System.out::println);
}
更新:更新用户名为jack的用户的余额为2000
update table
set balance = 2000
where (username = 'jack');
/**
* 更新语句
* update table set balance = 2000 where username = 'jack';
*/
@Test
void testUpdateWrapper() {
// 1.创建UpdateWrapper对象
UpdateWrapper updateWrapper = new UpdateWrapper<>();
// 2.链式编程,构建更新条件
updateWrapper.eq("username","jack");
// 3.更新数据
User u = new User();
u.setBalance(528);
// 4.执行更新:userMapper.update(要更新的数据,更新的where条件)
userMapper.update(u,updateWrapper);
}
需求:更新id为1,2,4的用户的余额,扣200
update table
set balance = balance - 200
where id in (1, 2, 4);
基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。
SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能了:
/**
* 更新语句
* update table set balance = balance - 200 where id in(1,2,4);
*/
@Test
void testUpdateWrapper() {
// 1.创建UpdateWrapper对象
UpdateWrapper updateWrapper = new UpdateWrapper<>();
// 2.链式编程,构建更新条件
updateWrapper.setSql("balance = balance - 200") // set balance = balance - 200
.in("id",List.of(1L,2L,4L)); // where id in(1,2,4)
// 3.执行更新 userMapper.update(要更新的数据,更新的where条件)
// 注意:第一个参数可以传null,.也就是不填更新字段和数据,而是基于UpdateWrapper中的setSql来更新
userMapper.update(null,updateWrapper);
}
无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称 - 属于硬编码,会出现字符串魔法值,这在编程规范中显然是不推荐的。
LambdaQueryWrapper和QueryWrapper以及UpdateWrapper在功能上基本是一样的!
区别:LambdaQueryWrapper在构建条件时是基于Lambda语法!
其中一种办法是基于变量的getter方法结合反射技术,因此我们只要将条件对应的字段的getter方法传递给MyBatis-Plus,它就能计算出对应的变量名了,而传递方法可以使用JDK8中的方法引用和Lambda表达式。
分别对应QueryWrapper和UpdateWrapper
尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码!
LambdaQueryWrapper
/**
* 条件查询之LambdaQueryWrapper‘
* select id,username,info,balance where username like '%o%' and balance >= 1000;
*/
@Test
void testLambdaQueryWrapper() {
// 1.创建QueryWrapper对象
// new QueryWrapper<>().lambda() <=> new LambdaQueryWrapper<>()
LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 2.链式编程,构建查询条件,方法参数传对应字段的get函数
lambdaQueryWrapper
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
// 3.查询数据
List userList = userMapper.selectList(lambdaQueryWrapper);
// 4.方法引用遍历输出查询返回的List集合
userList.forEach(System.out::println);
}
LambdaUpdateWraper
/**
* 更新语句之LambdaUpdateWrapper
* update table set balance = balance - 200 where id in(1,2,4);
*/
@Test
void testLambdaUpdateWrapper() {
// 1.创建LambdaUpdateWrapper对象
LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
// 2.链式编程,构建更新条件,方法参数传对应字段的get方法
// 使用set方法来设置要更新的字段和新值
lambdaUpdateWrapper
.setSql("balance = balance - 200")
.in(User::getId, List.of(1L, 2L, 4L));
// 3.执行更新 userMapper(要更新的数据,更新的where条件)
// 注意:第一个参数可以传null,也就是不填更新字段和数据,而是基于UpdateWrapper中的setSql来更新
userMapper.update(null, lambdaUpdateWrapper);
}
在演示的UpdateWrapper的案例中,我们在代码中编写了更新的SQL语句:
这种写法在某些企业是不允许的,因为SQL语句最好都维护在持久层,而不是业务层。
就当前案例来说,由于条件时in语句,只能将SQL写在Mapper.xml文件,利用foreach来生成动态SQL,这实在是太麻烦了,假如查询条件更复杂,动态SQL的编写也会更加复杂!
所以MyBatis-Plus提供了自定义SQL功能,让我们可以利用MyBatis-Plus的Wrapper来构建生成复杂的where查询条件,然后再结合Mapper.xml编写自己定义SQL语句中剩下的部分,MP它更擅长的是where条件的编写。