Mybatis缓存(1)--------系统缓存及简单配置介绍

时间:2022-05-19 19:08:19

前言

Mybatis的缓存主要有两种:

  • 系统缓存,也就是我们一级缓存与二级缓存;
  • 自定义的缓存,比如Redis、Enhance等,需要额外的单独配置与实现,具体日后主要学习介绍。

在这里主要记录系统缓存的一些简单概念, 并没有涉及原理。其中会涉及Mybatis的相关配置以及生命周期等。

主要参考资料:《深入浅出Mybatis基础原理与实战》,http://www.mybatis.org/mybatis-3/zh/index.html


1、Mybatis简单配置介绍

  本文介绍的是基于XML的配置,并不是关于注解的Mybatis配置。当然复杂SQL情况下都建议使用XML配置。

  (1)配置步骤

  这里记录的只是Myabtis的简单配置,并没有证整合Spring等框架,所以相对简单。我开始学的时候也是反复记不住,不知道为什么要这么配置,这么配置的作用是什么。之后经过研读《深入浅出Mybatis基础原理与实战》(我这里只有PDF电子版本,有需要的朋友可以评论或者私信我),总结并画图让我对整个配置过程有了全新的认识。

  简单来说,Mybatis的配置主要分为以下几步(整合Spring之后有些就不需要了,但是一开始学习不建议直接整合Spring):

  • 编写POJO即JavaBean,最终的目的是将数据库中的查询结果映射到JavaBean上;
  • 配置与POJO对应的Mapper接口:里面有各种方法,对应mapper.xml中的查询语句;
  • 配置与POJO对应的XML映射:编写缓存,SQL查询等;
  • 配置mybatis-config.xml主要的Mybatis配置文件:配置数据源、扫描mapper.xml等。

  注意:以上的配置并没有严格的前后顺序;

  (2)配置流程图

  Mybatis缓存(1)--------系统缓存及简单配置介绍

  (3)配置总结

  可以这么总结Mybatis或者帮助理解Mybatis的配置,我总结了以下三点提供参考:

  • 一切Mybatis配置都是为了创建SqlSession进行SQL查询;
  • 归根结底程序代码中我们屏蔽了各种配置映射,只显式调用使用Mapper接口,那么接口实现类的获得是通过SqlSession.getMapper()获得;
  • 那么mapper接口实现类的获得是通过mybatis-config.xml->SqlSessionFactoryBuilder->SqlSessionFacotry->SqlSession->mapper;

  

2、Mybatis生命周期 

  正确理解SqlSessionFactory、SqlSessionFactoryBuilder、SqlSession和Mapper的生命周期对于优化Mybatis尤为重要,这样可以使Mybatis高效正确完成;同为重要时Mybatis的生命周期对于理解Myabtis缓存的配置也尤为重要,我这里只做简单的文字介绍(其实也好理解):

  (1)SqlSessionFactoryBuilder:作用就是创建一个构建器,一旦创建了SqlSessionFactory,它的任务就算完成了,可以回收。

  (2)SqlSessionFactory:作用是创建SqlSession,而SqlSession相当于JDBC的一个Connection对象,每次应用程序需要访问数据库,我们就要通过SqlSessionFactory创建一个SqlSession,所以SqlSessionFactory在整Mybatis整个生命周期中(每个数据库对应一个SqlSessionFactory,是单例产生的)。

  (3)SqlSession:生命周期是存在于请求数据库处理事务的过程中,是一个线程不安全的对象(在多线程的情况下,需要特别注意),即存活于一个应用的请求和申请,可以执行多条SQL保证事务的一致性。

  (4)Mapper:是一个接口,并没有实现类它的作用是发送SQL,返回我们需要的结果,或者发送SQL修改数据库表,所以它存活于一个SqlSession内,是一个方法级别的东西。当SqlSession销毁的时候,Mapper也会销毁。

3、Myabtis缓存介绍

  (1)系统缓存:包括一级缓存与二级缓存

  一级缓存:默认情况下Myabtis对于同一个SqlSession开启一级缓存    

  • 在默认没有配置的情况下,只会开启一级缓存(只针对同一个SqlSession而言);
  • 在参数与SQL完全一样的情况下并且不声明刷新缓存没超时的,使用同一个SqlSession对象调用同一个Mapper方法时(SqlSession对象生命周期为方法级别),SqlSession只会取出当前缓存数据,不会再到数据库中进行查询;
  • 如果不同的SqlSession,即使同一个Mapper也会进行到数据库中进行不同的查询,即不同的SqlSession一级缓存是无效的。

  二级缓存:这里可以结合SqlSessionFactory等的生命周期能加深理解

  • 不同的SqlSession是隔离的,为了解决这个问题,我们可以在SqlSessionFactory层面上设置二级缓存提供各个对象SqlSession
  • 二级缓存默认是不开启的,需要进行配置,Mybatis要求返回的POJO必须是可序列化的,即POJO实现Serializable接口。

  缓存的配置只需要在XML配置<cache/>即可,或者指定算法,刷新时间间隔,缓存状态,大小等

 <cache eviction="LRU" readOnly="true" flushInterval="100000" size="1024"></cache>

    A. 映射语句文件中所有select语句将会被缓存;

    B. 映射语句文件中所有insert、update和delete语句会被刷新缓存;

    C. 缓存使用默认的LRU最近最少使用算法回收;

    D. 根据时间表,缓存不会任何时间顺序刷新;

    E. 缓存会存储列表集合或对象的1024个引用

    F. 缓存被视为可read/write的缓存,意味着是不可以被共享的,而可以被安全地修改。

  (2)自定义缓存:结合Redis等主流缓存配置

  我们可以使用比如现在比较火的Redis缓存,需要实现Myabtis为我们提供的接口org.apache.ibatis.cache.Cache。虽然现在主流Mybatis用的都是自定义缓存,但是这里先不过多介绍,我一步一步来学习记录!

4、Mybatis系统缓存代码实现

包结构图:

Mybatis缓存(1)--------系统缓存及简单配置介绍

数据库user表:要与User对应,Mybatis会根据驼峰命名进行自动映射,即user表中id字段映射为User POJO中的id,如果使用的是插件生成,POJO就会自动对应。

mysql> use mybatis;
Database changed
mysql> select*from user;
+----+----------+
| id | name |
+----+----------+
| 1 | Zhangsan |
| 2 | Lisi |
+----+----------+
2 rows in set

(1)User.java: POJO

import java.io.Serializable;

/**
* POJO:User
* @author Lijian
*
*/
public class User implements Serializable{ private int id;
private String name; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

(2)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.lijian.dao.UserMapper">
<resultMap id="userMap" type="com.lijian.model.User">
<id property="id" column="id"/>
<result column="name" property="name" />
</resultMap>
<!-- 使用POJO映射结果集 -->
<select id="findByUserId" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
<!-- 使用resultMap映射结果集 -->
<select id="findByUserName" parameterType="string" resultMap="userMap">
select * from user where name = #{name}
</select>
</mapper>

(3)UserMapper.java:

public interface UserMapper {
User findByUserId(int id);
User findByUserName(String name);
}

(4)mybatis-config.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- mybatis-config.xml常用的配置及顺序:
(properties?,
settings?,
typeAliases?,
typeHandlers?,
objectFactory?,
objectWrapperFactory?,
reflectorFactory?,
plugins?,
environments?,
databaseIdProvider?,
mappers?)
-->    <!-- jdbc数据库属性配置文件db.properties -->
<properties resource="db.properties"></properties>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<typeAlias alias="user" type="com.lijian.model.User"/>
</typeAliases>
<!-- default 默认数数据源 -->
<!-- environments配置:可以注册多个数据源DataSource,每个数据源分为两部分:一个是数据源的配置,另外一个是数据库事务配置。 -->
<environments default="development">
<!-- dataSource 1-->
<environment id="development">
<transactionManager type="JDBC">
<!-- 关闭自动提交 -->
<property name="autoCommit" value="false"/>
</transactionManager>
<!-- POOLED连接池数据库 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 可以通过包名导入映射器 :但mapper interface与mapper.xml必须在同一个包下-->
<!-- <package name="com.lijian.mapper"/> -->
<!-- 文件路径引入 -->
<mapper resource="com/lijian/mapper/UserMapper.xml"/>
</mappers>
</configuration>

(5)SqlSessionFactoryUtils:

/**
* 创建SqlSession
* @author Lijian
* 创建顺序:mybatis-config.xml->SqlSessionFactoryBuilder->SqlSessionFactory(Singleton)->SqlSession
*
*/
import java.io.IOException;
import java.io.InputStream; import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; public class SqlSessionFactoryUtils {
private static final Logger logger = LogManager.getLogger(SqlSessionFactoryUtils.class); //synchronized lock
private static final Class CLASS_LOCK = SqlSessionFactoryUtils.class;
private static SqlSessionFactory sqlSessionFactory = null; //private constructors
private SqlSessionFactoryUtils(){};
/**
* 因为一个数据库对应一个SqlSessionFactory,所有采用单例模式生成SqlSessionFactory
* @return SqlSessionFactory
*/
public static SqlSessionFactory initSqlSessionFactory() {
String config = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(config);
}catch (IOException e) {
logger.info("SqlSessionFactoryUtils");
}
synchronized (CLASS_LOCK) {
if (sqlSessionFactory == null) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} }
return sqlSessionFactory;
}
/**
* openSession进行SQL查询
* @return SqlSession
*/
public static SqlSession openSession() {
if (sqlSessionFactory == null) {
initSqlSessionFactory();
}
return sqlSessionFactory.openSession();
} }

(6)MybatisMain.java:测试类

import org.apache.ibatis.session.SqlSession;

import com.lijian.dao.UserMapper;
import com.lijian.utils.SqlSessionFactoryUtils; public class MybatisMain {
public static void main(String[] args) {
SqlSession sqlSession = null;
SqlSession sqlSession2 = null;
try {
//获得SqlSession
sqlSession = SqlSessionFactoryUtils.openSession();
sqlSession2 = SqlSessionFactoryUtils.openSession();
//获得Mapper:动态代理生成UserMapper实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//默认一级缓存:相同SELECT与param,只查询一次
System.out.println("=======================默认使用系统一级缓存=======================");
userMapper.findByUserId(1);
userMapper.findByUserId(1);
//二级缓存commit才会有效
sqlSession.commit();
System.out.println("=======================重新创建SqlSession=======================");
sqlSession2 = SqlSessionFactoryUtils.openSession();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
userMapper2.findByUserId(1);
//二级缓存commit才会有效
sqlSession2.commit();
} catch (Exception e) {
System.err.println(e.getMessage());
}
finally {
if (sqlSession != null) {
//sqlSession生命周期是随着SQL查询而结束的
sqlSession.close();
}
if (sqlSession2 != null) {
sqlSession2.close();
}
}
}
}

注意UserMapper.xml当前是没有开启二级缓存的,故默认为一级缓存,如何得到证实呢?在MybatisMain中,我们创建了:

  • sqlSession:开启两个一模一样的SELECT SQL查询,即userMapper.findByUserId(1),那么如果一级缓存有效且开启的话,只会进行一次查询,之后有一次SQL语句日志输出;
  • sqlSession2:开启与sqlSession中一模一样的SELECT查询,如果二级缓存没有开启,一级缓存默认开启的话,是会进行查询的,会有一次SQL语句日志输出。
=======================默认使用系统一级缓存=======================
[DEBUG][main][2018-07-29 21:45:41][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Preparing: select * from user where id = ?
[DEBUG][main][2018-07-29 21:45:41][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Parameters: 1(Integer)
[TRACE][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Columns: id, name
[TRACE][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Row: 1, Zhangsan
[DEBUG][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Total: 1
=======================重新创建SqlSession=======================
[DEBUG][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Preparing: select * from user where id = ?
[DEBUG][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Parameters: 1(Integer)
[TRACE][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Columns: id, name
[TRACE][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Row: 1, Zhangsan
[DEBUG][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Total: 1

接下来我们开启二级缓存:在不同SqlSession中所有相同的SELECT语句将会被缓存,只会有一次SQL语句日志输出,并且会有Cache Hit Ratio缓存命中率

<?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.lijian.dao.UserMapper">
<!-- 开启二级缓存 :针对SqlSessionFactory,同时POJO必须实现Serializable接口
(1)所有select会被缓存
(2)insert、update、delete会刷新缓存
(3)默认使用LRU
(4)缓存是可read/write,不是共享的是可以被安全地修改
-->
<cache eviction="LRU" readOnly="true" flushInterval="100000" size="1024"></cache>
<resultMap id="userMap" type="com.lijian.model.User">
<id property="id" column="id"/>
<result column="name" property="name" />
</resultMap>
<!-- 使用POJO映射结果集 -->
<select id="findByUserId" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
<!-- 使用resultMap映射结果集 -->
<select id="findByUserName" parameterType="string" resultMap="userMap">
select * from user where name = #{name}
</select>
</mapper>

日志如下:

=======================默认使用系统一级缓存=======================
[DEBUG][main][2018-07-29 21:49:26][org.apache.ibatis.cache.decorators.LoggingCache] - Cache Hit Ratio [com.lijian.dao.UserMapper]: 0.0
[DEBUG][main][2018-07-29 21:49:27][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Preparing: select * from user where id = ?
[DEBUG][main][2018-07-29 21:49:27][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Parameters: 1(Integer)
[TRACE][main][2018-07-29 21:49:27][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Columns: id, name
[TRACE][main][2018-07-29 21:49:27][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Row: 1, Zhangsan
[DEBUG][main][2018-07-29 21:49:27][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Total: 1
[DEBUG][main][2018-07-29 21:49:27][org.apache.ibatis.cache.decorators.LoggingCache] - Cache Hit Ratio [com.lijian.dao.UserMapper]: 0.0
=======================重新创建SqlSession=======================
[DEBUG][main][2018-07-29 21:49:27][org.apache.ibatis.cache.decorators.LoggingCache] - Cache Hit Ratio [com.lijian.dao.UserMapper]: 0.3333333333333333

总结与补充

  (1)作为新手很大可能在配置过程中会遇到很多坑(我就是其中之一),比如Mybatis的日志配置。这里可以主要参考:http://www.mybatis.org/mybatis-3/zh/configuration.html

  (2)mybatis的相关配置文件(没有myabtis-config.xml)可以使用eclipse中的插件生成(具体网上有很多教程),但还要进行适当修改!

Mybatis缓存(1)--------系统缓存及简单配置介绍的更多相关文章

  1. Mybatis缓存1----系统缓存及简单配置介绍

      mybatis缓存 系统缓存:常用的一级缓存和二级缓存 一级缓存 一级缓存是SqlSession级别的缓存,在操作数据库时需要构建SqlSession对象,在对象中有一个数据结构用于存储缓存数据. ...

  2. CCNA网络工程师学习进程(6)vlan相关协议的配置与路由器简单配置介绍

        前面已经介绍了大部分与vlan技术相关的交换机的协议的配置,更深层次的还有STP协议和以太网端口聚合技术,接着还会简单介绍一下路由器的基本应用.     (1)STP(Spanning-tre ...

  3. windows系统常用软件及配置介绍

    常用工具 ,,,, 开发工具 ,,, 快捷键 ... 等等 vvv 等等

  4. centos7防火墙的简单配置介绍

    centos7版本 1.查看已开放的端口(默认不开放任何端口) firewall-cmd --list-ports 2.开启80端口 firewall-cmd --zone=public(作用域) - ...

  5. C&num; 嵌入dll 动软代码生成器基础使用 系统缓存全解析 &period;NET开发中的事务处理大比拼 C&num;之数据类型学习 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持 基于EF Core的Code First模式的DotNetCore快速开发框架 【懒人有道】在asp&period;net core中实现程序集注入

    C# 嵌入dll   在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形 ...

  6. host缓存,浏览器缓存---解决host缓存带来的伤

    1.缓存 缓存,对应工程师来讲简直太熟悉了,太方便了,省略到资源或数据的获取方式,直接缓存到离用户访问最快的地方,也降低服务器的压力,比如: (1)静态文件获取 服务器->cdn->本地磁 ...

  7. &lbrack;原创&rsqb;关于mybatis中一级缓存和二级缓存的简单介绍

    关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...

  8. 关于mybatis中一级缓存和二级缓存的简单介绍

    关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...

  9. mybatis&lpar;4&rpar;&lowbar;二级缓存深入&lowbar;使用第三方ehcache配置二级缓存

    增删改对二级缓存的影响 1.增删改也会清空二级缓存 2.对于二级缓存的清空实质上是对value清空为null,key依然存在,并非将Entry<k,v>删除 3.从DB中进行select查 ...

随机推荐

  1. nginx location详解&lpar;三&rpar;

    location官方文档:http://nginx.org/en/docs/http/ngx_http_core_module.html#location Syntax: location [ = | ...

  2. java中的堆、栈、常量池

    java中的堆.栈.常量池 分类: java2010-01-15 03:03 4248人阅读 评论(5) 收藏 举报 javastring编译器jvm存储equals Java内存分配: 1. 寄存器 ...

  3. 表空间移动(transporting tablespaces)

    --表空间移动(transporting tablespaces) --------------------------------------2014/01/15   1. 表空间传输步骤简介.   ...

  4. springboot添加swagger2组件

    swagger2是一个可以构建和调试RESTful API文档的组件,利用swagger2的注解可以快速的在项目中构建Api文档,并且提供了测试API的功能 1,引入依赖 <dependency ...

  5. 四五月份:关键词是沟通、绘画和SQL

    例行总结一下四五月份的感受. 关键词有三个:沟通.绘画和SQL. 整体来说,这两个月在努力跟这三个关键词死磕,略有些进展,因此汇报一下. 虽然这三个关键词从重要度来说是从左到右的,但从叙述来讲,还是先 ...

  6. Luogu P3953 逛公园

    不管怎么说,这都是一道十分神仙的NOIp题 你可以说它狗,但不可以否认它就是NOIp的难度 首先这道题很显然是道图论题还是一道图论三合一(最短路+拓扑+图上DP) 先考虑最短路,我们分别以\(1\)和 ...

  7. git中如何切换分支,拉取分支,合并分支

    idea中如何使用git来做分支的切换合并: https://blog.csdn.net/autfish/article/details/52513465 本地分支与远程分支: https://seg ...

  8. Markdown的学习笔记一

    之前学习看些书籍.学些技术都喜欢用xmind做思维导图的笔记,慢慢的发现想把一些笔记做的详细一些就会变得很复杂,个人觉得误了思维导图本意,而且用手机查看的时候也各种不方便.所以开始学习使用markdo ...

  9. FormsAuthentication&period;SetAuthCookie

    这两天在研究 Forms 进行用户验证, 它本身没有什么上msdn上查一下就知道怎么个搞法了! 不过我在测试的时候发现也会产生 了一些疑问! 1. 什么我在web.config 的 authentic ...

  10. python已写内容中可能的报错及解决办法

    理论上我发的每个短文,直接复制放到py里面,python xx.py是可以执行的,不过因为版本,编码什么的问题会有报错,详见这里 报错: SyntaxError: Non-ASCII characte ...