Hibernate Session缓存

今天有同事遇到了一个Session缓存的问题:

  1. 背景说明
    在现有的架构中,我们对ActionSupport 做了二次封装,所有的Aciton几乎都继承了封装后的CurdActionSupport。CurdActionSupport实现了ModelDriven、Preparable接口,在调用action的 save()方法时如果是更新操作,会做二次绑定。
    此处我们需要实现的功能为在更新或删除时,先比较当次操作的uuid值,如果与现有对象的uuid值不同,需要在归档表中记录一份实体类序列化后的值。

  2. 问题描述
    由于save()有prepare操作,在进入save()后我们可以得到一个实体类,我们暂且命名为 entityFromForm,entityFromForm中的各个属性均来自表单。为了与数据库版本进行比较,需要将数据库中对应的实体取出,此时调用封装好的Hibernate的getById() 会得到 entityFromDB。然后就会发现entityFromForm,entityFromDB各个属性都是一样的,所有属性字段均来自表单。大写懵逼。Talk is cheap,here is the code:

    @Namespace(\"/hibernatesession\")
    public class FwTimeAction extends CrudActionSupport<T> {
        private T entityFromForm;
        private String id;
        //直接将service层逻辑在action中做说明
        public String save() throws Exception {
            System.out.println(entityFromForm.toString()); //原谅这里为了说明问题直接写了syso
            if(!Util.isEmpty(entityFromForm.getId())){   
                T entityFromDB = Tmanger.getById(entityFromForm.getId());
                System.out.println(binder.toJson(entityFromDB));
            }
        }
    }
    

    这样两次打印是一样的。

  3. 问题分析
    save()方法中有prepare操作,实际是在执行save()之前,执行了一步getById().这样save()中的entityFromForm,其实是entityFromDB然后根据表单值 set各个属性。在Hibernate的session中首先从数据库读取了表单中传过来的id值对应的记录,组装成实体类,然后根据表单update了一下对应的属性值。当需要与数据库原始数据做对比时,我们期望Hibernate会再读取一次数据库,所以显式调用了一下getById。但此时hibernate看到你要找的这个我session里面有啊,为了提高效率与一致性直接将entityFromForm返回。所以两次输出一致。

  4. 问题解决
    既然分析得到是因为session中存在相同id对象,那我们把session中该id的对象清空就好了。这让我们想起了前一篇文章.所以改造后的代码大体是这样:

    @Namespace(\"/hibernatesession\")
    public class FwTimeAction extends CrudActionSupport<T> {
        private T entityFromForm;
        private String id;
        //直接将service层逻辑在action中做说明
        public String save() throws Exception {
            System.out.println(entityFromForm.toString()); //原谅这里为了说明问题直接写了syso
            if(!Util.isEmpty(entityFromForm.getId())){
                Tmanager.evict(entity);  
                T entityFromDB = Tmanger.getById(entityFromForm.getId());
                System.out.println(binder.toJson(entityFromDB));
                //TODO 需要补充序列化逻辑
            }
            Tmanager.save(entity);
        }
    }  
    

    这样得到的entityFromDB就真的是读取数据库中内容得到的了。
    不过这样又会存在一个问题,

    Tmanager.save(entity);
    

    会抛出org.hibernate.NonUniqueObjectException异常。出现的原因是在save时,session中又有了两个同id的对象。所以最终代码如下:

    @Namespace(\"/hibernatesession\")
    public class FwTimeAction extends CrudActionSupport<T> {
        private T entityFromForm;
        private String id;
        //直接将service层逻辑在action中做说明
        public String save() throws Exception {
            System.out.println(entityFromForm.toString()); //原谅这里为了说明问题直接写了syso
            T entityFromDB = null;
            if(!Util.isEmpty(entityFromForm.getId())){
                Tmanager.evict(entity);  
                entityFromDB = Tmanger.getById(entityFromForm.getId());
                System.out.println(binder.toJson(entityFromDB));
                //TODO 需要补充序列化逻辑
            }
            if(!Util.isEmpty(entityFromDB){
                Tmanager.evict(entityFromDB);   
            }
            Tmanager.save(entity);
        }
    }  
    

最近的感觉有点像helpdesk,希望自己能不断丰富,谈笑间bug灰飞烟灭的感觉很爽。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.