本文讲述HIbernate中一级、二级缓存的概念以及如何使用。
一、大纲
2.什么是一级缓存
3.一级缓存示例展示
4.二级缓存以及示例展示
5.总结
二、什么是一级缓存
在hibernate中所谓的一级缓存就是session对象,但是一级缓存对提高性能的作用性并不是很大,其session主要的目的是管理实体对象的状态(临时、离线、持久化)。
其缓存模型如下图:
三、一级缓存示例
3.1 查询(get,load, HQL)
3.1.1 get/load方法
@Test
public void testGet() {
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
tx.begin();
// 第一次查询
User user1 = (User)session.get(User.class, 1);
System.out.println(user1);
// 如果session中存在id=1的User对象缓存,那么再次查询时,将不需要执行SQL
User user2 = (User)session.get(User.class, 1);
System.out.println(user2);
tx.commit();
session.close();
} 如果执行第二次查询在session关闭之后,那么会抛出异常,session已经关闭。load方法类型,只是其支持懒加载查询。
3.1.2 Hql查询
@Test
public void testHqlQueryFromCache() throws Exception {
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
tx.begin();
// 使用HQL查询id=1的User对象,此时会将id=1的对象写入到session缓存中
User user1 = (User)session.createQuery("FROM User WHERE id = 1").uniqueResult();
System.out.println(user1);
System.out.println("================");
// 执行第二次同样查询,默认情况无法从session中获取id=1的对象,但是也会将id=1的对象写入session缓存中
User user2 = (User)session.createQuery("FROM User WHERE id = 1").uniqueResult();
System.out.println(user2);
System.out.println("================");
// 使用get方法查询,由于session中已经存在id=1的对象,所以不会执行SQL
User user3 = (User)session.get(User.class, 1);
System.out.println(user3);
tx.commit();
session.close();
}3.2 Update/delete对Session的影响
在session执行查询之后,如果此时执行update或者delete操作,session中的数据是否会发生变化呢?通过下面这个例子展示:
@Test
public void testUpdate() {
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
tx.begin();
// 执行查询操作,将id=1的数据,存放在session中
User user = (User)session.get(User.class, 1);
System.out.println(user);
// 执行update操作,将id=1的数据修改
session.createQuery("UPDATE User SET name = ? WHERE id = 1").setParameter(0, "updateOrDelete").executeUpdate();
// 如果执行的update操作,修改了session中的实体name属性,那么获取时将得到updateOrDelete值,否则保持变
User user2 = (User)session.get(User.class, 1);
System.out.println(user2);
tx.commit();
session.close();
}
得出结论update或者delete(这里未展示delete操作),不会修改当前session的数据,只能等待下次session查询,或者手动使用session.refresh()/clear等刷新session数据。
四、二级缓存以及示例展示
4.1 什么是二级缓存
二级缓存实际上是提供了一个公共数据缓存库,用于提高查询性能,当对数据的实时性要求非常高时,没有必要使用二级缓存,二级缓存模型如下:
可以理解为二级缓存是不同session的共享数据,而一级缓存是针对同一个session操作的。
4.2 配置二级缓存
Hibernate默认并不使用二级缓存,如果使用的话,需要配置一些参数提供使用;
1、配置二级缓存类型(提供者)《hibernate.cfg.xml配置中》
<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
2、设置需要配置的实体类、集合、查询条件
<!-- 开启查询缓存 --> <property name="hibernate.cache.use_query_cache">true</property> <!-- 实体类 --> <class-cache usage="read-write" class="com.hibernate.seconde_cache.User" /> <!-- 注:也可以在实体映射文件中配置<cache>这里不做介绍 -->
4.3 各种类型缓存处理
在4.2中粗略的展示了一些基本的二级缓存配置,在此节中将主要展示三种情况二级缓存配置。
4.3.1 单个实体二级缓存
1)配置
<!-- 设置缓存提供者 --> <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property> <!-- 实体类 --> <class-cache usage="read-write" class="com.hibernate.seconde_cache.User" />
2)示例展示
@Test
public void testSingleEntity() throws Exception {
// 第一个session,存储数据
Session session1 = factory.openSession();
Transaction tx1 = session1.beginTransaction();
tx1.begin();
User user1 = (User)session1.get(User.class, 1);
System.out.println(user1);
tx1.commit();
session1.close();
System.out.println("\n===================\n");
// 第二个session读取第一个session的数据
Session session2 = factory.openSession();
Transaction tx2 = session2.beginTransaction();
tx2.begin();
User user2 = (User)session2.get(User.class, 1);
System.out.println(user2);
tx2.commit();
session2.close();
}3)update和delete对二级缓存的影响
@Test
public void testSingleEntityOfUpdate() throws Exception {
// 第一个session,存储数据
Session session1 = factory.openSession();
Transaction tx1 = session1.beginTransaction();
tx1.begin();
User user1 = (User)session1.get(User.class, 1);
System.out.println(user1);
tx1.commit();
session1.close();
System.out.println("\n===================\n");
Session updateSession = factory.openSession();
updateSession.createQuery("UPDATE User SET name=? WHERE id = 1").setParameter(0, "Second Cache").executeUpdate();
System.out.println("\n===================\n");
// 第二个session读取第一个session的数据
Session session2 = factory.openSession();
Transaction tx2 = session2.beginTransaction();
tx2.begin();
User user2 = (User)session2.get(User.class, 1);
System.out.println(user2);
tx2.commit();
session2.close();
} 由此可以update和delete的操作,会即使影响二级缓存的操作,其他session可以及时发现
4.3.2 查询条件缓存
在介绍如何缓存查询条件之前,如何通过Hibernate提供的方法实现缓存,并解释此种方法的不良效果,以及引入查询条件如何缓存。
1)非查询条件实现缓存(缓存配置同4.3.1)
使用list执行相同查询条件,依旧重复执行SQL
@Test
public void testHql() throws Exception {
// 第一个session,存储数据
Session session1 = factory.openSession();
Transaction tx1 = session1.beginTransaction();
tx1.begin();
List user1= session1.createQuery("FROM User").list();
System.out.println(user1);
tx1.commit();
session1.close();
System.out.println("\n===================\n");
// 第二个session读取第一个session的数据
Session session2 = factory.openSession();
Transaction tx2 = session2.beginTransaction();
tx2.begin();
List user2 = session2.createQuery("FROM User").list();
System.out.println(user2);
tx2.commit();
session2.close();
}
使用iterate方法替代list方法,但是会出现N+1此查询问题,先查询符合条件的Id,之后在根据Id查询。
@Test
public void testHqlOfIterate() throws Exception {
Session session1 = factory.openSession();
Transaction tx1 = session1.beginTransaction();
tx1.begin();
// 使用iterate方法替代List方法
Iterator<User> users1= session1.createQuery("FROM User").iterate();
while(users1.hasNext()) {
System.out.println(users1.next());
}
tx1.commit();
session1.close();
System.out.println("\n===================\n");
Session session2 = factory.openSession();
Transaction tx2 = session2.beginTransaction();
tx2.begin();
Iterator<User> users2= session2.createQuery("FROM User").iterate();
while(users2.hasNext()) {
System.out.println(users2.next());
}
tx2.commit();
session2.close();
} 2)使用查询条件缓存
配置相比于4.3.1需要添加:
<!-- 开启查询缓存 --> <property name="hibernate.cache.use_query_cache">true</property>
就如1)中所属,使用查询条件过滤,可以减少1)中的查询次数,大大提高查询效率:
@Test
public void testHqlOfCondition() throws Exception {
Session session1 = factory.openSession();
Transaction tx1 = session1.beginTransaction();
tx1.begin();
// 通过执行setCacheable(true)方法,表示将此次查询条件缓存
// 也就是以FROM Use WHERE id < 4为键存储,值:对应查询的结果集
List list1= session1.createQuery("FROM User WHERE id < 4").setCacheable(true).list();
System.out.println(list1);
tx1.commit();
session1.close();
System.out.println("\n===================\n");
Session session2 = factory.openSession();
Transaction tx2 = session2.beginTransaction();
tx2.begin();
List list2= session2.createQuery("FROM User WHERE id < 4").setCacheable(true).list();
System.out.println(list2);
tx2.commit();
session2.close();
} 注:查询条件必须保持一直,否则视为不同查询条件。delete和update操作与4.3.1的影响一致,这里不做效果展示。
4.3.3 集合元素查询缓存展示
示例实体映射模型为(班级、学生方式《多对1的关系》),具体的映射代码、实体类代码这里不展示。
1)配置(相比4.3.1添加如下配置)
<!-- 配置班级实体缓存 --> <class-cache usage="read-write" class="com.hibernate.seconde_cache.Classes"/> <!-- 配置学生实体缓存 --> <class-cache usage="read-write" class="com.hibernate.seconde_cache.Student"/> <!-- 配置班级中的学生集合缓存,注意使用的方式是类全限定名.属性名 --> <collection-cache usage="read-write" collection="com.hibernate.seconde_cache.Classes.students"/>
2)代码示例:
@Test
public void testSet() throws Exception {
Session session1 = factory.openSession();
Transaction tx1 = session1.beginTransaction();
tx1.begin();
// 查询id=1的班级
Classes classes1 = (Classes)session1.get(Classes.class, 1);
System.out.println(classes1);
// 执行懒加载获取数据;如果想将集合元素也缓存,那么必须添加集合配置
System.out.println(classes1.getStudents());
tx1.commit();
session1.close();
System.out.println("\n===================\n");
Session session2 = factory.openSession();
Transaction tx2 = session2.beginTransaction();
tx2.begin();
// 第二个session,获取二级缓存中的数据
Classes classes2 = (Classes)session2.get(Classes.class, 1);
System.out.println(classes2);
System.out.println(classes2.getStudents());
tx2.commit();
session2.close();
}如果没有添加
<collection-cache usage="read-write" collection="com.hibernate.seconde_cache.Classes.students"/>
那么会出现如下结果:
五、总结
涉及到的二级缓存知识,远远比本文多,一般不会使用本文中的Hashtable提供者,通常使用:
1级缓存是必须使用的,因为任何操作都需要使用到session,但是其对性能提高影响不大,而二级缓存对那些对数据实时性以及准确性要求不高的情况,建议使用,可以提高查询性能。
本文出自 “java程序冥” 博客,请务必保留此出处http://793404905.blog.51cto.com/6179428/1617287
原文:http://793404905.blog.51cto.com/6179428/1617287