一个Memcache+Hibernate自处理二级缓存问题

来源:互联网 发布:java 调试 class文件 编辑:程序博客网 时间:2024/06/05 08:36

背景:使用Memcache+Hibernate时,在同一个session中先做新增操作,object放入memcache中,后续操作中,再从Memcache中取出update时会报错。而当缓存使用的是Ehcache时,则不会有该问题。
报错关键信息:a different object with the same identifier value was already associated with the session
解决方法直接看本文最下面
猜想:两者缓存中的差别为Memcache需要序列化保存,而Ehcache不需要,顺着这个思路想下去:

  1. 新增,object被序列化到Memcached中,后面再取出来更新时,反序列化,id一样,但是hibernate认为这不是同一个obect。

  2. 使用ehcache时,用的是Map的方式实现,不需要序列化,所以这种方式手动管理的二级缓存不会有这样子的问题。

验证:这里的猜想完全基于Hibernate对object比较的机制,即报错中说的为什么是“a different object”,于是查看了下Hibernate的源码。

测试代码:

@Testpublic 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从一级缓存中获取的实例)

源码

@Overridepublic 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);
0 0
原创粉丝点击