背景:使用Memcache+Hibernate时,在同一个session中先做新增操作,object放入memcache中,后续操作中,再从Memcache中取出update时会报错。而当缓存使用的是Ehcache时,则不会有该问题。
报错关键信息:a different object with the same identifier value was already associated with the session
解决方法直接看本文最下面
猜想:两者缓存中的差别为Memcache需要序列化保存,而Ehcache不需要,顺着这个思路想下去:
新增,object被序列化到Memcached中,后面再取出来更新时,反序列化,id一样,但是hibernate认为这不是同一个obect。
使用ehcache时,用的是Map的方式实现,不需要序列化,所以这种方式手动管理的二级缓存不会有这样子的问题。
验证:这里的猜想完全基于Hibernate对object比较的机制,即报错中说的为什么是“a different object”,于是查看了下Hibernate的源码。
测试代码:
@Test
public void test1() {
session.beginTransaction();
User user = new User();
user.setAge(20);
user.setName("zhang san");
session.save(user);
//put user into memcache
client.set("111", 10000, user);
//get user from memcache
user = (User) client.get("111");
user.setName("li si");
session.update(user);
session.getTransaction().commit();
System.out.println("Over");
}
报错:
org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.closer.user.User#7]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:617)
at
…..
StatefulPersistenceContext.class中的代码抛出了异常,通过查看该代码,发现Hibernate判断两个实体是不是same object主要采用看两者的内存地址是否两等。(object为从memcache反序列化后的实例,entity为通过id从一级缓存中获取的实例)
源码
@Override
public void checkUniqueness(EntityKey key, Object object) throws HibernateException {
final Object entity = getEntity( key );
if ( entity == object ) {
throw new AssertionFailure( "object already associated, but no entry was found" );
}
if ( entity != null ) {
throw new NonUniqueObjectException( key.getIdentifier(), key.getEntityName() );
}
}
而当二级缓存使用的是ehcache是,由于不需要序列的过程,因此二级缓存中的实例跟一级缓存中的实例指向的同一个内存地址。
接下来的问题就是:改由使用hibernate集成的二级缓存管理时,为何就不会有问题。所以要查看下,看Hibernate在保存时有做了什么操作(暂时没有找到hibernate-memcache的源码包,后续再看看)
解决方法:根据上面的结果,其实只要在update前,将entity从一级缓存中擦除即可解决问题
// clear L1 cache
//session.evict(user);
session.clear();
user.setName("li si");
session.update(user);