如何使用Spring Data Jpa实现批量更新?

12

如何使用Spring Data Jpa实现批量更新?我有一个Goods实体,对于不同的用户级别,价格是不同的,例如:

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 Data Jpa实现批量更新?


在开始考虑批量更新之前,您需要提供命名参数。这些参数是在方法签名中以分号为前缀的参数,您的方法签名应如下所示:void batchUpdate(@Param("price") Double price, @Param("goodsId ") Long goodsid, @Param("level") Long level); - Robert Niestroj
但如果是这样,它只能一次更新一个。 - zhuguowei
1
@RobertNiestroj "冒号前缀" - Eagle_Eye
5个回答

9

谢谢,对于这种情况直接使用JDBC非常方便,但我不确定Spring Data JPA和JDBC Template是否可以共存。 - zhuguowei
他们可以。我们使用它就像这样。我们有普通的Spring Data JPA存储库和使用Spring JDBCTemplate的自定义DAO。 - Robert Niestroj
你能否知道一些在Github上结合Spring Data Jpa和Jdbc Template的代码示例吗? - zhuguowei

7

在遵循Spring Data JPA批量插入的例子后,我创建了自己的方法来更新数据而不必处理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)

如果您的实体已在事务中进行管理,则无需调用 saveAll - Akash Jain

1
如果你正在使用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"
/>

以下内容摘自以下文章。 使用JPA和Hibernate进行批处理的最佳方法


1

为了更多参考,这次对话还需要添加一个链接

Spring Data JPA 批量插入/更新

在使用 Spring Data JPA 进行批量插入/更新时需要考虑以下几点:

  1. 实体的 @Id 创建使用的 GenerationType
  2. 设置 Hibernate 属性的 Batch size 属性
  3. 设置 order_inserts(使 Batch_size 对插入语句有效)或 order_updates(使 Batch_size 对更新语句有效)属性的 Hibernate 属性
  4. 设置 batch_versioned_data 以使 order_updates 对 Batch_size 实现生效

0
我们可以通过编程方式启用查询批处理。如果需要处理数千条记录的大量更新,则可以使用以下代码来实现相同的功能。
您可以定义自己的批处理大小,并调用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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接