使用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秒
可见开启与不开启的性能差距达几十倍以上。