关于for循环插入数据,遇到重复的,手动回滚之前插入的数据

有个问题想问大家,有没有标准的处理方式:比如for循环插入10条数据,但是在插入之前需要先判断这条数据是不是已经存在数据库中。那肯定是在每条数据插入之前先判断有没有数据重复,但是前4条没有问题,但是第五条重复了。这个时候,我肯定要回滚,并且提示前台有重复数据。那么这时候我怎么手动回滚?我现在的处理方式是:先for循环10条数据是不是有重复,如果检验合格,那么再for循环插入,但是我觉得这么写总归不妥。各位有经验的大佬,能不能告知小弟怎么处理?

         int flag = 0;
        //先判断异常
        for(int i = 0;i<jsonArray1.size();i++){
            //判断仓库号是否存在
            JSONObject object = jsonArray1.getJSONObject(i);
            //判断货架是否存在
            String location = object.getString("location");
            if(!StringUtil.isEmpty(location)) {
                Shelf_GongZhuang shelf = shelf_GongZhuang_Dao.findByLocation(location);
                if (shelf == null) {
                    flag = 1;
                    map.put("msg", "不存在货架号" + location);
                    map.put("success", false);
                    break;
                }
            }
        }
        if(flag == 1){
            return map;
        }
        //正常情况进行入库操作
        for(int i = 0;i<jsonArray1.size();i++) {
        //循环插入
        ......
        }

看具体业务场景。普通场景就是先检查是否存在,存在就更新,不存在就插入。需要保持数据一致就加事务一起回滚,如果不需要保持一致,就让抛异常的数据失败算了,try-catch住,后面再补偿。

1赞

不是存在就更新,而是存在就整个不插入。所以没办法抛出异常,那么不异常的情况下怎么手动回滚?我接触到现在的都是异常回滚。我不知道怎么做手动回滚:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();加上这句话好像也不对

数据量大吗,如果不大的话可以将货号存到数据结构里面,例如数组、map或者list里面,增删货号更新数据结构,批量插入的时候从数据结构里面判断是否存在重复。缓存数据的时候不需要存太多字段,存一些关键性的数据就可以。

下面那位老哥说的是对的,既然存在就不插入,就先把存在的数据剔除,剩下的插入。数据库手动回滚我也没操作过,我一般是抛出异常,让spring事务去回滚。

不是这样的,我的需求是:只要存在重复的货号,那么我就提示前台,你把存在的货号改了,然后重新提交。所以,这边只要有重复的货号,我就不插入任何数据。但是,老哥就接着你的思路,你是更改数据结构,但是这种更改不还是需要先for循环检测一遍是否已经存在,如果已经存在就剔除存在的,然后再for循环插入剩余的数据结构。你是这么想的吗?如果不是,老哥能不能详细描述一下?

你可能理解错了,照着你说的那样,如果提交过来这批数据里面如果存在一条数据重复,数据库都不会添加任何数据。我上面说的那个意思是只有操作数据库的时候才会更新缓存。更新数据只是插入数据或者删除数据的时候的后置操作。别的时候只是做查重使用的。这样做要求你项目启动后就得加载更新这个数据结构,将数据库里面的数据先查一遍放到里面去为以后的查重提供基础参照。不过查重算法可能需要你去写。如果数据量大的话,从头遍历到尾这样是不行的。

用异步线程池,future得到的东西用countdownlatch锁住,全部判断完毕,如果有一个是有问题的全局事务回滚 :zzz:

那老哥,我想问你这全局事物回滚怎么写?肯定不是异常情况下回滚,而是正常情况下怎么回滚?

或者我应该这么问:老哥,你知道怎么在正常 的情况下,手动回滚吗?

肯定手动触发呀,或者判断完毕之后直接new一个业务异常,或者通过socket推送到前端都可以,怎么实现方式很多,你只要能控制这几十条数据在线程池处理完后统一判断就可以。况且这种数据也不会很大,浪费的资源也不多

new 一个业务异常?所以说还是手动创建一个异常业务,然后交由Transaction托管,由它完成回滚机制。我们没有办法在正常的业务下手动回滚,是这么理解吗?

可以手动回滚呀 哥哥

 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();  

我让你抛异常或者socker推消息是为了给前段提示,让前段知道数据怎么呢,有啥问题

是的,是这句话。我在代码里面也是加了这句话,但是好像没办法放在正常的业务下:
比如:for(int i = 0;i<10;i++){
//判断是否存在重复数据
if(存在){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
break;
}
else{
//插入数据

}
}
这么写后台会报错

我记得以前用jdbc的时候,有个connection,可以直接connection.rollback();不知道对应的spring有没有类似的方法

好好学学 spring吧

哎 难顶,没话说

使用spring,你不需要自己手动写代码去控制事务。而是要使用spring的声明式事务方法。
给你写个伪代码:

@Transactional   // 声明式事务
public void save (Object[] param ) {
	for (Object o : param) {
		if (...) {
			// 判断是否重复,
			throw new Exception("数据重复,已经存在");
		}
		// 保存
		this.dao.save(o);
	}
}

在spring的声明式事务中,是通过抛出异常来进行事务的回滚。你可以在Controller catch住这个异常,或者使用全局的异常处理器来处理这个异常。