并发导致数据库出现重复数据

蔡佳娃 发布于 2015/05/19 22:32
阅读 1K+
收藏 1

服务:http rest接口

接口中通过 xxxDeal.saveOrUpdate(bean)方法去保存或更新一条数据库记录。

数据层使用的是hibernate JPA注解。

业务实现:

@Service
public class XxxDeal {

   private IXxxDao xxxDao;

   @Transtional(RollbackFor=Exception.class)
   public void saveOrUpdate(Bean bean) {
      Entity entity = xxxDao.findByCode(bean.getCode());
      if (entity) {
          saveMethod(bean);
      } else {
          updateMethod(entity, bean);
      }

      //other business
   }
   
   public void saveMethod(Bean bean) {
       Entity  entity = new Entity();
       //将bean的属性拷贝到entity,然后通过xxxDao.save(entity)保存
   }

   public void updateMethod(Entity entity, Bean bean) {
       //将bean的属性拷贝到entity,然后通过xxxDao.update(entity)更新
   }
}
问题:

当客户端同时发起两个一样的请求时,saveOrUpdate处理就会出问题,向数据库写入两条一样的数据。请教大家这块怎么去做同步,我已经试过了,在方法上直接加synchronized关键字也不行。还是会出现重复数据。

加载中
0
齐纳尔多

唯一性索引不行吗?

蔡佳娃
蔡佳娃
加上的话只能在异常中去处理了正常的业务了
0
simlegate
simlegate
照例说synchronized没问题,是不是你synchronized使用不正确。
牛牛-豆豆
牛牛-豆豆
回复 @蔡佳娃 : 你这个是集群环境,加sync只能是保证单个环境
maradona
maradona
要么是静态方法,要么是单例,而且不能有集群,不然你同步是没有意义的
蔡佳娃
蔡佳娃
用法应该没问题,现在只能怀疑事务的边界也有影响
0
s
snecker
数据库唯一列加unique
0
QAllen
QAllen
集群环境下,还是数据库上加唯一限制吧
0
大汉刺史
大汉刺史
你保存两次就说明两次都是调用的saveMethod,你这个if(entity)应该是 if(entity!=null)吧。说明第二次执行这个方法的时候,entity也是null.说明第一次没有保存进去,应该是session没有提交吧。
0
R-Lu
R-Lu
如果是使用Hibernate,我记得它有这种方法,saveOrUpdate,完全没有必要让你自己来区分吧。
蔡佳娃
蔡佳娃
这些大家的思路。最后我的解决方案是,方法同步,然后设置了事务的隔离级别为未提交读,这样就暂时解决了重复写入的问题。而未提交读导致更新时会出现失败的现象,又在业务中加入了手动事务的处理流程,如在关键地方执行flush方法。这样子表面解决了现在的问题,但是不知道是不是又埋下了隐患
蔡佳娃
蔡佳娃
唉,用的是hibernate,但是注解是基于jpa的
0
甘薯
甘薯
我如果是你 就使用SP
0
麦壳原野
麦壳原野

这个问题的原因应该是事务隔离级别的问题,应该是一个事务在读取完后,紧接着其他的事务执行的插入操作,所以应该用ISOLATION_SERIALIZABLE 隔离级别,防止在事务读取和插入之间有其他事务插入数据。

@Transtional(RollbackFor=Exception.class, propagation=PROPAGATION.REQIRED, isolation=ISOLATION_SERIALIZABLE)
 public void saveOrUpdate(Bean bean) {
      Entity entity = xxxDao.findByCode(bean.getCode());
      if (entity) {
          saveMethod(bean);
      } else {
          updateMethod(entity, bean);
      }
 
      //other business
 }

@Transtional(RollbackFor=Exception.class, propagation=PROPAGATION.REQIRED, readonly=false)
public void Entity findByCode(code){
//...
}

我觉得这样应该就可以了

j
java路
我试过,还是没用啊
返回顶部
顶部