一级缓存(默认是开启的)
一级缓存是就是sqlsession,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域,一级缓存中的key是由sql语句、条件、statement等信息组成的一个唯一值,一级缓存中的value,就是查询出的结果对象。
原理:当第一次查询id为1的用户时,会将查询结果写入sqlsession一级缓存区域的map中,此时如果再查询一次id为1的用户,就会直接查询一级缓存中的数据(不用查询数据库,只有一个sql语句)。但如果在两次查询中有修改、添加、删除用户执行了commit时,则会清空一级缓存中的数据,此时在查询id为1的用户,则会重新查询数据库(两条sql查询语句)
例1: 两次查询没有修改、删除、添加commit
1.编写测试类,调用usermapper中的查询方法,usermapper中的的查询方法前边有,这里不多写
public class Demo12 {
/**
// * session级别一级缓存,默认开启
// * @throws IOException
// */
@Test
public void test11() throws IOException {
InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
// 1.通过SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂。
SqlSessionFactory factory= new SqlSessionFactoryBuilder().build(is);
//2.会话
SqlSession session=factory.openSession();
//3.通过会话获取dao接口(mapper)
UserMapper mapper=session.getMapper(UserMapper.class);
User user1=mapper.findUserById(1);
System.out.println("user1:"+user1);
User user2=mapper.findUserById(1);
System.out.println("user2:"+user2);
session.commit();
session.close();
}
}
2.查询结果,只有一条sql语句
DEBUG [main] - ==> **Preparing: SELECT * from user where id=?**
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
user1:User{id=1, username='王五', sex='2', birthday=null, address='null'}
user2:User{id=1, username='王五', sex='2', birthday=null, address='null'}
例2: 两次查询之间有修改、删除、添加commit
1.编写测试类,调用usermapper中的查询方法和保存方法,usermapper中的的查询和保存方法前边有,这里不多写
public class Demo12 {
/**
// * session级别一级缓存,默认开启
// *保存,删除,更新,一级缓存数据会自动更新,会执行sql语句
// * @throws IOException
// */
@Test
public void test11() throws IOException {
InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
// 1.通过SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂。
SqlSessionFactory factory= new SqlSessionFactoryBuilder().build(is);
//2.会话
SqlSession session=factory.openSession();
//3.通过会话获取dao接口(mapper)
UserMapper mapper=session.getMapper(UserMapper.class);
User user1=mapper.findUserById(1);
System.out.println("user1:"+user1);
//保存用户
mapper.save(new User("广东","1",null,"广东"));
User user2=mapper.findUserById(1);
System.out.println("user2:"+user2);
}
}
2.查询结果,有两条查询sql语句
DEBUG [main] - ==> **Preparing: SELECT * from user where id=?**
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
user1:User{id=1, username='王五', sex='2', birthday=null, address='null'}
DEBUG [main] - ==> Preparing: insert into user (username,sex,birthday,address) values (?,?,?,?)
DEBUG [main] - ==> Parameters: 广东(String), 1(String), null, 广东(String)
DEBUG [main] - <== Updates: 1
DEBUG [main] - ==> **Preparing: SELECT * from user where id=?**
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
user2:User{id=1, username='王五', sex='2', birthday=null, address='null'}
二级缓存(所有一级缓存共享。需要手动开启)
二级缓存就是同一个namespace下的mapper(sessionFactory),二级缓存中,也有一个map结构,这个区域就是一级缓存区域,一级缓存中的key是有sql语句、条件、statement等信息组成的一个唯一值,一级缓存中的value,就是查询出的结果对象。
原理:将session写入二级缓存中,必须将session关闭才能写入
实现:
1.在全局配置文件中配置二级缓存总开关
<settings>
<!--允许开启二级缓存-->
<setting name="cacheEnabled" value="true"></setting>
</settings>
2.在UserMapper中配置二级缓存
<!--配置二级缓存
默认使用的是mybatis自带的缓存技术,prepetualCache(永久缓存)
-->
<cache></cache>
3.user序列化
public class User implements Serializable {
案例1:创建三个session,分别查询用户信息
1.编写测试类
/**
// * sessionFactory级别二级缓存,需手动开启
// *保存,删除,更新,一级缓存数据会自动更新,会执行sql语句
// * @throws IOException
// */
@Test
public void test12() throws IOException {
InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
// 1.通过SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂。
SqlSessionFactory factory= new SqlSessionFactoryBuilder().build(is);
//2.会话
SqlSession session1=factory.openSession();
SqlSession session2=factory.openSession();
SqlSession session3=factory.openSession();
//3.获取dao
UserMapper mapper1= session1.getMapper(UserMapper.class);
UserMapper mapper2= session2.getMapper(UserMapper.class);
UserMapper mapper3= session3.getMapper(UserMapper.class);
User user1=mapper1.findUserById(1);
System.out.println("user1"+user1);
//session关闭之后,才能写入二级缓存中
session1.close();
User user2=mapper2.findUserById(1);
System.out.println("user2"+user2);
session1.close();
User user3=mapper3.findUserById(1);
System.out.println("user3"+user3);
}
2.结果,只有一条查询语句
DEBUG [main] - ==> **Preparing: SELECT * from user where id=?**
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
user1User{id=1, username='王五', sex='2', birthday=null, address='null'}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5f71c76a]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5f71c76a]
DEBUG [main] - Returned connection 1601292138 to pool.
DEBUG [main] - Cache Hit Ratio [com.szl.mapper.UserMapper]: 0.5
user2User{id=1, username='王五', sex='2', birthday=null, address='null'}
DEBUG [main] - Cache Hit Ratio [com.szl.mapper.UserMapper]: 0.6666666666666666
user3User{id=1, username='王五', sex='2', birthday=null, address='null'}
案例2:创建三个session,分别查询用户信息,在查询之间有保存提交
1.编写测试类
/**
// * sessionFactory级别二级缓存,需手动开启
// *保存,删除,更新,一级缓存数据会自动更新,会执行sql语句
// * @throws IOException
// */
@Test
public void test12() throws IOException {
InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
// 1.通过SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂。
SqlSessionFactory factory= new SqlSessionFactoryBuilder().build(is);
//2.会话
SqlSession session1=factory.openSession();
SqlSession session2=factory.openSession();
SqlSession session3=factory.openSession();
//3.获取dao
UserMapper mapper1= session1.getMapper(UserMapper.class);
UserMapper mapper2= session2.getMapper(UserMapper.class);
UserMapper mapper3= session3.getMapper(UserMapper.class);
User user1=mapper1.findUserById(1);
System.out.println("user1"+user1);
//session关闭之后,才能写入二级缓存中
session1.close();
//保存
mapper3.save(new User("斗门雨神","1",null,"广东"));
session3.commit();
session3.close();
User user2=mapper2.findUserById(1);
System.out.println("user2"+user2);
session2.close();
}
2.结果,有两条查sql语句
DEBUG [main] - ==> **Preparing: SELECT * from user where id=?**
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
user1User{id=1, username='王五', sex='2', birthday=null, address='null'}
DEBUG [main] - ==> **Preparing: insert into user (username,sex,birthday,address) values (?,?,?,?)**
DEBUG [main] - ==> Parameters: 斗门雨神(String), 1(String), null, 广东(String)
DEBUG [main] - ==> **Preparing: SELECT * from user where id=?**
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
user2User{id=1, username='王五', sex='2', birthday=null, address='null'}
mybatis整合ehcache缓存
mybatis本身是一个持久性层框架,不是专门的缓存框架,所以对缓存的实现不够好,不能支持分布式。
ehcache是一个分布式的缓存框架。
分布式:系统为了提高性能,通常会对系统采用分布式部署(集群式部署)——有M服务器,每个服务器上跑相同的项目,如果每台服务器的最大访问量是N,则此时就可以有MN个访问用户。
整合思路:
Cache是一个接口,它的默认实现的是mybatis的perpetualCache,如果想要整合mybatis的二级缓存,则实现Cache接口即可。
整合步骤:
1.添加jar包,放到lib中
2.设置映射文件中的cache标签
<!--配置二级缓存
默认使用的是mybatis自带的缓存技术,perpetualCache(永久缓存)
type可以改成ehcache缓存
-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
3.在src下添加ehcache配置文件
maxElementsInMemory :设置基于内存的缓存中可存放的对象最大数目
eternal:设置对象是否为永久的,true表示永不过期,此时将忽略
timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false
timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值
overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
flashInterval:设置二级缓存刷新间隔,过了这个时间就清空(毫秒)
diskPersistent 当jvm结束时是否持久化对象 true false 默认是false
diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
4.测试结果同Mybatis二级缓存测试结果
禁用指定方法的二级缓存
直接在mapper.XML文件中要禁用的方法中添加useCache=“false”即可,默认是true
<select id="findUserById" parameterType="int" resultType="com.szl.model.User" **useCache="false"**>
SELECT * from user where id=#{id}
</select>
结果:禁用查询缓存后,sql语句有两条
DEBUG [main] - ==> Preparing: SELECT * from user where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
user1User{id=1, username='王五', sex='2', birthday=null, address='null'}
DEBUG [main] - ==> Preparing: SELECT * from user where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
user2User{id=1, username='王五', sex='2', birthday=null, address='null'}
刷新缓存
<!--默认插入、更新、删除会清空缓存,默认情况下flushCache=“true”,如果设置为false,则上述方法就不会清空二级缓存-->
<insert id="save" parameterType="com.szl.model.User" flushCache="false">
insert into user (username,sex,birthday,address)
values (#{username},#{sex},#{birthday},#{address})
</insert>