介绍
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
特性
- 无侵入
只做增强不做改变,引入它不会对现有工程产生影响,完全可以像使用MyBatis一样使用 - 集成 CRUD 操作
框架中的通用 Mapper、通用 Service,可实现单表大部分 CRUD 操作,还有条件构造器,满足各类复杂的查询需求 - 支持 Lambda 形式调用
通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 - 内置分页插件
基于 MyBatis 物理分页,配置好后,无需手动设置分页,分页插件支持多种数据库 - 内置代码生成器
采用代码或者 Maven 插件可根据表,快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎 - 内置性能分析插件
可输出 Sql 语句以及其执行时间,可在开发测试时启用该功能,能快速揪出慢查询 - 内置全局拦截插件
提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
开始使用
1 引入
添加依赖,pom.xml
<!-- 引入 Spring Boot Starter 父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
</dependencies>
2 配置
在 application.yml 配置文件中添加 数据库的相关配置:
# DataSource Config
spring:
datasource:
driver-class-name: org.h2.Driver
schema: classpath:db/schema-h2.sql
data: classpath:db/data-h2.sql
url: jdbc:h2:mem:test
username: root
password: test
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:
@SpringBootApplication
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(QuickStartApplication.class, args);
}
}
功能介绍
1 Mapper CRUD接口:BaseMapper
EntityMapper继成BaseMapper<T>,即可获得CRUD功能的所有方法,泛型T为实体类类型。
2 Service CRUD接口:IService
业务层接口继承IService<T>,业务层实现类继承ServiceImpl<EntityMapper, T>,即可获得CRUD功能的所有方法,泛型T为实体类类型。
3 查询(条件构造器)
3.1 AbstractWrapper
抽象类,包含了设置查询条件的方法:
有allEq,eq,ne,gt,ge,lt,le,between,notBetween,like,
notLike,likeLeft,likeRight,isNull,isNotNull,in,notIn,inSql,notInSql,
groupBy,orderByAsc,orderByDesc,orderBy,having,or,and,
nested,apply,last,exists,notExists
3.2 QueryWrapper
QueryWrapper继承自 AbstractWrapper,主要方法:
- 设置查询字段 select
// select 方法
select(String... sqlSelect)
// 下面两个方法,过滤查询字段(主键除外)
select(Predicate<TableFieldInfo> predicate)
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
// 用法
select("id", "name", "age")
select(i -> i.getProperty().startsWith("test")) // 查询test开头的字段
- 设置查询条件方法
继承自 AbstractWrappe的设置查询条件的方法,如eq、like等
QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
// 查询name=张三
queryWrapper.eq("name", "张三");
String time = "2020-04-04 00:00:00";
// 当time不为空时,查询time>=2020-04-04 00:00:00
queryWrapper.ge(time != null, "time", time);
// 执行
List<UserDO> users = userMapper.selectList(queryWrapper);
3.3 LambdaQueryWrapper
LambdaQueryWrapper,拥有的方法和QueryWrapper类似,查询的字段使用函数传入,可以防止写错字段
// LambdaQueryWrapper对象两种方式创建
// 1:通过QueryWrapper获取
QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
LambdaQueryWrapper<UserDO> lambdaQueryWrapper = queryWrapper.lambda();
// 2:直接新建
lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询name=张三
lambdaQueryWrapper.eq(UserDO::getName, "张三");
// 执行
List<UserDO> users = userMapper.selectList(queryWrapper);
3.4 UpdateWrapper
继承了AbstractWrapper,拥有设置查询条件的方法,如eq、like等;
常用对象有UpdateWrapper和LambdaUpdateWrapper,用于更新,特有的方法有:
- set
设置更新的字段和值 - setSql
设置更新语句中set部分
UpdateWrapper updateWrapper = new UpdateWrapper();
// LambdaUpdateWrapper lambda = updateWrapper.lambda();
// 将name为张三的记录,age更新为25
updateWrapper.eq("name", ""张三);
updateWrapper.set("age", 25);
// 执行(使用了IService的方法)
iUserService.update(updateWrapper);
3.5 自定义SQL使用 Wrapper
我们自定义的Mapper的方法,在Mybatis Plus版本大于或等于3.0.7后,也可以使用Wrapper了
Mapper自定义方法:
// 自定义查询,使用注解
@Select("select * from user ${ew.customSqlSegment}")
List<UserDO> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);
// 或者自定义查询,使用Mapper.xml
<select id="getAll" resultType="UserDO">
SELECT * FROM user ${ew.customSqlSegment}
</select>
service中使用:
LambdaQueryWrapper<UserDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(UserDO::getName, "张三");
List<UserDO> userList = userMapper.getAll(lambdaQueryWrapper);
注:
有些时候我们需要查询第一条时,可以使用AbstractWrapper中的last方法,来添加limit语句
LambdaQueryWrapper<UserDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(UserDO::getName, "张三");
lambdaQueryWrapper.last("limit 1");
// 查询一条,添加了limit 1,防止查询到多条
UserDO user = userMapper.selectOne(lambdaQueryWrapper);
MyBatis缓存
1 一级缓存
Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存的作用域默认是一个SqlSession。
也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中; 第二次以后是直接去缓存中取。
当执行SQL查询中间发生了增删改的操作,MyBatis会把SqlSession的缓存清空。
一级缓存的范围有SESSION和STATEMENT两种,默认是SESSION,如果不想使用一级缓存,可以把一级缓存的范围指定为STATEMENT,这样每次执行完一个Mapper中的语句后都会将一级缓存清除。
如果需要更改一级缓存的范围,可以在Mybatis的配置中,在下通过localCacheScope指定。
注:每个线程有他自己的SqlSession实例,SqlSession重要的四个对象
- Execute:调度执行StatementHandler、ParmmeterHandler、ResultHandler执行相应的SQL语句;
- StatementHandler:使用数据库中Statement(PrepareStatement)执行操作,即底层是封装好了的prepareStatement;
- ParammeterHandler:处理SQL参数;
- ResultHandler:结果集ResultSet封装处理返回。
2 二级缓存
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。
SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开席需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的, 也就是要求实现Serializable接口。
配置方法,在映射XML文件配置
<cache></cache>
开启了二级缓存后:
- 缓存作用域为Mapper的namespace级别
- 映射语句文件中的所有select语句将会被缓存。
- 映射语句文件中的所有的insert、update和delete语句会刷新缓存
- 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回
- 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改
可对二级缓存进行具体的配置:
<?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.em.qx.dao.UserMapper">
<!--开启本mapper的namespace下的二级缓存-->
<!--
eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
(1) LRU,最近最少使用的,一处最长时间不用的对象
(2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
(3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
(4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,
移除最长时间不用的对形象
flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当
SQL被执行的时候才会去刷新缓存。
size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。默认是1024个对象
readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,他的默认值是false,不允许我们修改
-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
</mapper>
注:
- 当开启缓存后,数据的查询执行的流程就是二级缓存 -> 一级缓存 -> 数据库
- 二级缓存可自定义存储源,如 EHCache、RedisCache(分布式)
- 软引用SoftReference和弱引用WeakReference的概念
Spring缓存
从3.1开始,Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。Spring Cache是作用在方法上的,其核心思想:
当我们在调用一个缓存方法时会把该方法参数和返回结果,作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。所以在使用Spring Cache的时候要保证缓存的方法对于相同的方法参数,要有相同的返回结果。(需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的)
在启动类上加@EnableCaching注解,开启Spring Boot应用程序缓存功能。还可以使用Redis来实现CacheManager。
缓存注解
- @Cacheable
@Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。@Cacheable可以指定三个属性,value、key和condition。
value是Cache的名称(可以是多个),key是缓存的key,condition是缓存的条件
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
public User find(User user) {
System.out.println("find user by user " + user);
return user;
}
- @CachePut
@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。@CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。一般用在更新方法上。
@CachePut(value={"users"}, key="#user.id", condition="#user.id%2==0")
public User update(User user) {
System.out.println("update user " + user);
return user;
}
- @CacheEvict
@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。
@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。
allEntries,boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。
beforeInvocation,定义是否在对应方法成功执行之后触发,默认为true,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。当指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。
@CacheEvict(value="users", key="#user.id", condition="#user.id%2==0", beforeInvocation=true, allEntries=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
- @Caching
@Caching注解可以在一个方法或者类上同时指定多个Spring Cache相关的注解。其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。
@Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),@CacheEvict(value = "cache3", allEntries = true) })
public User find(Integer id) {
return null;
}
Key策略
Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring支持两种策略,默认策略和自定义策略。
- 默认策略
默认的key生成策略是通过KeyGenerator生成的,其默认策略如下:
如果方法没有参数,则使用0作为key。
如果只有一个参数的话则使用该参数作为key。
如果参数多余一个的话则使用所有参数的hashCode作为key。
如果我们需要指定自己的默认策略的话,那么我们可以实现自己的KeyGenerator,然后指定我们的Spring Cache使用的KeyGenerator为我们自己定义的KeyGenerator。
-
自定义策略
自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”,如:"#name", "#p0"。
除了使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。root包含内容如下图:
使用代码:
@Cacheable(value="users", key="#id")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#user.id")
public User find(User user) {
return null;
}
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
return null;
}
// 当使用root对象的属性作为key时,可以将“#root”省略,因为Spring默认使用的就是root对象的属性。
@Cacheable(value="users", key="#root.method.name")
public User find(User user) {
return null;
}