Spring-Data-JDBC集成ArcadeDB事务场景count()报错问题咨询
问题背景
使用Spring-Data-JDBC仓库搭配ArcadeDB作为持久化存储时,调用简单的count()方法会抛出运行错误,错误日志如下:
2022-07-10 12:54:33.755 DEBUG 14320 --- [ main] o.s.jdbc.support.JdbcTransactionManager : Creating new transaction with name [org.springframework.data.jdbc.repository.support.SimpleJdbcRepository.count]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly 2022-07-10 12:54:33.760 DEBUG 14320 --- [ main] o.s.jdbc.support.JdbcTransactionManager : Acquired Connection [org.postgresql.jdbc.PgConnection@9263c54] for JDBC transaction 2022-07-10 12:54:33.797 DEBUG 14320 --- [ main] o.s.jdbc.datasource.DataSourceUtils : Setting JDBC Connection [org.postgresql.jdbc.PgConnection@9263c54] read-only 2022-07-10 12:54:33.802 DEBUG 14320 --- [ main] o.s.jdbc.support.JdbcTransactionManager : Switching JDBC Connection [org.postgresql.jdbc.PgConnection@9263c54] to manual commit 2022-07-10 12:54:34.802 DEBUG 14320 --- [ main] o.s.jdbc.core.JdbcTemplate : Executing SQL query [SELECT COUNT(*) FROM `Team`] org.springframework.jdbc.BadSqlGrammarException: StatementCallback; bad SQL grammar [SELECT COUNT(*) FROM `Team`]; nested exception is org.postgresql.util.PSQLException: ERROR: Syntax error on parsing query: Encountered " <READ> "READ "" at line 1, column 7. Was expecting one of: <EOF> <ISOLATION> ... ";" ... <ISOLATION> ..
相同SQL查询语句通过原生JdbcTemplate执行时可成功运行,执行代码如下:
jdbcTemplate.execute("select count(*) from `Team`")
初步推测报错原因是Repository调用会在事务上下文中执行SQL,而原生JdbcTemplate调用未开启事务。
咨询问题
- 是否可实现ArcadeDB与Spring事务管理器正常兼容?
- 若无法兼容,是否可以在不启用事务的前提下使用Spring Data Repository?
问题解答
ArcadeDB与Spring事务管理器的兼容性说明
ArcadeDB的PostgreSQL兼容协议目前对JDBC标准事务初始化语句的支持存在缺陷,报错中提到的READ关键字解析失败,本质是Spring的JdbcTransactionManager在开启只读事务时,会先向连接发送SET READ ONLY这类事务设置语句,ArcadeDB的PG协议解析器无法正确识别该语句,未做消费处理,直接将残留的READ关键字拼接到后续执行的COUNT查询前,最终触发SQL语法错误。
可以实现正常兼容,落地方式有两种:
- 自定义
JdbcTransactionManager子类,重写设置连接只读状态的方法,跳过向ArcadeDB连接发送setReadOnly相关指令,避免触发协议层的解析bug - 替换使用ArcadeDB官方提供的原生JDBC驱动,不要走PostgreSQL兼容协议连接,官方驱动对JDBC事务规范的支持完整度远高于PG兼容层
无事务使用Spring Data JDBC Repository的实现方式
不需要强制开启事务就能运行Repository方法,可通过以下方式配置:
- 针对单个查询方法:在自定义Repository接口的对应方法上标注
@Transactional(propagation = Propagation.NOT_SUPPORTED),方法执行时会直接挂起当前存在的事务,以非事务态运行,不需要全局调整事务配置 - 针对全局Repository:自定义
JdbcRepositoryFactoryBean,重写其添加事务顾问的逻辑,去掉对所有Repository方法默认添加@Transactional的行为,也可以直接在配置中排除Spring Data JDBC默认的自动事务切面
注意:非事务态执行写操作会失去原子性保障,只建议对纯查询方法使用非事务配置
内容的提问来源于stack exchange,提问作者Gregor
相关产品推荐
相关产品推荐

