mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和mybatis整合ehcache
- 格式:doc
- 大小:13.92 KB
- 文档页数:5
MyBatis(六)缓存机制之⼀级缓存⼀、⼀级缓存介绍 1、⼀级缓存(local cache),即本地缓存,作⽤域默认为SqlSession。
当 Session flush 或 close 后,该 session 中的所有 Cache 将被清空。
2、本地缓存不能被关闭,但可以调⽤ clearCache() 来清空本地缓存,或者改变缓存的作⽤域。
3、在 MyBatis3.1 之后,可以配置本地缓存的作⽤域,在 MyBatis.xml 中配置。
4、⼀级缓存是 sqlsession 级别的缓存。
⼀级缓存是⼀直开启的,sqlsession级别的⼀个 map,与数据库同⼀次会话期间查询到的数据会放在本地缓存中。
多个⼀级缓存中的数据不能共⽤。
以后如果需要获取相同的数据,直接从缓存中获取,不必再去查询数据库。
5、⼀级缓存的⼯作机制 同⼀次会话期间只要查询过的数据都会保存在当前的 SqlSession 的⼀个 Map 中 key:hashCode+查询的 SqlId + 编写的 sql 查询语句 + 参数⼆、⼀级缓存失效的四种情况 ⼀级缓存失效情况(没有使⽤到当前⼀级缓存的情况,效果就是,还需要再向数据发送SQL) 1、sqlSession 不同:使⽤不同的 sqlSession 数据库会话,不同的 SqlSession 对应不同的⼀级缓存; 2、sqlSession 相同:但查询条件不同(当前⼀级缓存中还没有这个数据) 3、如果sqlSession相同:两次查询之间执⾏了增删改操作(这次增删改可能对当前数据有影响) 4、如果sqlSession相同,⼿动清除了⼀级缓存(把缓存内容清空) SqlSession 级别的缓存就相当于⼀个 Map。
三、⼀级缓存演⽰ 1、在同⼀个 SqlSession 中查询同⼀条记录 代码:/*** mybatis中的⼀级缓存默认开启,是SqlSession级别的* 即同⼀个SqlSession对于⼀个sql语句,执⾏之后就会存储在缓存中,* 下次执⾏相同的sql,直接从缓存中取*/@Testpublic void testFirstLevelCache() throws IOException {SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();SqlSession sqlSession = sqlSessionFactory.openSession();try {EmployeeMapperCache mapper = sqlSession.getMapper(EmployeeMapperCache.class);Employee emp01 = mapper.getEmpById(1);System.out.println("emp01 = " + emp01);//处理业务Employee emp02 = mapper.getEmpById(1);System.out.println("emp02 = " + emp02);System.out.println(emp01 == emp02);} finally {sqlSession.close();}} 运⾏结果: 可以看出,查询的是同⼀条记录,但第⼀次是从数据库中查询的,有 SQL 语句,第⼆次查询是就是从缓存(SqlSession)中获取的。
缓存(⼀级缓存和⼆级缓存)缓存可以将数据保存在内存中,是互联⽹系统常常⽤到的。
⽬前流⾏的缓存服务器有 MongoDB、Redis、Ehcache 等。
缓存是在计算机内存上保存的数据,读取时⽆需再从磁盘读⼊,因此具备快速读取和使⽤的特点。
和⼤多数持久化框架⼀样,MyBatis 提供了⼀级缓存和⼆级缓存的⽀持。
默认情况下,MyBatis 只开启⼀级缓存。
⼀级缓存⼀级缓存是基于 PerpetualCache(MyBatis⾃带)的 HashMap 本地缓存,作⽤范围为 session 域内。
当 session flush(刷新)或者close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。
在参数和 SQL 完全⼀样的情况下,我们使⽤同⼀个 SqlSession 对象调⽤同⼀个 mapper 的⽅法,往往只执⾏⼀次 SQL。
因为使⽤SqlSession 第⼀次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,⽽不会再次发送 SQL 到数据库。
由于 SqlSession 是相互隔离的,所以如果你使⽤不同的 SqlSession 对象,即使调⽤相同的 Mapper、参数和⽅法,MyBatis 还是会再次发送 SQL 到数据库执⾏,返回结果。
⽰例:WebsiteMapperpublic Website selectWebsiteById(int id);WebsiteMapper.xml<select id="selectWebsiteById"resultType="net.biancheng.po.Website">SELECT * FROM websiteWHERE id=#{id}</select>测试代码public class Test {public static Logger logger = Logger.getLogger(Test.class);public static void main(String[] args) throws IOException {InputStream config = Resources.getResourceAsStream("mybatis-config.xml"); // 根据配置⽂件构建SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);SqlSession ss = ssf.openSession();Website site = ss.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);logger.debug("使⽤同⼀个sqlsession再执⾏⼀次");Website site2 = ss.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);// 请注意,当我们使⽤⼆级缓存的时候,sqlSession调⽤了 commit⽅法后才会⽣效mit();logger.debug("现在创建⼀个新的SqlSeesion对象在执⾏⼀次");SqlSession ss2 = ssf.openSession();Website site3 = ss2.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);// 请注意,当我们使⽤⼆级缓存的时候,sqlSession调⽤了 commit⽅法后才会⽣效mit();}}运⾏结果DEBUG [main] - ==> Preparing: SELECT * FROM website WHERE id=?DEBUG [main] - ==> Parameters: 1(Integer)DEBUG [main] - <== Total: 1DEBUG [main] - 使⽤同⼀个sqlsession再执⾏⼀次DEBUG [main] - 现在创建⼀个新的SqlSeesion对象在执⾏⼀次DEBUG [main] - ==> Preparing: SELECT * FROM website WHERE id=?DEBUG [main] - ==> Parameters: 1(Integer)DEBUG [main] - <== Total: 1从运⾏结果可以看出,第⼀个 SqlSession 实际只发⽣过⼀次查询,⽽第⼆次查询就从缓存中取出了,也就是 SqlSession 层⾯的⼀级缓存。
mybatis一级二级缓存原理
MyBatis 提供了两级缓存机制,即一级缓存和二级缓存。
这两级缓存的原理如下:
1. 一级缓存(SqlSession 级别的缓存):
一级缓存是在同一个 SqlSession 中,对于相同的查询条件,只会执行一次SQL 查询,查询结果被缓存起来,后续的相同查询可以直接从缓存中获取结果,避免了重复的数据库查询操作。
一级缓存是基于 SqlSession 对象的,也就是说,只要 SqlSession 不关闭,一级缓存就会一直存在。
2. 二级缓存(Mapper 级别的缓存):
二级缓存是跨 SqlSession 的,也就是说,即使在不同的 SqlSession 中,只要查询条件相同,就可以共享同一个查询结果。
二级缓存是基于 Mapper 接口的,可以在多个 Mapper 之间共享数据。
二级缓存可以配置为只对某些特定的 Mapper 或者全局启用。
MyBatis 的两级缓存机制可以有效地减少对数据库的访问次数,提高应用程序的性能。
但是需要注意的是,由于二级缓存是跨 SqlSession 的,因此在使用二级缓存时需要特别注意线程安全问题,以及在 SqlSession 关闭或者清空缓存时要正确地处理缓存数据。
缓存概述∙正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持;∙一级缓存基于PerpetualCache的 HashMap 本地缓存,其存储作用域为Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
∙二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache、Hazelcast等。
∙对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
∙MyBatis 的缓存采用了delegate机制及装饰器模式设计,当put、get、remove时,其中会经过多层 delegate cache 处理,其Cache类别有:BaseCache(基础缓存)、EvictionCache(排除算法缓存) 、DecoratorCache(装饰器缓存):BaseCache :为缓存数据最终存储的处理类,默认为 PerpetualCache,基于Map存储;可自定义存储处理,如基于EhCache、Memcached等;EvictionCache :当缓存数量达到一定大小后,将通过算法对缓存数据进行清除。
默认采用 Lru 算法(LruCache),提供有 fifo 算法(FifoCache)等;DecoratorCache:缓存put/get处理前后的装饰器,如使用 LoggingCache 输出缓存命中日志信息、使用 SerializedCache 对 Cache的数据 put或get 进行序列化及反序列化处理、当设置flushInterval(默认1/h)后,则使用 ScheduledCache 对缓存数据进行定时刷新等。
带你深⼊理解MyBatis缓存机制⽬录⼀、简介1、缓存机制介绍2. ⼀级缓存和⼆级缓存⼆、⼀级缓存三、⼆级缓存3.1 mybatis⾃带的⼆级缓存3.1.1 代码测试⼆级缓存3.1.2 查询结果存⼊⼆级缓存的时机3.1.3 ⼆级缓存相关配置四、整合EHCache4.1 EHCache简介4.2 整合操作五、缓存基本原理5.1 Cache接⼝5.2 PerpetualCache总结⼀、简介1、缓存机制介绍当客户端发起⼀次查询请求时,⾸先通过java程序进⾏⽹络传输访问mysql数据库及对应的数据的服务器硬盘,当第⼆次的请求也是查询相同的数据时再通过这个流程显然有点“浪费”上次请求访问到的资源,所以我们将第⼀次查询到的数据存到缓存区域,当发⽣下⼀次相同请求时直接在缓存区域拿就⾏了。
2. ⼀级缓存和⼆级缓存①使⽤顺序查询的顺序是:先查询⼆级缓存,因为⼆级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使⽤。
如果⼆级缓存没有命中,再查询⼀级缓存如果⼀级缓存也没有命中,则查询数据库SqlSession关闭之前,⼀级缓存中的数据会写⼊⼆级缓存②效⽤范围⼀级缓存:SqlSession级别⼆级缓存:SqlSessionFactory级别它们之间范围的⼤⼩参考下⾯图:⼆、⼀级缓存当使⽤相同查询条件查询数据时,⼀共只打印了⼀条SQL语句,两个变量指向同⼀个对象。
⼀级缓存失效的情况:不是同⼀个SqlSession同⼀个SqlSession但是查询条件发⽣了变化同⼀个SqlSession两次查询期间执⾏了任何⼀次增删改操作同⼀个SqlSession两次查询期间⼿动清空了缓存同⼀个SqlSession两次查询期间提交了事务三、⼆级缓存3.1 mybatis⾃带的⼆级缓存3.1.1 代码测试⼆级缓存①开启⼆级缓存功能在想要使⽤⼆级缓存的Mapper配置⽂件中加⼊cache标签<mapper namespace="com.zengchuiyu.mybatis.dao.EmployeeMapper"><!-- 启动⼆级缓存功能 --><cache/>②让实体类⽀持序列化public class Employee implements Serializable {③junit测试这个功能的测试操作需要将SqlSessionFactory对象设置为成员变量public class CacheTest {private SqlSessionFactory factory;@Beforepublic void init() throws IOException {factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));}//测试⼆级缓存,(mybatis⾃带的)@Testpublic void test1(){SqlSession session = factory.openSession();EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);Employee employee = mapper.selectEmpById(2);System.out.println("employee = " + employee);//在执⾏第⼆次查询前,关闭当前SqlSessionsession.close();//开启新的SqlSessionsession = factory.openSession();mapper = session.getMapper(EmployeeMapper.class);employee = mapper.selectEmpById(2);System.out.println("employee = " + employee);session.close();}}打印效果:22:48:18.669 [main] DEBUG com.zengchuiyu.mybatis.dao.EmployeeMapper - Cache Hit Ratio [com.zengchuiyu.mybatis.dao.EmployeeMapper]: 0.5④缓存命中率⽇志中打印的Cache Hit Ratio叫做缓存命中率Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.0(0/1)Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.5(1/2)Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.6666666666666666(2/3)Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.75(3/4)Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.8(4/5)缓存命中率=命中缓存的次数/查询的总次数3.1.2 查询结果存⼊⼆级缓存的时机结论:SqlSession关闭的时候,⼀级缓存中的内容会被存⼊⼆级缓存3.1.3 ⼆级缓存相关配置eviction属性:缓存回收策略LRU(Least Recently Used) – 最近最少使⽤的:移除最长时间不被使⽤的对象。
Memcache缓存1.MyBatis缓存MyBatis缓存分为一级缓存与二级缓存一级缓存:基于PerpetualCache 的HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有Cache 就将清空。
二级缓存:与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace)缓存更新机制:当某一作用域(一级缓存Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有select 中的缓存将被清空避免使用二级缓存:/isea533/article/details/445662572.Memcachea)简介Memcache 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。
它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。
Memcache基于一个存储键/值对的hashmapb)安装安装命令-d 选项是启动一个守护进程-m 是分配给Memcache使用的内存数量,单位是MB,默认是64MB-u 是运行Memcache的用户-L 是监听的服务器IP地址,默认应该是本机-I page大小,默认1M-p 是设置Memcache监听的端口,默认是11211,最好是1024以上的端口-c 选项是最大运行的并发连接数,默认是1024,按照你服务器的负载量来设定-P 是设置保存Memcache的pid文件位置-h 打印帮助信息-f 指定chunk增长因子大小,该值越小所能提供的chunk间隔越小,可以减少内存的浪费-n chunk最小尺寸-v 输出警告和错误信息-vv 打印客户端的请求和返回信息PS:Memcached单进程在32位系统中最大使用内存为2G,若在64位系统则没有限制,这是由于32位系统限制单进程最多可使用2G内存,要使用更多内存,可以分多个端口开启多个Memcached进程最大30天的数据过期时间,设置为永久的也会在这个时间过期Key: 最大键长为250字节,大于长度无法存储,常量KEY_MAX_LENGTH 250控制Value: 单个item最大数据是1MB,超过1MB数据不予存储,常量POWER_BLOCK 1048576进行控制运行命令1. 使用telnet连接Memcache服务器端口2. 连接上端口输入stats命令,获取描述Memcache服务器运行情况的参数操作命令 /zzulp/article/details/7823511 命令参数以及用法: 1. s et:命令用于向缓存添加新的键值对。
mybatis中的一二级缓存的实现原理Mybatis是一个优秀的ORM(ObjectRelationalMapping)框架,它提供了一种简单的方式来访问数据库。
在Mybatis中,缓存是一个非常重要的概念,它可以提高应用程序的性能。
Mybatis中的缓存分为一级缓存和二级缓存,下面将详细介绍它们的实现原理。
一级缓存Mybatis中的一级缓存是指在同一个SqlSession中执行相同的SQL语句时,返回的结果会被缓存起来,下一次执行相同的SQL语句时,可以直接从缓存中获取结果,而不需要再次查询数据库。
一级缓存的实现是基于内存的,缓存的生命周期与SqlSession的生命周期一致。
一级缓存是Mybatis默认开启的,可以通过SqlSession的clearCache()方法来清空一级缓存。
二级缓存Mybatis中的二级缓存是指在同一个应用程序中多个SqlSession之间共享缓存数据,它的作用是提高应用程序的性能。
二级缓存的实现是基于缓存机制的,缓存的生命周期与应用程序的生命周期一致。
二级缓存是通过使用单独的缓存空间来实现的,可以使用第三方缓存框架如Ehcache、Redis等来实现。
Mybatis中的二级缓存的使用需要注意以下几点:1. Mapper文件中需要配置开启二级缓存<cacheeviction='LRU'flushInterval='100000'size='1024'readOnly='true'/>2. 对于需要缓存的对象,需要实现Serializable接口3. 在不同的SqlSession中使用同一个Mapper,才能共享缓存数据4. 对于更新、插入、删除操作,会清空相关的缓存总结:Mybatis中的缓存是提高应用程序性能的重要手段,一级缓存和二级缓存的实现原理不同,使用时需要注意它们的区别和限制条件。
Mybatis⼀级缓存与⼆级缓存的实现mybatis缓存mybatis作为⼀个流⾏的持久化⼯具,缓存必然是缺少不了的组件。
通过这篇⽂章,就让我们来了解⼀下mybatis的缓存。
mybatis缓存类型说起mybatis的缓存,了解过的同学都知道,mybatis中可以有两种缓存类型:第⼀种,我们通常称为以及缓存,或者sqlSession级别的缓存,这种缓存是mybatis⾃带的,如果mapper中的配置都是默认的话,那么⼀级缓存也是默认开启的。
第⼆种,就是⾮sqlSession级别的缓存了,我们通常称为⼆级缓存,mybatis中的⼆级缓存需要实现Cache接⼝,并且配置在mapper中,要先开启的话,需要⼀些配置,下⾯我们会详细说到。
⼀级缓存作为mybatis⾃带的缓存,我们通过代码来分析⼀下其原理。
⾸先,我们来看下⼀级缓存的效果。
测试代码:@Testpublic void test_Cache() throws Exception {InputStream input = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(input);SqlSession sqlSession = factory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);System.out.println("the first query : ");mapper.queryAllUsers();System.out.println("====================================");System.out.println("the second query : ");mapper.queryAllUsers();mit();}mapper配置如下,我们采⽤默认配置:<select id="queryAllUsers" resultType="User">select * from hwc_users</select>运⾏结果如下:Created connection 1191654595.Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@470734c3]==> Preparing: select * from hwc_users==> Parameters:<== Columns: id, name, age, email<== Row: 1, 胡⽂超, 35, huwenchao@<== Row: 2, 胡⽂超, 35, huwenchao@<== Row: 3, 胡⽂超, 35, huwenchao@<== Row: 4, 胡⽂超, 35, huwenchao@<== Row: 5, 胡⽂超, 35, huwenchao@<== Row: 6, 胡⽂超, 35, huwenchao@<== Row: 7, 胡⽂超, 35, huwenchao@<== Row: 8, 胡⽂超, 35, huwenchao@<== Row: 9, 胡⽂超, 35, huwenchao@<== Total: 9====================================the second query :Cache Hit Ratio [erMapper]: 0.0Process finished with exit code 0从上述结果可以看到,第⼆次查询并没有从数据库获取,并且没有从⼆级缓存中获取,由此可见,默认配置情况下,同⼀个sqlSession中会默认使⽤mybatis的⼀级缓存。
mybatis的缓存机制及⽤例介绍在实际的项⽬开发中,通常对数据库的查询性能要求很⾼,⽽mybatis提供了查询缓存来缓存数据,从⽽达到提⾼查询性能的要求。
mybatis的查询缓存分为⼀级缓存和⼆级缓存,⼀级缓存是SqlSession级别的缓存,⼆级缓存时mapper级别的缓存,⼆级缓存是多个SqlSession共享的。
mybatis通过缓存机制减轻数据压⼒,提⾼数据库性能。
⼀级缓存:mybatis的⼀级缓存是SQLSession级别的缓存,在操作数据库时需要构造SqlSession对象,在对象中有⼀个HashMap⽤于存储缓存数据,不同的SqlSession之间缓存数据区域(HashMap)是互相不影响的。
⼀级缓存的作⽤域是SqlSession范围的,当在同⼀个SqlSession中执⾏两次相同的sql语句时,第⼀次执⾏完毕会将数据库中查询的数据写到缓存(内存)中,第⼆次查询时会从缓存中获取数据,不再去底层进⾏数据库查询,从⽽提⾼了查询效率。
需要注意的是:如果SqlSession执⾏了DML操作(insert、update、delete),并执⾏commit()操作,mybatis则会清空SqlSession中的⼀级缓存,这样做的⽬的是为了保证缓存数据中存储的是最新的信息,避免出现脏读现象。
当⼀个SqlSession结束后该SqlSession中的⼀级缓存也就不存在了,Mybatis默认开启⼀级缓存,不需要进⾏任何配置。
注意:Mybatis的缓存机制是基于id进⾏缓存,也就是说Mybatis在使⽤HashMap缓存数据时,是使⽤对象的id作为key,⽽对象作为value保存例⼦说明:⼯程架构图:pom.xml<project xmlns="/POM/4.0.0" xmlns:xsi="/2001/XMLSchema-instance" xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion><groupId>com.gm.test</groupId><artifactId>MybatisCacheTest</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!-- spring版本号 --><spring.version>4.3.10.RELEASE</spring.version><!-- mybatis版本号 --><mybatis.version>3.2.6</mybatis.version><!-- log4j⽇志⽂件管理包版本 --><slf4j.version>1.7.7</slf4j.version><log4j.version>1.2.17</log4j.version></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><!-- 表⽰开发的时候引⼊,发布的时候不会加载此包 --><scope>test</scope></dependency><!-- spring核⼼包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-oxm</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version></dependency><!-- aop --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.4</version></dependency><!-- mybatis核⼼包 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>${mybatis.version}</version></dependency><!-- mybatis/spring包 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.2.2</version></dependency><!-- 导⼊java ee jar 包 --><dependency><groupId>javax</groupId><artifactId>javaee-api</artifactId><version>7.0</version></dependency><!-- 导⼊Mysql数据库链接jar包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.30</version></dependency><!-- 导⼊dbcp的jar包,⽤来在applicationContext.xml中配置数据库 --> <dependency><groupId>commons-dbcp</groupId><artifactId>commons-dbcp</artifactId><version>1.2.2</version></dependency><!-- JSTL标签类 --><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><!-- ⽇志⽂件管理包 --><!-- log start --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><!-- 格式化对象,⽅便输出⽇志 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.1.41</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>${slf4j.version}</version></dependency><!-- log end --></dependencies><build><plugins><plugin><artifactId>maven-war-plugin</artifactId><configuration><version>3.1</version></configuration></plugin></plugins></build></project>sql语句:CREATE TABLE employee(id INT(11) PRIMARY KEY AUTO_INCREMENT,loginname VARCHAR(18),PASSWORD VARCHAR(18),NAME VARCHAR(18) DEFAULT NULL,sex CHAR(2) DEFAULT NULL,age INT(11) DEFAULT NULL,phone VARCHAR(21),sal DOUBLE,state VARCHAR(18));INSERT INTO employee (loginname,PASSWORD,NAME,sex,age,phone,sal,state) VALUES('jack','123456','杰克','男',26,'12345678936',9800,'ACTIVE'); INSERT INTO employee (loginname,PASSWORD,NAME,sex,age,phone,sal,state) VALUES('rose','123456','露丝','⼥',21,'78965412395',6800,'ACTIVE'); INSERT INTO employee (loginname,PASSWORD,NAME,sex,age,phone,sal,state) VALUES('tom','123456','汤姆','男',25,'139********',8800,'ACTIVE'); INSERT INTO employee (loginname,PASSWORD,NAME,sex,age,phone,sal,state) VALUES('alice','123456','爱丽丝','⼥',20,'74185296375',5800,'ACTIVE'); log4j.properties:log4j.rootLogger=ERROR, stdout.gm.mapper.EmployeeMapper=DEBUGlog4j.appender.console=org.apache.log4j.ConsoleAppenderyout=org.apache.log4j.PatternLayoutyout.ConversionPattern=[%-12d{HH\:mm\:ss.SS}] [%p] %l %m%nlog4j.appender.stdout=org.apache.log4j.ConsoleAppenderyout=org.apache.log4j.PatternLayoutyout.ConversionPattern=%d %p [%c] - %m%nmybatis-config.xml:<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configuration PUBLIC "-////DTD Config 3.0//EN" "/dtd/mybatis-3-config.dtd"><configuration><!-- 全局参数 --><settings><!-- 设置但JDBC类型为空时,某些驱动程序要指定值,default:OTHER,插⼊空值时不需要指定类型 --><setting name="jdbcTypeForNull" value="NULL" /><!-- 要使延迟加载⽣效必须配置下⾯两个属性 --><setting name="lazyLoadingEnabled" value="true" /><setting name="aggressiveLazyLoading" value="false" /><setting name="logImpl" value="LOG4J" /></settings><!-- <plugins> <plugin interceptor="com.manager.util.MybatisInterceptor"></plugin></plugins> --><environments default="mysql"><environment id="mysql"><!-- 指定事务管理类型,type="JDBC"指直接简单使⽤了JDBC的提交和回滚设置 --><transactionManager type="JDBC" /><!-- dataSource指数据源配置,POOLED是JDBC连接对象的数据源连接池的实现 --><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis" /><property name="username" value="root" /><property name="password" value="1qaz@wsx" /></dataSource></environment></environments><mappers><mapper resource="com/gm/mapper/EmployeeMapper.xml" /></mappers></configuration>Employee.java:package com.gm.domain;import java.io.Serializable;public class Employee implements Serializable {/****/private static final long serialVersionUID = 1L;private Integer id;private String loginname;private String password;private String name;private String sex;private String age;private String phone;private Double sal;private String state;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getLoginname() {return loginname;}public void setLoginname(String loginname) {this.loginname = loginname;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getName() {return name;}public void setName(String name) { = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}public Double getSal() {return sal;}public void setSal(Double sal) {this.sal = sal;}public String getState() {return state;}public void setState(String state) {this.state = state;}}EmployeeMapper.xml:<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-////DTD Mapper 3.0//EN" "/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.gm.mapper.EmployeeMapper"><select id="selectEmployeeById" parameterType="int"resultType="com.gm.domain.Employee">SELECT * FROM employee WHERE id = #{id}</select><!-- 查询所有Employee --><select id="selectAllEmployee" parameterType="int"resultType="com.gm.domain.Employee">SELECT * FROM employee</select><!-- 根据id删除Employee --><delete id="deleteEmployeeById" parameterType="int">DELETE FROMemployee WHERE id = #{id}</delete></mapper>EmployeeMapper.java:package com.gm.mapper;import java.util.List;import com.gm.domain.Employee;public interface EmployeeMapper {// 根据id查询EmployeeEmployee selectEmployeeById(Integer id);// 查询所有EmployeeList<Employee> selectAllEmployee();// 根据id删除Employeevoid deleteEmployeeById(Integer id);}⼯⼚⼯具类:MySqlSessionFactory.java:package com.gm.factory;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;public class MySqlSessionFactory {private static SqlSessionFactory sqlSessionFactory = null;static {try {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {e.printStackTrace();}}// 获取SqlSession对象的静态⽅法public static SqlSession getSqlSession() {return sqlSessionFactory.openSession();}// 获取SqlSessionFactory的静态⽅法public static SqlSessionFactory getSessionFactory() {return sqlSessionFactory;}}执⾏⽅法类⼀:OneLevelCacheTest.javapackage com.gm.test;import org.apache.ibatis.session.SqlSession;import com.gm.domain.Employee;import com.gm.factory.MySqlSessionFactory;import com.gm.mapper.EmployeeMapper;public class OneLevelCacheTest {public void testCache1() {// 使⽤⼯⼚类获得SqlSession对象SqlSession sqlSession = MySqlSessionFactory.getSqlSession();// 获得EmployeeMapping对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee1 = employeeMapper.selectEmployeeById(1);System.out.println(employee1);// 再次查询id为1的Employee对象,因为是同⼀个SqlSession,所以会从之前的⼀级缓存中查找数据Employee employee2 = employeeMapper.selectEmployeeById(1);System.out.println(employee2);sqlSession.close();}public static void main(String[] args) {OneLevelCacheTest t = new OneLevelCacheTest();t.testCache1();}public void testCache2() {// 使⽤⼯⼚类获得SqlSession对象SqlSession sqlSession = MySqlSessionFactory.getSqlSession();// 获得EmployeeMapping对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee1 = employeeMapper.selectEmployeeById(1);System.out.println(employee1);// 执⾏delete操作employeeMapper.deleteEmployeeById(4);// commit提交mit();// 再次查询id为1的Employee对象,因为DML操作会清空sqlSession缓存,所以会再次执⾏select语句Employee employee2 = employeeMapper.selectEmployeeById(1);System.out.println(employee2);sqlSession.close();}public void testCache3() {// 使⽤⼯⼚类获得SqlSession对象SqlSession sqlSession = MySqlSessionFactory.getSqlSession();// 获得EmployeeMapping对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee1 = employeeMapper.selectEmployeeById(1);System.out.println(employee1);// 关闭⼀级缓存sqlSession.close();// 再次访问,需要再次获取⼀级缓存,然后才能查找数据,否则会抛出异常sqlSession = MySqlSessionFactory.getSqlSession();// 再次获得EmployeeMapper对象employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee2 = employeeMapper.selectEmployeeById(1);System.out.println(employee2);sqlSession.close();}public void testCache4() {// 使⽤⼯⼚类获得SqlSession对象SqlSession sqlSession = MySqlSessionFactory.getSqlSession();// 获得EmployeeMapping对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee1 = employeeMapper.selectEmployeeById(1);System.out.println(employee1);// 关闭⼀级缓存sqlSession.close();// 再次访问,需要再次获取⼀级缓存,然后才能查找数据,否则会抛出异常sqlSession = MySqlSessionFactory.getSqlSession();// 再次获得EmployeeMapper对象employeeMapper = sqlSession.getMapper(EmployeeMapper.class);// 再次查询id为1的Employee对象,因为DML操作会清空sqlSession缓存,所以会再次执⾏select语句Employee employee2 = employeeMapper.selectEmployeeById(1);System.out.println(employee2);sqlSession.close();}}运⾏之后结果:2018-08-22 09:35:47,858 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Preparing: SELECT * FROM employee WHERE id = ? 2018-08-22 09:35:47,886 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Parameters: 1(Integer)2018-08-22 09:35:47,899 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - <== Total: 1com.gm.domain.Employee@56235b8ecom.gm.domain.Employee@56235b8e通过观察结果可以看出,在第⼀次查询id为1的Employee对象时执⾏了⼀条select语句,但是第⼆次获取id为1的Employee对象时并没有执⾏select语句,因为此时⼀级缓存也就是SqlSession缓存中已经缓存了id为1的Employee对象,Mybatis直接从缓存中将对象取出来,并没有再次去查询数据库,所以第⼆次也就没有执⾏select语句执⾏⽅法类⼆:public static void main(String[] args) {OneLevelCacheTest t = new OneLevelCacheTest();t.testCache2();}运⾏之后的结果:2018-08-22 09:41:17,024 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Preparing: SELECT * FROM employee WHERE id = ?2018-08-22 09:41:17,045 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Parameters: 1(Integer)2018-08-22 09:41:17,058 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - <== Total: 1com.gm.domain.Employee@56235b8e2018-08-22 09:41:17,058 DEBUG [com.gm.mapper.EmployeeMapper.deleteEmployeeById] - ==> Preparing: DELETE FROM employee WHERE id = ?2018-08-22 09:41:17,059 DEBUG [com.gm.mapper.EmployeeMapper.deleteEmployeeById] - ==> Parameters: 4(Integer)2018-08-22 09:41:17,079 DEBUG [com.gm.mapper.EmployeeMapper.deleteEmployeeById] - <== Updates: 12018-08-22 09:41:17,125 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Preparing: SELECT * FROM employee WHERE id = ?2018-08-22 09:41:17,125 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Parameters: 1(Integer)2018-08-22 09:41:17,128 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - <== Total: 1com.gm.domain.Employee@370736d9结果分析:在第⼀次查询id为1的employee对象时执⾏了⼀条select语句,接下来执⾏了⼀个delete并commit操作,Mybatis为了保证缓存中存储的是最新消息,会清空SqlSession缓存。
mybatis缓存总结⼀、什么是mybatis缓存,怎么使⽤mybatis缓存分为⼀级缓存和⼆级缓存,缓存就是将从数据库中查询到的数据放⼊到临时缓冲区内存中;⼀级缓存是⼀直开启的;SqlSession级别的⼀个Map与数据库同⼀次会话期间查询到的数据会放在本地缓存中,再次查询时如果获取相同的数据,直接从缓存中拿,不在从数据库查询。
⼆、⼀级缓存失效的情况1、sqlsession不同,2 sqlsession相同,查询条件不同(缓存中没有这个数据)3、sqlsession相同,两次查询之间执⾏了增删改操作4、sqlsession相同⼿动清除了⼀级缓存三、⼆级缓存的⼯作机制以及使⽤⼆级缓存(全局缓存):基于namespace级别的缓存:⼀个namespace对应⼀个⼆级缓存;⼯作机制: 1、⼀个会话,查询⼀条数据,这个数据就会被放在当前会话的⼀级缓存中;2、如果会话关闭;⼀级缓存中的数据会被保存到⼆级缓存中;新的会话查询信息,就可以参照⼆级缓存中的内容;不同namespace查出的数据会放在⾃⼰对应的缓存中(map) 效果:数据会从⼆级缓存中获取查出的数据都会被默认先放在⼀级缓存中。
只有会话提交或者关闭以后,⼀级缓存中的数据才会转移到⼆级缓存中3、使⽤:1)POJO需要实现序列化接⼝ 2)去mapper.xml中配置使⽤⼆级缓存 <cache></cache> 3)开启全局⼆级缓存配置:<setting name="cacheEnabled" value="true"/>四、缓存有关的设置、属性1)、cacheEnabled=true:false:关闭缓存(⼆级缓存关闭)(⼀级缓存⼀直可⽤的)2)、每个select标签都有useCache="true": false:不使⽤缓存(⼀级缓存依然使⽤,⼆级缓存不使⽤)3)、【每个增删改标签的:flushCache="true":(⼀级⼆级都会清除)】增删改执⾏完成后就会清楚缓存;测试:flushCache="true":⼀级缓存就清空了;⼆级也会被清除;如果flushCache=true;每次查询之后都会清空缓存;缓存是没有被使⽤的;4)、sqlSession.clearCache();只是清楚当前session的⼀级缓存;5)、localCacheScope:本地缓存作⽤域:(⼀级缓存SESSION);当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁⽤⼀级缓存;第三⽅缓存整合:1)、导⼊第三⽅缓存2)、导⼊与第三⽅缓存整合的适配包;官⽅有;包即可;3)、mapper.xml中使⽤⾃定义缓存<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>@Testpublic void testSecondLevelCache02() throws IOException{SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();SqlSession openSession = sqlSessionFactory.openSession();SqlSession openSession2 = sqlSessionFactory.openSession();try{//1、DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);DepartmentMapper mapper2 = openSession2.getMapper(DepartmentMapper.class);Department deptById = mapper.getDeptById(1);System.out.println(deptById);openSession.close();Department deptById2 = mapper2.getDeptById(1);System.out.println(deptById2);openSession2.close();//第⼆次查询是从⼆级缓存中拿到的数据,并没有发送新的sql}finally{}}@Testpublic void testSecondLevelCache() throws IOException{SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();SqlSession openSession = sqlSessionFactory.openSession();SqlSession openSession2 = sqlSessionFactory.openSession();try{//1、EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);Employee emp01 = mapper.getEmpById(1);System.out.println(emp01);openSession.close();//第⼆次查询是从⼆级缓存中拿到的数据,并没有发送新的sql//mapper2.addEmp(new Employee(null, "aaa", "nnn", "0"));Employee emp02 = mapper2.getEmpById(1);System.out.println(emp02);openSession2.close();}finally{}}@Testpublic void testFirstLevelCache() throws IOException{SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();SqlSession openSession = sqlSessionFactory.openSession();try{EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);Employee emp01 = mapper.getEmpById(1);System.out.println(emp01);//xxxxx//1、sqlSession不同。
mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和
mybatis整合ehcache
1 查询缓存1.1 什么是查询缓存mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
mybaits 提供一级缓存,和二级缓存。
一级缓存是SqlSession级别的缓存。
在操作数据库时需要构造sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。
不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。
Mybatis默认开启一级缓存。
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper 的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存是多个SqlSession 共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql 中传递参数也相同即最终执行相同的sql语句,第一次执行
完毕会将数据库中查询的数据写到缓存(内存),第二次会
从缓存中获取数据将不再从数据库查询,从而提高查询效率。
Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。
如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
1.2 一级缓存1.2.1 一级缓存工作原理下图是根据id查询用户的一级缓存图解第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的
用户信息,如果没有,从数据库查询用户信息。
得到用户信息,将用户信息存储到一级缓存中。
如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
1.2.2 一级缓存测试mybatis默认支持一级缓存,不需要在配置文件去配置。
按照上边一级
缓存原理步骤去测试。
@Test public void testCache1() throws Exception{ SqlSessionsqlSession = sqlSessionFactory.openSession();//创建代理对象UserMapperuserMapper =
sqlSession.getMapper(UserMapper.class); //
下边查询使用一个SqlSession //第一次发起请求,查询id为1的用户Useruser1 =
userMapper.findUserById(1);
System.out.println(user1); // 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
//更新user1的信息
user1.setUsername('测试用户22');
userMapper.updateUser(user1); //执行commit操作去清空缓存mit(); //第二次发起请求,查询id为1的用户Useruser2 = userMapper.findUserById(1);
System.out.println(user2);
sqlSession.close(); }1.2.3 一级缓存应用正式开发,是将mybatis和spring进行整合开发,事务控制在service中。
一个service方法中包括很多mapper方法调用。
service{ //开始执行时,开启事务,创建SqlSession 对象//第一次调用mapper的方法findUserById(1) //第二次调用mapper的方法findUserById(1),从一级缓存中取数据//aop控制只要方法结束,sqlSession 关闭sqlsession关闭后就销毁数据结构,清空缓存Service结束sqlsession关闭}如果是执行两次service调用查询相同的用户信息,不走一级缓存,因为Service方法结束,sqlSession就关闭,一级缓存就清空。
1.3 二级缓存
1.3.1 原理首先开启mybatis的二级缓存。
sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。
如果SqlSession3去执行相同mapper下sql,执行commit提交,清空该mapper下的二级缓存区域的数据。
sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。
数据类型仍然为HashMapUserMapper有一个二级缓存区域(按namespace分,如果namespace相同则使用同一个相同的二级缓存区),其它mapper也有自己的二级缓存区域(按namespace分)。
每一个namespace的mapper 都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。
1.3.2 开启二级缓存mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。
在核心配置文件SqlMapConfig.xml中加入
<settingname='cacheEnabled'value='true'/><!-- 全局配置参数,需要时再设置--> <settings>
<!-- 开启二级缓存默认值为true -->
<settingname='cacheEnabled'value='true'/>
</settings> 描述允许值默认值cacheEnabled对在此配置文件下的所有cache 进行全局性开/关设置。
true falsetrue。