有经验可以少走点弯路,所以走过的坑要记住,虽然我们不能两次踏入同一条河流,但我们可能多次掉入同一个坑!
1、最近项目做一个关于批量修改或者增加记录的功能,为了方便,就手动写一个saveOrUpdate()函数,你可能会说这个hibernate不是自带吗?我首先说一下项目的情况:
从前台传来的多条记录,是没有主键的,(因为主键自动增长,且前台可以新增或修改多条数据,所以编辑前查询也有没有显示自动增长主键,当然这也有一个解决方案:编辑之前查询数据结果显示在前台包含自动增长主键,新增数据记录就不包含主键,这样后台根据有没有主键来操作是save还是update,不想改所以今天不是这种情况)
所以今天的目的就是自己写一个saveOrUpdate()函数,可以处理save或者update,本来想的流程是:
1)、从前台获取对象,然后根据一个条件(其实这个条件也可以是主键且保证唯一)查询数据库是否有这条记录;
2)、如果没有则用hibernate自带save()函数完成插入操作,否则把前台对象A的主键设置为从数据库获得的A1,然后使用hibernate自带函数Update()进行更新,想到这里我觉得一且很顺利,But。
3)、实际操作起来,并不是这样的,save()没什么问题,毕竟数据库没有,session缓存也没有;但是Update就会报错:org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:[com.fatkun.dao.hibernate.User#12]
反正就是说,当前session对象(我用的是getCurrentSession(),而不是create..),session缓存里面已经有一个主键一样的对象(标识),系统认为是同一个(我这里猜测应该是根据主键来判断是否是同一个对象),所以用当前session查询出A1,然后设置前台传来对象A,然后Update(A),就会报错的,当前session缓存里就有A1,这个时候换成A,就会冲突了。
啰嗦了这么多,来说解决方案:
《1》使用Hibernate自带的merge()函数,如果前台对象包含主键,直接使用,比saveOrUpdate更好用。
《2》可以把session缓存对象去掉 ,然后使用hibernate自带update():

@Override
    public void forceUpdate(T o,T oo) {
        // 这里必须两个对象,试图用一个用临时变量保存然后脱管再update不行
        //我这里o是session缓存中已经包含的,需要脱管,然后用oo来更新数据库。
        if(this.getCurrentSession().contains(o)){
            this.getCurrentSession().evict(o);
            this.getCurrentSession().update(oo);
        }else{
            this.getCurrentSession().update(o);
        }

    }
《3》 用另一种方法,就是把对象A的属性值覆盖A1(可以主键不覆盖),然后update(A1).
BeanUtils.copyProperties(A1,A); 即将A的值赋值给A1。
《4》最简单方便的方式就是查询结果传给前台包含自动增长主键,这样新增的就主键为空,用save(),修改的包含主键,则直接update(),不查询,因为编辑是默认有主键的,有主键代表默认数据库有记录,不用查,当然适合不删除数据的部分。