1 @Test 2 public void testCache() { 3 Session session = sf.openSession(); 4 session.beginTransaction(); 5 Topic t1 = (Topic)session.get(Topic.class, 5); 6 Topic t3 = (Topic)session.get(Topic.class, 5); 7 System.out.println("session1:"); 8 System.out.println(t1); 9 System.out.println(t3); 10 session.getTransaction().commit(); 11 session.close(); 12 13 14 15 Session session2 = sf.openSession(); 16 session2.beginTransaction(); 17 Topic t2 = (Topic)session2.load(Topic.class, 5); 18 // sf.evict(Topic.class, 5); 19 System.out.println("session2:"); 20 System.out.println(t2); 21 session2.getTransaction().commit(); 22 session2.close(); 23 }
console:
Hibernate:
select
topic0_.id as id2_1_,
topic0_.category_id as category4_2_1_,
topic0_.createDate as createDate2_1_,
topic0_.title as title2_1_,
category1_.id as id0_0_,
category1_.name as name0_0_
from
Topic topic0_
left outer join
Category category1_
on topic0_.category_id=category1_.id
where
topic0_.id=?
session1:
com.bjsxt.hibernate.Topic@1056bc5:5
com.bjsxt.hibernate.Topic@1056bc5:5
session2:
com.bjsxt.hibernate.Topic@f26800:5
我的问题:
session2里面load时没有发sql就证明是从二级缓存里取的啊,但为啥拿出的topic对象与之前在session1里的不是同一个呢?
其实二级缓存在存储数据的时候,做了一些特殊的处理,当数据从数据库加载到内存的时候,放入一级缓存(之前一级缓存的原理就不细说了),也罢数据放入二级缓存了,但是放入二级缓存的数据却不是对象,这一点和一级缓存有区别,一级缓存放入的是一个具体的对象,对象的属性是有值的,所以可以直接获取(并且是同一个对象),但数据哎放入二级缓存的时候,hibernate内部吧该对象装箱了(吧具体对象转化成了object对象),但该object对象依然具有和和原始对象一样的属性只不过类型却是object类型(也称散装数据),装箱的同时也把该对象的类型字符串(包名加类型)也保存了一份,具体结构是,吧该对象和该对象的类型字符串通过键值对保存了二级缓存(本质是一个map集合所以可以通过键值对保存),但键却很特殊的字符串,键是该类型字符串和该对象的oid(对呀数据主键)的拼接,通过”#”分割,具体是类字符串#oid,对应该对象(objec)。当重二级缓存中获取该数据(不能说是对象)的时候,会更具id和类字符去匹配二级缓存的所有key,如果能找到,则通过反射生产一个对象,并返回给用户(程序),这就很合理的解释了为什么不是同一个对象,因为是反射生产的,和很好的解释了为什么要保存该对象的类字符串,那么为什么要反射,而不直接吧该对象拆箱呢?,个人认为:如果拆箱(吧object转化为具体的po对象)就是一个对象,二级缓存是多个线程共享,如果多个用户同时公用同一个对象(内存地址的指向相同),会引发安全问题(反正这样不好),