Mybatis Plus应用和缓存介绍

介绍

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,主要方法:

  1. 设置查询字段 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开头的字段
  1. 设置查询条件方法
    继承自 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重要的四个对象

  1. Execute:调度执行StatementHandler、ParmmeterHandler、ResultHandler执行相应的SQL语句;
  2. StatementHandler:使用数据库中Statement(PrepareStatement)执行操作,即底层是封装好了的prepareStatement;
  3. ParammeterHandler:处理SQL参数;
  4. ResultHandler:结果集ResultSet封装处理返回。

2 二级缓存

MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。


image

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。

缓存注解

  1. @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;
}
  1. @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;
}
  1. @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);
}
  1. @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支持两种策略,默认策略和自定义策略。

  1. 默认策略
    默认的key生成策略是通过KeyGenerator生成的,其默认策略如下:
    如果方法没有参数,则使用0作为key。
    如果只有一个参数的话则使用该参数作为key。
    如果参数多余一个的话则使用所有参数的hashCode作为key。

如果我们需要指定自己的默认策略的话,那么我们可以实现自己的KeyGenerator,然后指定我们的Spring Cache使用的KeyGenerator为我们自己定义的KeyGenerator。

  1. 自定义策略
    自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”,如:"#name", "#p0"。
    除了使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。root包含内容如下图:


    image

使用代码:

@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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,398评论 0 4
  • 前言 本文主要讲解Mybatis的以下知识点: Mybatis缓存一级缓存二级缓存与Ehcache整合 Mappe...
    Java3y阅读 820评论 0 16
  • MyBatis是一个优秀的持久层ORM框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL 本身...
    楼兰King阅读 665评论 0 5
  • Mybatis介绍之缓存 Mybatis中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。一级...
    willcoder阅读 127评论 0 1
  • 先进篇(第八) 颜渊死,颜路请子之车以为之椁。子曰:“才不才,亦各言其子也。鲤也死,有棺而无椁。吾不徒行以为之椁。...
    姬乾玮阅读 144评论 0 0