Quarkus中Jdbi批量查询大ID列表遇Arjuna事务异常求助
解决Quarkus+Jdbi批量IN查询的Arjuna事务异常
异常原因分析
你的代码在循环执行多批次查询时,若外层存在@Transactional注解,所有查询会共享同一个长事务。当批次过多、总查询耗时超过Arjuna事务超时阈值时,事务会被收割器强制终止,导致数据库连接被回收,结果集提前关闭,触发result set is closed错误。若尝试复用同一个Handle执行多次查询,同样会绑定到同一事务上下文,面临相同的超时问题。
解决方案
1. 让每个批次查询使用独立短事务
移除外层事务(或调整事务传播行为),利用Jdbi的withHandle自动管理单次查询的事务生命周期,确保每个批次查询完成后立即提交事务,避免长事务超时。
修正后的代码示例:
List<Map<String, Object>> result = new ArrayList<>(); // DB2的IN子句最多支持1000个参数,可将批次调至1000以减少循环次数 List<List<Integer>> listOfIdLists = chopListToLists(ids, 1000); for (List<Integer> idBatch : listOfIdLists) { // 每次查询独立开启/提交事务,结果集立即加载到内存 List<Map<String, Object>> batchResult = jdbi(db).withHandle(handle -> handle.createQuery("SELECT ... WHERE id IN (<id>)") .bindList("id", idBatch) .mapToMap() .list() // 立即将结果集转为内存列表,避免事务关闭后依赖结果集 ); result.addAll(batchResult); }
2. 调整事务传播行为(若必须保留外层事务)
如果方法外层必须存在事务,将事务传播行为设为REQUIRES_NEW,让每个批次查询在独立的新事务中执行:
import jakarta.transaction.Transactional; @Transactional(Transactional.TxType.REQUIRES_NEW) public List<Map<String, Object>> fetchBatch(List<Integer> idBatch) { return jdbi(db).withHandle(handle -> handle.createQuery("SELECT ... WHERE id IN (<id>)") .bindList("id", idBatch) .mapToMap() .list() ); } // 调用处 List<Map<String, Object>> result = new ArrayList<>(); List<List<Integer>> listOfIdLists = chopListToLists(ids, 1000); for (List<Integer> batch : listOfIdLists) { result.addAll(fetchBatch(batch)); }
3. 临时调整事务超时时间(不推荐)
若无法拆分事务,可临时增大Quarkus事务超时阈值,在application.properties中添加:
quarkus.transaction-manager.default-timeout=60s
此方案仅适用于查询总耗时可控的场景,长期来看仍建议拆分短事务。
关键注意事项
- 确认DB2的IN子句参数上限:DB2默认允许IN子句最多包含1000个参数,批次大小不要超过这个值,否则会触发SQL语法错误。
- 确保结果集立即加载:Jdbi的
list()方法默认会立即将结果集转为内存列表,避免依赖未关闭的结果集;若使用stream()则需注意在事务内处理完流。
内容的提问来源于stack exchange,提问作者jon martin solaas
相关产品推荐
相关产品推荐

