Spring:使用存储库中的查询注释批量本机查询?

Bra*_*rks 5 java spring-boot spring-repositories

我必须一次更新插入数千条记录,分成几批,比如说一次 25 个实际更新插入。upsert 基于 service+key 的唯一约束,如果插入失败,则会更新值。

请注意,我对 UPSERT 情况感兴趣,而不仅仅是标准插入,因为我想避免额外的数据库读取操作,并且这些数据大部分是静态的。

我将 Spring 与 Postgres 14 结合使用,并认为我应该执行类似以下操作来在 Repository 类中执行更新插入,但希望确保如果可能的话,更新插入是批量完成的。

我已经设置了以下 Spring Boot 属性,但没有看到任何表明正在发生批处理的日志消息:

spring.jpa.properties.hibernate.jdbc.batch_size = 25
spring.jpa.properties.hibernate.order_inserts = true
Run Code Online (Sandbox Code Playgroud)

我可以使用这样的方法来做到这一点,还是我必须用另一种方式来做到这一点,就像可能的那样

spring.jpa.properties.hibernate.jdbc.batch_size = 25
spring.jpa.properties.hibernate.order_inserts = true
Run Code Online (Sandbox Code Playgroud)

编辑:我的目标是只批量执行数据库插入或更新(每批单个事务),如果值没有更改,则不执行任何操作。因此,对于尚未插入任何值的新“服务”,我将一次插入大约 5000 个值,我想确保这是分组完成的,以避免 Postgres 过载并导致大量事务日志。

编辑2:请注意,如果您使用@DataJpaTest 来测试您的upsert,H2 数据库将会失败,因为它不支持`INSERT ON CONFLICT(unique) UPDATE。更多详情请点击此处

Ken*_*han 3

大多数其他答案都提到了GenerationType.IDENTITY与休眠批处理属性相关的内容,例如等,但是只有当您通过其脏检查机制或的脏jdbc.batch_size检查机制以休眠方式批量更新记录时,这些事情才重要。如果您使用本机 SQL 进行更新,则与这些设置无关。EntityManagerpersist()merge()

从根本上来说,批量执行本机 SQL 需要使用 JDBC 2.0 Batch API,但没有简单的方法可以通过 hibernate API 访问这些 API。因此,我建议使用JdbcTemplate其中的batchUpdate()方法,该方法将委托给 JDBC 2.0 Batch APIPreparedStatement#addBatch()来完成实际工作。

鉴于您已经拥有本机 upsert SQL,实际上通过JdbcTemplate.

首先,定义一条记录my_table

public record MyTable(String service, String key, String value, int version){}
Run Code Online (Sandbox Code Playgroud)

然后实现一个服务来更新它们:

@Service
public class MyTableService {

    @Autowired
    NamedParameterJdbcTemplate jt;

    @Transactional
    public void upsert(List<MyTable> rows) {

        String sql = "INSERT INTO my_table "
                + "  (service, key, value, version) "
                + "VALUES "
                + "  (:service, :key, :value, :version) "
                + "ON CONFLICT ON CONSTRAINT allow_one_value_per_service_key DO "
                + "  UPDATE "
                + "    SET "
                + "      value = excluded.value, version=my_table.version + 1 "
                + "    WHERE  "
                + "      my_table.value <> excluded.value";

        MapSqlParameterSource[] params = rows.stream().map(r -> {
            MapSqlParameterSource paramValues = new MapSqlParameterSource();
            paramValues.addValue("service", r.service());
            paramValues.addValue("key", r.key());
            paramValues.addValue("value", r.value());
            paramValues.addValue("version", r.version());
            return paramValues;
        }).toArray(MapSqlParameterSource[]::new);

        jt.batchUpdate(sql, params);

    }
}

Run Code Online (Sandbox Code Playgroud)

使用它:

public record MyTable(String service, String key, String value, int version){}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

1311 次

最近记录:

2 年,4 月 前