如何使用Spring Data Jpa实现批量更新?
How to implement batch update using Spring Data Jpa?
如何使用SpringData Jpa实现批量更新?
我有一个商品实体,对于不同的用户级别,有不同的价格,例如
goodsId level price
1 1 10
1 2 9
1 3 8
更新商品时我想批量更新这些价格,如下:
@Query(value = "update GoodsPrice set price = :price where goodsId=:goodsId and level=:level")
void batchUpdate(List<GoodsPrice> goodsPriceList);
但它抛出异常,
Caused by: java.lang.IllegalArgumentException: Name for parameter binding must not be null or empty! For named parameters you need to use @Param for query method parameters on Java versions < 8.
那么如何正确使用Spring数据Jpa实现批量更新呢?
根据 docs. You have to look at plain JDBC, there are a few methods regarding batch insert/updates.
,我认为使用 Spring Data JPA 是不可能的
如果您使用的是 Hibernate,并且愿意自己管理事务,则可以选择。
这里是测试例子
int entityCount = 50;
int batchSize = 25;
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = entityManagerFactory().createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
for ( int i = 0; i < entityCount; ++i ) {
if ( i > 0 && i % batchSize == 0 ) {
entityManager.flush();
entityManager.clear();
transaction.commit();
transaction.begin();
}
Post post = new Post(String.format( "Post %d", i + 1 ) );
entityManager.persist( post );
}
transaction.commit();
} catch (RuntimeException e) {
if ( transaction != null && transaction.isActive()) {
transaction.rollback();
}
throw e;
} finally {
if (entityManager != null) {
entityManager.close();
}
}
还建议将以下属性设置为适合您需要的内容。
<property
name="hibernate.jdbc.batch_size"
value="25"
/>
<property
name="hibernate.order_inserts"
value="true"
/>
<property
name="hibernate.order_updates"
value="true"
/>
所有这些都来自以下文章。
The best way to do batch processing with JPA and Hibernate
按照这个例子:Spring Data JPA Batch Inserts,我创建了自己的更新方式,无需处理 EntityManager。
我的做法是首先检索所有我想要更新的数据,在您的情况下,它将是 WHERE goodsId=:goodsId AND level=:level
。然后我使用 for 循环遍历整个列表并设置我想要的数据
List<GoodsPrice> goodsPriceList = goodsRepository.findAllByGoodsIdAndLevel();
for(GoodsPrice goods : goodsPriceList) {
goods.setPrice({{price}});
}
goodsRepository.saveAll(goodsPriceList);
插入或更新需要以下一些内容。打开 generate_statistics 以便您可以查看是否真的在批处理它
// for logging purpose, to make sure it is working
spring.jpa.properties.hibernate.generate_statistics=true
// Essentially key to turn it on
spring.jpa.properties.hibernate.jdbc.batch_size=4
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
日志在这里
27315 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
603684 nanoseconds spent preparing 4 JDBC statements;
3268688 nanoseconds spent executing 3 JDBC statements;
4028317 nanoseconds spent executing 2 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
6392912 nanoseconds spent executing 1 flushes (flushing a total of 3 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
再 link 添加到此对话以供更多参考
Spring Data JPA batch insert/update
尝试使用 Spring Data JPA 进行批量插入/更新时需要考虑的几件事
- 用于@Id创建实体的GenerationType
- 设置 Hibernate 的 Batch size 属性 属性
- 设置order_inserts(使Batch_size对Insert语句生效)或order_updates(使Batch_size对Update语句生效)属性
- 设置batch_versioned_data以使order_updates对Batch_size实施有效
我们可以以编程方式启用查询批处理。如果需要处理数千条记录的批量更新,那么我们可以使用以下代码来实现相同的目的。
您可以定义自己的批处理大小并调用 updateEntityUtil() 方法来使用 Spring Data JPA 或本机查询来简单触发更新查询。
代码片段:-
// Total number of records to be updated
int totalSize = updateEntityDetails.size();
// Total number of batches to be executed for update-query batch processing
int batches = totalSize / batchSize;
// Calculate last batch size
int lastBatchSize = totalSize % batchSize;
// Batching-process indexes
int batchStartIndex = 0, batchEndIndex = batchSize - 1;
// Count of modified records in database
int modifiedRecordsCount = 0;
while (batches-- > 0) {
// Call updateEntityUtil to update values in database
modifiedRecordsCount += updateEntityUtil(batchStartIndex, batchEndIndex, regionCbsaDetails);
// Update batch start and end indexes
batchStartIndex = batchEndIndex + 1;
batchEndIndex = batchEndIndex + batchSize;
}
// Execute last batch
if (lastBatchSize > 0)
modifiedRecordsCount += updateEntityUtil(totalSize - lastBatchSize, totalSize - 1,
updateEntityDetails);
如何使用SpringData Jpa实现批量更新? 我有一个商品实体,对于不同的用户级别,有不同的价格,例如
goodsId level price
1 1 10
1 2 9
1 3 8
更新商品时我想批量更新这些价格,如下:
@Query(value = "update GoodsPrice set price = :price where goodsId=:goodsId and level=:level")
void batchUpdate(List<GoodsPrice> goodsPriceList);
但它抛出异常,
Caused by: java.lang.IllegalArgumentException: Name for parameter binding must not be null or empty! For named parameters you need to use @Param for query method parameters on Java versions < 8.
那么如何正确使用Spring数据Jpa实现批量更新呢?
根据 docs. You have to look at plain JDBC, there are a few methods regarding batch insert/updates.
,我认为使用 Spring Data JPA 是不可能的如果您使用的是 Hibernate,并且愿意自己管理事务,则可以选择。
这里是测试例子
int entityCount = 50;
int batchSize = 25;
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = entityManagerFactory().createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
for ( int i = 0; i < entityCount; ++i ) {
if ( i > 0 && i % batchSize == 0 ) {
entityManager.flush();
entityManager.clear();
transaction.commit();
transaction.begin();
}
Post post = new Post(String.format( "Post %d", i + 1 ) );
entityManager.persist( post );
}
transaction.commit();
} catch (RuntimeException e) {
if ( transaction != null && transaction.isActive()) {
transaction.rollback();
}
throw e;
} finally {
if (entityManager != null) {
entityManager.close();
}
}
还建议将以下属性设置为适合您需要的内容。
<property
name="hibernate.jdbc.batch_size"
value="25"
/>
<property
name="hibernate.order_inserts"
value="true"
/>
<property
name="hibernate.order_updates"
value="true"
/>
所有这些都来自以下文章。 The best way to do batch processing with JPA and Hibernate
按照这个例子:Spring Data JPA Batch Inserts,我创建了自己的更新方式,无需处理 EntityManager。
我的做法是首先检索所有我想要更新的数据,在您的情况下,它将是 WHERE goodsId=:goodsId AND level=:level
。然后我使用 for 循环遍历整个列表并设置我想要的数据
List<GoodsPrice> goodsPriceList = goodsRepository.findAllByGoodsIdAndLevel();
for(GoodsPrice goods : goodsPriceList) {
goods.setPrice({{price}});
}
goodsRepository.saveAll(goodsPriceList);
插入或更新需要以下一些内容。打开 generate_statistics 以便您可以查看是否真的在批处理它
// for logging purpose, to make sure it is working
spring.jpa.properties.hibernate.generate_statistics=true
// Essentially key to turn it on
spring.jpa.properties.hibernate.jdbc.batch_size=4
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
日志在这里
27315 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
603684 nanoseconds spent preparing 4 JDBC statements;
3268688 nanoseconds spent executing 3 JDBC statements;
4028317 nanoseconds spent executing 2 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
6392912 nanoseconds spent executing 1 flushes (flushing a total of 3 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
再 link 添加到此对话以供更多参考
Spring Data JPA batch insert/update
尝试使用 Spring Data JPA 进行批量插入/更新时需要考虑的几件事
- 用于@Id创建实体的GenerationType
- 设置 Hibernate 的 Batch size 属性 属性
- 设置order_inserts(使Batch_size对Insert语句生效)或order_updates(使Batch_size对Update语句生效)属性
- 设置batch_versioned_data以使order_updates对Batch_size实施有效
我们可以以编程方式启用查询批处理。如果需要处理数千条记录的批量更新,那么我们可以使用以下代码来实现相同的目的。
您可以定义自己的批处理大小并调用 updateEntityUtil() 方法来使用 Spring Data JPA 或本机查询来简单触发更新查询。
代码片段:-
// Total number of records to be updated
int totalSize = updateEntityDetails.size();
// Total number of batches to be executed for update-query batch processing
int batches = totalSize / batchSize;
// Calculate last batch size
int lastBatchSize = totalSize % batchSize;
// Batching-process indexes
int batchStartIndex = 0, batchEndIndex = batchSize - 1;
// Count of modified records in database
int modifiedRecordsCount = 0;
while (batches-- > 0) {
// Call updateEntityUtil to update values in database
modifiedRecordsCount += updateEntityUtil(batchStartIndex, batchEndIndex, regionCbsaDetails);
// Update batch start and end indexes
batchStartIndex = batchEndIndex + 1;
batchEndIndex = batchEndIndex + batchSize;
}
// Execute last batch
if (lastBatchSize > 0)
modifiedRecordsCount += updateEntityUtil(totalSize - lastBatchSize, totalSize - 1,
updateEntityDetails);