Spring Data JDBC @Modifying with PostgreSQL RETURNING для возврата данных из измененных строк

PostgreSQL предоставляет удобный способ возврата данных из операторов DML, см. https://www.postgresql.org/docs/current/dml-returning.html

То, чего я пытался добиться, выглядит примерно так:

@Modifying
@Query("DELETE FROM Book b WHERE b.title = :title RETURNING *")
Book deleteReturning(@Param("title") String title);

чтобы получить удаленную строку. Однако это приводит к следующему исключению

org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [DELETE FROM Book b WHERE b.title = ? RETURNING *]; A result was returned when none was expected.; nested exception is org.postgresql.util.PSQLException: A result was returned when none was expected.
    at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:104)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:862)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:883)
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:321)
    at org.springframework.data.jdbc.repository.query.AbstractJdbcQuery.lambda$createModifyingQueryExecutor$0(AbstractJdbcQuery.java:103)
    at org.springframework.data.jdbc.repository.query.StringBasedJdbcQuery.execute(StringBasedJdbcQuery.java:85)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor$QueryMethodInvoker.invoke(QueryExecutorMethodInterceptor.java:195)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:152)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:130)
    ...
Caused by: org.postgresql.util.PSQLException: A result was returned when none was expected.
    at org.postgresql.jdbc.PgStatement.checkNoResultUpdate(PgStatement.java:269)
    at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:131)
    at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
    at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
    at org.springframework.jdbc.core.JdbcTemplate.lambda$update$0(JdbcTemplate.java:867)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:617)
    ... 95 more

Spring Data JDBC заявляет, что модифицирующий запрос может возвращать только void, int и boolean, см. https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#jdbc.query-methods.at-query.modifying

Добавление пользовательского RowMapper приводит к такому же исключению.

    public class BookRowMapper<T> implements RowMapper<Book> {
        @Override
        public Book mapRow(final ResultSet rs, final int rowNum) throws SQLException {
            return new Book(rs.getString(1), ...);
        }
    }

    @Modifying
    @Query(value = "delete from Book b where b.title = :title returning *", rowMapperClass = BookRowMapper.class)
    Book deleteReturning(@Param("title") String title);

Есть ли возможность добиться этого?

Изменить: я знаю простой JDBC https://stackoverflow.com/a/40787385/1239904, но я хотел бы иди Весенним путем.


person Mahatma_Fatal_Error    schedule 04.11.2020    source источник


Ответы (1)


Когда вы аннотируете метод с помощью @Modifying, он выполняется как оператор DML, который не возвращает никакого значения, кроме количества обновленных строк.

Если ваш оператор возвращает ResultSet, вы не должны использовать @Modifying, поэтому JDBC Spring Data пытается извлечь ResultSet и создать из него возвращаемые значения для метода.

person Jens Schauder    schedule 05.11.2020
comment
Спасибо. Работает так, как вы описали. Однако действительно ли отсутствие @Modifying не приводит к негативным побочным эффектам? - person Mahatma_Fatal_Error; 05.11.2020
comment
@Modifying изменяет метод, вызываемый драйвером JDBC, и способ обработки результатов. Это все. - person Jens Schauder; 05.11.2020