浏览 6731 次
锁定老帖子 主题:Hibernate 二级缓存的一些疑问?
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2006-08-02
我写了二个例子来验证二级缓存的使用情况: 这两个例子是在二级缓存开启时运行的: 例一: 在执行这段代码前以保证所有数据已经在二级缓存中 Session session = HibernateSessionFactory.currentSession(); TUser u = (TUser) session.load(TUser.class, new Integer(3)); System.out.println("name = " + u.getName());//直接从缓存加载 TUser u2 = (TUser) session.load(TUser.class, new Integer(3)); System.out.println("name = " + u2.getName());//直接从缓存加载 session.clear(); session.close(); Session session2 = HibernateSessionFactory.currentSession(); TUser u1 = (TUser)session2.load(TUser.class,new Integer(4)); System.out.println("name = " + u1.getName());//直接从缓存加载 session2.close(); 这3次加载并没有产生sql查询,而是直接从缓存中获取到了数据,这个可以理解,数据应该是从一级缓存或二级缓存中加载的。 这个例子如果在关闭二级缓存的情况下运行,将执行2次SQL查询,这个也可以理解,第一次加载后,由于第二次是从一级缓存中取的数据,所以第二次不产生SQL查询。而第三次是一个新的session,需要SQL查询。 对这个例子的疑问在于:不是说只有load才能使用二级缓存吗?使用get时将查询一级缓存查找不到就直接执行SQL查询了,但上面这个例子我把所有的load换成get,运行结果却和没换前一样。 例二: 在执行这段代码前以保证所有数据已经在二级缓存中 Session session = HibernateSessionFactory.currentSession(); TUser u = (TUser) session.load(TUser.class, new Integer(3)); System.out.println("name = " + u.getName());//直接从缓存加载 Transaction tx = session.beginTransaction(); Query query = session.createQuery("delete TUser where id<5"); query.executeUpdate(); tx.commit(); // session.evict(u); TUser u2 = (TUser) session.load(TUser.class, new Integer(3)); System.out.println("name = " + u2.getName());//从一级缓存加载了脏数据 session.clear();//有无这个对后面一次查询无影响 session.close(); Session session2 = HibernateSessionFactory.currentSession(); TUser u1 = (TUser)session2.load(TUser.class,new Integer(4)); System.out.println("name = " + u1.getName());//产生了SQL查询,报错 session2.close(); 以我现在的了解,这个例子的最后一次报错也不应该出现呀?应该还是可以从二级缓存中取到脏数据,而不是产生一次SQL查询。难道说Hibernate 3.05在执行bulk delete时,没有去清空一级缓存,反倒清空了二级缓存? 请教各位产生这种问题的原因,在Hibernate 3.0中,bulk delete/update有没有处理二级缓存?因为我查以前的资料《深入浅出Hibernate》里的例子都说明用bulk delete时并不会对缓存进行处理。如果是这样那例二的第三次加载时不应该报错呀,而应该是取得二级缓存中的脏数据。 网上多数资料都说get不会查询二级缓存,但在第一个例子中,我将load换成了get,运行结果却没有任何区别,难道在这个版本中get出会查询二级缓存? 谢谢! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2006-08-03
谁说get方法不查找二级缓存???
只有 find方法不会查找 二级缓存, 请祥看 我写的 文章,你就明白了!! http://blog.csdn.net/woshichenxu/archive/2006/01/22/586361.aspx |
|
返回顶楼 | |
发表时间:2006-08-03
chenxu 写道 谁说get方法不查找二级缓存???
只有 find方法不会查找 二级缓存, 请祥看 我写的 文章,你就明白了!! http://blog.csdn.net/woshichenxu/archive/2006/01/22/586361.aspx 写得不错,不过关于这点 “ 4、不推荐采用hibernate的批量删除方法来删除大批量的记录数据。 原因是hibernate的批量删除会执行1条查询语句外加 满足条件的n条删除语句。而不是一次执行一条条件删除语句!! 当待删除的数据很多时会有很大的性能瓶颈!!!” 这是在版本2.X才有的。 对于3.0以上,已经是一条sql语句搞定了。 |
|
返回顶楼 | |
发表时间:2006-08-03
Jamsa 写道 Hibernate-Version: 3.0.5
我写了二个例子来验证二级缓存的使用情况: 这两个例子是在二级缓存开启时运行的: 例一: 在执行这段代码前以保证所有数据已经在二级缓存中 Session session = HibernateSessionFactory.currentSession(); TUser u = (TUser) session.load(TUser.class, new Integer(3)); System.out.println("name = " + u.getName());//直接从缓存加载 TUser u2 = (TUser) session.load(TUser.class, new Integer(3)); System.out.println("name = " + u2.getName());//直接从缓存加载 session.clear(); session.close(); Session session2 = HibernateSessionFactory.currentSession(); TUser u1 = (TUser)session2.load(TUser.class,new Integer(4)); System.out.println("name = " + u1.getName());//直接从缓存加载 session2.close(); 这3次加载并没有产生sql查询,而是直接从缓存中获取到了数据,这个可以理解,数据应该是从一级缓存或二级缓存中加载的。 这个例子如果在关闭二级缓存的情况下运行,将执行2次SQL查询,这个也可以理解,第一次加载后,由于第二次是从一级缓存中取的数据,所以第二次不产生SQL查询。而第三次是一个新的session,需要SQL查询。 对这个例子的疑问在于:不是说只有load才能使用二级缓存吗?使用get时将查询一级缓存查找不到就直接执行SQL查询了,但上面这个例子我把所有的load换成get,运行结果却和没换前一样。 例二: 在执行这段代码前以保证所有数据已经在二级缓存中 Session session = HibernateSessionFactory.currentSession(); TUser u = (TUser) session.load(TUser.class, new Integer(3)); System.out.println("name = " + u.getName());//直接从缓存加载 Transaction tx = session.beginTransaction(); Query query = session.createQuery("delete TUser where id<5"); query.executeUpdate(); tx.commit(); // session.evict(u); TUser u2 = (TUser) session.load(TUser.class, new Integer(3)); System.out.println("name = " + u2.getName());//从一级缓存加载了脏数据 session.clear();//有无这个对后面一次查询无影响 session.close(); Session session2 = HibernateSessionFactory.currentSession(); TUser u1 = (TUser)session2.load(TUser.class,new Integer(4)); System.out.println("name = " + u1.getName());//产生了SQL查询,报错 session2.close(); 以我现在的了解,这个例子的最后一次报错也不应该出现呀?应该还是可以从二级缓存中取到脏数据,而不是产生一次SQL查询。难道说Hibernate 3.05在执行bulk delete时,没有去清空一级缓存,反倒清空了二级缓存? 请教各位产生这种问题的原因,在Hibernate 3.0中,bulk delete/update有没有处理二级缓存?因为我查以前的资料《深入浅出Hibernate》里的例子都说明用bulk delete时并不会对缓存进行处理。如果是这样那例二的第三次加载时不应该报错呀,而应该是取得二级缓存中的脏数据。 网上多数资料都说get不会查询二级缓存,但在第一个例子中,我将load换成了get,运行结果却没有任何区别,难道在这个版本中get出会查询二级缓存? 谢谢! hibernate的官方文档真的该骂,很多写得不详细。 实际上,hibernate3.0.5版本以上的处理机制是这样的: 1、对于bulk update,hibernate毫无办法,这时脏数据肯定不能避免,而且也的确能理解。对于hibernate来说,如果要循环去更新每一个缓存,多困难。 2、对于bulk delete,hibernate所能做的就是直接将对于这个class的所有缓存清除。所以,不管你做bulk delete后,然后取任何一个,当然是去数据库直接读取了。比如,你先load出根据id为:1、2、3的对象;然后用批量删除,删掉1、2;然后再去load 3的。就会发现,实际上3的对象也不在缓存当中了,同样需要发sql请求去数据库访问。 |
|
返回顶楼 | |
发表时间:2006-08-03
谢谢JBoss和chenxu的帮助,我对二级缓存的问题清楚了一些。
另外,在前面的基础上又做了一些测试。JBoss兄说的delete对于缓存的管理是正确的,hibernate直接清空了整个对象的二级缓存。但对于update却好像有一些偏差,我的测试代码如下: public void testUpdate() { Session session = HibernateSessionFactory.currentSession(); TUser u = (TUser) session.load(TUser.class, new Integer(3)); System.out.println("加载到了一级缓存,name = " + u.getName());// 直接从缓存加载 Transaction tx = session.beginTransaction(); Query query = session.createQuery("update TUser set age = 100"); query.executeUpdate(); tx.commit(); TUser l1 = (TUser) session.get(TUser.class, new Integer(3)); System.out.println("一级缓存脏数据:" + l1.getAge()); session.close(); Session session2 = HibernateSessionFactory.currentSession(); TUser l2 = (TUser) session2.get(TUser.class, new Integer(3)); if (l2 != null) System.out.println("换了一个session, 二级缓存已被清空了,需要执行sql查询name = " + l2.getAge()); else System.out.println("u is null"); session2.close(); } 执行update的情况和delete时的情况是一样的,二级缓存被清空了。 总结(如果不正确,请大家指正): 1、在执行bulk update/delete操作时,Hibernate将不会处理一级缓存,如果此时从一级缓存加载数据,将得到脏数据。 2、在执行bulk update/delete操作时,Hibernate将清空被delete或update的对象的二级缓存,重新加载些对象的任何一条记录时都将执行sql查询。 也就是说,在执行bulk update/delete时,如果要消除缓存的影响,只需要用evict来清空一级缓存,而二级缓存Hibernate将会自动清空。 这里顺便提一下小技巧: 可以在执行bulk update/delete前先获取将被批量删除的对象,用session.evict(u)清除缓存,由于此时对象仍在二级缓存中,加载速度较快。 然后再调用再调用bulk update/delete语句,而不应该在执行bulk update/delete之后再去执行session.evict,如果这样做,由于二级缓存的对象已被清除了,再加载时将从一级缓存加载,如果加载不到就将产生sql查询了。如果先于bulk update/delete之前用session.evict对象就不会产生这个问题了。 我的测试代码: protected void setUp() throws Exception { super.setUp(); Session session = HibernateSessionFactory.currentSession(); Transaction tx = session.beginTransaction(); for (int i = 0; i < 20; i++) { TUser u = new TUser(); u.setId(new Integer(i)); u.setName("name" + i); u.setAge(new Integer(i)); session.save(u); } tx.commit(); session.close(); } protected void tearDown() throws Exception { super.tearDown(); Session session = HibernateSessionFactory.currentSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery("delete TUser"); query.executeUpdate(); tx.commit(); session.close(); } public void testUpdate() { Session session = HibernateSessionFactory.currentSession(); TUser u = (TUser) session.load(TUser.class, new Integer(3)); System.out.println("加载到了一级缓存,name = " + u.getName());// 直接从缓存加载 TUser u3 = (TUser) session.load(TUser.class, new Integer(4)); System.out.println("加载到了一级缓存,name = " + u3.getName()); session.evict(u); System.out.println("清空了id为3的对象的一级缓存,下次在一级缓存中加载时将执行sql,而不会得到脏数据。"); Transaction tx = session.beginTransaction(); Query query = session.createQuery("update TUser set age = 100"); query.executeUpdate(); tx.commit(); TUser l1 = (TUser) session.get(TUser.class, new Integer(3)); System.out.println("由于清空id为3的对象的一级缓存,上面会执行sql查询,不会得到脏数据:" + l1.getAge()); TUser l11 = (TUser) session.get(TUser.class, new Integer(4)); System.out.println("由于没有清空id为4的对象的一级缓存,将得到脏数据:" + l11.getAge()); session.close(); Session session2 = HibernateSessionFactory.currentSession(); TUser l2 = (TUser) session2.get(TUser.class, new Integer(3)); if (l2 != null) System.out.println("换了一个session, 二级缓存已被清空了,需要执行sql查询name = " + l2.getAge()); else System.out.println("u is null"); session2.close(); } |
|
返回顶楼 | |