使用spring data jpa开启高性能批量insert/update

1、jdbc url加上参数

&rewriteBatchedStatements=true

2、yaml/properties加上配置

spring.jpa.properties.hibernate.jdbc.batch_size: 2000
spring.jpa.properties.hibernate.jdbc.batch_versioned_data: true
spring.jpa.properties.hibernate.order_inserts: true
spring.jpa.properties.hibernate.order_updates: true
spring.jpa.properties.hibernate.generate_statistics: false

3、BatchRepository

@Slf4j
@Repository
public class BatchRepository {

    @PersistenceContext
    protected EntityManager entityManager;

    /**
     * Spring Data JPA调用的是Hibernate底层的实现。每次批量保存时,攒够 batchSize 条记录再集中em.flush(),
     *
     * @see org.hibernate.cfg.BatchSettings#STATEMENT_BATCH_SIZE
     */
    @Value("${spring.jpa.properties.hibernate.jdbc.batch_size}")
    private Integer batchSize;

    /**
     * @see org.hibernate.cfg.BatchSettings#BATCH_VERSIONED_DATA
     */
    @Value("${spring.jpa.properties.hibernate.jdbc.batch_versioned_data}")
    private String batchVersionedData;

    /**
     * @see org.hibernate.cfg.BatchSettings#ORDER_INSERTS
     */
    @Value("${spring.jpa.properties.hibernate.order_inserts}")
    private String orderInserts;

    /**
     * @see org.hibernate.cfg.BatchSettings#ORDER_UPDATES
     */
    @Value("${spring.jpa.properties.hibernate.order_updates}")
    private String orderUpdates;

    public EntityManager getEntityManager() {
        return entityManager;
    }

    @PostConstruct
    public void init() {
        log.info("BaseDao初始化加载。batchSize:{},batchVersionedData:{},orderInserts:{},orderUpdates:{}",
                batchSize, batchVersionedData, orderInserts, orderUpdates);
    }

    /**
     * 批量 insert,实现了性能呈倍提升。注意: <br>
     * 1. 需要配置 {@link #batchSize},且jdbc.url开启rewriteBatchedStatements为true <br>
     * 2. 关于rewriteBatchedStatements,可参考MySQL官网解释:{@linkplain 'https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-connp-props-performance-extensions.html#cj-conn-prop_rewriteBatchedStatements'}
     * 3. 主键不能用生成策略,否则会报:{@link org.springframework.dao.InvalidDataAccessApiUsageException}: detached entity passed to persist
     *
     * @param entities 实体对象列表
     * @param <T>      实体类型(必须有@Entity注解)
     * @see #batchSize
     */
    @Transactional(rollbackFor = Exception.class)
    public <T> void batchInsert(List<T> entities) {
        if (entities == null || entities.isEmpty()) {
            return;
        }
        for (T entity : entities) {
            entityManager.persist(entity);
        }
    }

    /**
     * 批量update ( 通过Hibernate进行实现 )
     *
     * @param entities 实体对象列表
     * @param <T>      实体类型(必须有@Entity注解)
     * @see #batchSize
     */
    @Transactional(rollbackFor = Exception.class)
    public <T> void batchUpdate(List<T> entities) {
        if (entities == null || entities.isEmpty()) {
            return;
        }
        Session session = this.entityManager.unwrap(Session.class);
        session.setJdbcBatchSize(batchSize);
        for (T t : entities) {
            session.update(t);
        }
    }
}

4、性能验证
写入1000条
1000条逐条save耗时:55秒
saveAll:22秒
batchInsert(rewriteBatchedStatements=false): 10秒
batchInsert(rewriteBatchedStatements=true且开启第2步里的配置): 0.39秒

可见开启与不开启的性能差距达几十倍以上。

posted @ 2024-03-21 16:04  漠孤烟  阅读(2057)  评论(0编辑  收藏  举报