是否可以在 Spring Data JPA Repository 的 @Query 注释中使用 PostgreSQL 匿名块(PL/pgSQL)?

Posted

技术标签:

【中文标题】是否可以在 Spring Data JPA Repository 的 @Query 注释中使用 PostgreSQL 匿名块(PL/pgSQL)?【英文标题】:Is it possible to use PostgreSQL anonymous block (PL/pgSQL) within the @Query annotation of Spring Data JPA Repository? 【发布时间】:2022-01-19 08:58:47 【问题描述】:

我需要调用一个带参数的匿名块。

@Repository
public interface FinAccountRepository extends JpaRepository<FinAccount, Long> 

    @Modifying
    @Query(nativeQuery = true, value =
            "DO $$ <<credit_account>>\n" +
            "DECLARE\n" +
            "    nNewBalance numeric(19,2);\n" +
            "    nNewId bigint;\n" +
            "BEGIN\n" +
            "    update fin_account set balance = balance + :amount where id = :account\n" +
            "    returning balance into nNewBalance;\n" +
            "    select nextval('seq_fin_account_def_id') into nNewId;\n" +
            "    insert into fin_account_definition(id, account, transaction, balance)\n" +
            "    values (nNewId, :account, :tx, nNewBalance); \n" +
            "END credit_account $$;")
    void credit(@Param("tx") UUID tx, @Param("account") Long account, @Param("amount") BigDecimal amount);


以下代码抛出错误

org.springframework.dao.DataIntegrityViolationException: could not execute native bulk manipulation query; SQL [DO $$
DECLARE
    nNewBalance numeric(19,2);
BEGIN
    update fin_account set balance = balance - :amount where id = :account 
    returning fa.balance into nNewBalance;
    insert into fin_account_definition(id, account, transaction, balance)
    values (nextval('seq_fin_account_def_id'), :account, :tx, nNewBalance);
END $$;]; nested exception is org.hibernate.exception.DataException: could not execute native bulk manipulation query

. . .

Caused by: org.hibernate.exception.DataException: could not execute native bulk manipulation query

. . .

Caused by: org.postgresql.util.PSQLException: The column index is out of range: 1, number of columns: 0.
    at org.postgresql.core.v3.SimpleParameterList.bind(SimpleParameterList.java:69) ~[postgresql-42.2.24.jar:42.2.24]
    at org.postgresql.core.v3.SimpleParameterList.setLiteralParameter(SimpleParameterList.java:128) ~[postgresql-42.2.24.jar:42.2.24]
    at org.postgresql.jdbc.PgPreparedStatement.bindLiteral(PgPreparedStatement.java:1042) ~[postgresql-42.2.24.jar:42.2.24]
    at org.postgresql.jdbc.PgPreparedStatement.setNumber(PgPreparedStatement.java:520) ~[postgresql-42.2.24.jar:42.2.24]
    at org.postgresql.jdbc.PgPreparedStatement.setBigDecimal(PgPreparedStatement.java:337) ~[postgresql-42.2.24.jar:42.2.24]
    at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.setBigDecimal(HikariProxyPreparedStatement.java) ~[HikariCP-4.0.3.jar:na]
    at org.hibernate.type.descriptor.sql.DecimalTypeDescriptor$1.doBind(DecimalTypeDescriptor.java:47) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:73) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:276) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:271) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.loader.custom.sql.NamedParamBinder.bind(NamedParamBinder.java:34) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.engine.query.spi.NativeSQLQueryPlan.performExecuteUpdate(NativeSQLQueryPlan.java:102) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    ... 160 common frames omitted

我也尝试使用序数参数 - 结果是一样的。 但是,没有绑定参数的匿名块被正确调用。 我对从 Spring Data JPA Repository 的方法将参数传递给 postgresql 匿名块的能力感兴趣。

【问题讨论】:

【参考方案1】:

您不需要为此使用 PL/pgSQL。这可以在单个 SQL 语句中完成:

with changed_account as (
   update fin_account 
      set balance = balance + :amount 
   where id = :account
   returning balance, id
)            
insert into fin_account_definition(id, account, transaction, balance)
select nextval('seq_fin_account_def_id'), c.id, :tx, c.balance
from changed_account c;

【讨论】:

以上是关于是否可以在 Spring Data JPA Repository 的 @Query 注释中使用 PostgreSQL 匿名块(PL/pgSQL)?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在 Spring Data JPA Repository 的 @Query 注释中使用 PostgreSQL 匿名块(PL/pgSQL)?

如何在 Spring Data (JPA) 派生查询中按多个属性排序?

是否有适用于 JPA、spring-data、spring-data-rest 的通用 REST 查询语言

即使 JPA 实体不脏,我是不是可以强制 spring-data 更新可审计字段?

处理 JPA 规范和 spring-data-jpa 时如何使用声明 Stream 作为返回类型

Spring-Data-JPA 爬坑记