【记录一次性 操作百万数据 从数据库拷贝修改再插入报错问题】

记录一次性 操作百万数据 从数据库拷贝修改再插入报错问题

问题一: identifier of an instance of was altered from 1353655 to null

 由于在同一方法上加入@Transactional(rollbackFor = Exception.class)事务管理导致 实体类在修改主键会报错,
 其实我们只有在保存数据的时候才会用到事务(当然这里从数据库查询认为不会报错);
 所以从这方面出发,我们可以把事务管理只加载 保存 的地方。
 这里提供一种方法(手动抛出异常: TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()):
		try {
			demodao.batchInsert(demoList);
		} catch (Exception e) {

			e.printStackTrace();
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		}

当然如果你数据量小,你可以直接创建一个新的对象,然后把属性赋值给新对象、如BeanUtils.copyProperties等方法。但是如果你数据量大,如果你直接BeanUtils.copyProperties可能会报错:javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session :,这里可以看问题二

问题二:javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session :

hibernate是不允许两个主键相同的对象在同一个session里面的,所以最根本的解决办法就是,当你需要修改这个对象的时候只要修改A的相应的值然后save保存就可以了,没必要再创建B再去保存B.

这里数据量小没有问题,你也可以把数据分段再进行操作,我这里的实体id使用的是自增,另外在使用jpa操作大量数据的时候,修改和插入操作是循环调用save方法插入,大量数据量,效率会有点低。所以我们可以自己实现批量插入,在你的dao层创建BaseRepository接口,并实现。以后其余的接口继承改接口就可以了
如下:

@NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {

  /**
   * 批量增加
   * @param entities 批量增加实体
   * @param <S> 实体
   */
  <S extends T> Iterable<S> batchInsert(Iterable<S> entities);


  /**
   * 批量更新
   * @param entities 批量更新的实体
   * @param <S> 实体
   */
  <S extends T> Iterable<S> batchUpdate(Iterable<S> entities);
}

实现:

@SuppressWarnings("SpringJavaConstructorAutowiringInspection")
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> {

    /**
     * batch大小
     */
    private static final int BATCH_SIZE = 500;

    /**
     * EntityManager
     */
    private EntityManager entityManager;


    public BaseRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager em) {
        super(entityInformation, em);
        this.entityManager = em;
    }

    public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
        this.entityManager = em;
    }



    @Override
    @Transactional(rollbackFor = Exception.class)
    public <S extends T> Iterable<S> batchInsert(Iterable<S> entities) {
        Iterator<S> iterator = entities.iterator();
        int index = 0;
        while (iterator.hasNext()){
            entityManager.persist(iterator.next());
            index++;
            if (index % BATCH_SIZE == 0){
                entityManager.flush();
                entityManager.clear();
            }
        }
        if (index % BATCH_SIZE != 0){
            entityManager.flush();
            entityManager.clear();
        }
        return entities;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public <S extends T> Iterable<S> batchUpdate(Iterable<S> entities) {
        Iterator<S> iterator = entities.iterator();
        int index = 0;
        while (iterator.hasNext()){
            entityManager.merge(iterator.next());
            index++;
            if (index % BATCH_SIZE == 0){
                entityManager.flush();
                entityManager.clear();
            }
        }
        if (index % BATCH_SIZE != 0){
            entityManager.flush();
            entityManager.clear();
        }
        return entities;
    }
}

dao层 : public interface XXXDao extends BaseRepository<XXX,Long> 这样,最后记得在启动类上扫描这个实现类,不然会报找不到实现接口的错误

@EnableJpaRepositories(repositoryBaseClass = BaseRepositoryImpl.class)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值