JPA 批量插入较大数据 解决性能慢问题

使用jpa saveAll接口的话需要了解原理:

	@Transactional
	@Override
	public <S extends T> List<S> saveAll(Iterable<S> entities) {

		Assert.notNull(entities, "Entities must not be null!");

		List<S> result = new ArrayList<>();
		// 使用for循环遍历
		for (S entity : entities) {
			result.add(save(entity));
		}

		return result;
	}

	@Transactional
	@Override
	public <S extends T> S save(S entity) {

		Assert.notNull(entity, "Entity must not be null.");
		// 每条数据都会查询之后 做下判断
		if (entityInformation.isNew(entity)) {
			em.persist(entity);
			return entity;
		} else {
			return em.merge(entity);
		}
	}

	public boolean isNew(T entity) {

		ID id = getId(entity);
		Class<ID> idType = getIdType();

		if (!idType.isPrimitive()) {
		     // 如果id有值,则认为不是新数据,则更新操作,否则就是写入操作
			return id == null;
		}

		if (id instanceof Number) {
			return ((Number) id).longValue() == 0L;
		}

		throw new IllegalArgumentException(String.format("Unsupported primitive id type %s", idType));
	}

以上是jpa源码,所以导致写入数据很慢。因为for遍历一行一行数据写入,而且还要判断;

以下为亲测两种解决方案:

第一种: 自己编写写入逻辑,引入 EntityManager entityManager,代码如下
批量写入一批数据。一次事务提交一批。


    @Value("${spring.jpa.properties.hibernate.jdbc.batch_size:1000}")
    private int batchSize;

    @PersistenceContext
    private EntityManager entityManager;

    public <T> void batchInsert(List<T> list) {
        if (!ObjectUtils.isEmpty(list)){
            for (int i = 1; i <= list.size(); i++) {
                // 写入操作
                entityManager.persist(list.get(i - 1));
                if (i % batchSize == 0) {
                    entityManager.flush();
                    entityManager.clear();
                }
            }
            if (list.size() % batchSize != 0) {
               //flush() 同步持久上下文环境,即将持久上下文环境的所有未保存实体的状态信息保存到数据库中。
                entityManager.flush();
               //clear() 清除持久上下文环境,断开所有关联的实体。如果这时还有未提交的更新则会被撤消。
                entityManager.clear();
            }
        }
    }

    public <T> void batchUpdate(List<T> list) {
        if (!ObjectUtils.isEmpty(list)){
            for (int i = 1; i < list.size(); i++) {
                entityManager.merge(list.get(i - 1));
                if (i % batchSize == 0) {
                    entityManager.flush();
                    entityManager.clear();
                }
            }
            if (list.size() % batchSize != 0) {
                entityManager.flush();
                entityManager.clear();
            }
        }
    }

第二种:不需要自己编写逻辑,使用jpa saveAll()方法
开启JPA批处理
在这里插入图片描述

jpa 表映射@Table 下对主键使用序列,postgre支持创建序列,可以使用,其他数据源不一定。

    @GeneratedValue(strategy = SEQUENCE, generator = "seqGen")
    @SequenceGenerator(name = "seqGen", sequenceName = "seq", initialValue = 1)

这样做的逻辑saveAll()不需要判断isNew,直接走em.persist(entity);

两种的性能差不多,记录下

更多推荐