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

背景:使用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的源码。

测试代码:

@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);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值