提交后自动附加记录被分离,jOOQ迭代更新Relation记录时连接关闭异常的解决方案咨询
这个问题的核心原因是你获取记录的时机和事务上下文不匹配,导致记录持有的数据库连接在更新时已经被释放了。让我一步步拆解原因并给出解决方案:
为什么会出现这个异常?
你当前的代码是先执行fetchInto(RELATION)查询获取记录,之后才开启JPA事务。这时候jOOQ执行查询时会从连接池获取一个临时连接,查询完成后就把连接归还到池里了——也就是说,你拿到的RelationRecord对象持有的Configuration(包含连接)已经失效了。当你后续在事务里尝试更新记录时,jOOQ用这个失效的连接执行SQL,自然就抛出"Connection is closed"异常。
而contact.attach(jooq().configuration())能解决问题,是因为它把当前JPA事务绑定的有效Configuration重新关联到了记录上,让更新操作能拿到可用的连接。
如何避免记录被分离?
这里有几个更优雅的解决方案,不需要每次手动attach:
1. 调整代码顺序:在事务开启后执行查询
把查询操作放到JPA事务开启之后,这样jOOQ会自动绑定到JPA的事务连接上,这个连接在事务结束前不会被释放,记录也就不会和有效连接分离:
// 先确保事务处于活跃状态 if (!JPA.em().getTransaction().isActive()) { JPA.em().getTransaction().begin(); } // 事务内执行查询,记录会持有当前事务的有效连接 var contacts = jooq() .selectFrom( RELATION .join(BULK_CONTACT_EDIT_CONTACTS) .on( BULK_CONTACT_EDIT_CONTACTS.CONTACT_ID.eq(RELATION.ID) .and(BULK_CONTACT_EDIT_CONTACTS.BULK_CONTACT_EDIT_ID.eq(bulkContactEditId)) .and(BULK_CONTACT_EDIT_CONTACTS.PROCESSED.isFalse()))) .limit(batchSize) .fetchInto(RELATION); for (RelationRecord contact : contacts) { contact.setOrganizationName("OrganizationName"); contact.update(); JPA.em().getTransaction().commit(); if (!JPA.em().getTransaction().isActive()) { JPA.em().getTransaction().begin(); } }
2. 改用jOOQ原生事务管理
如果业务允许,统一使用jOOQ的事务API来管理整个流程,避免JPA和jOOQ的事务上下文不一致的问题。jOOQ会全程托管连接和事务,确保记录从查询到更新都持有有效连接:
jooq().transaction(config -> { DSLContext ctx = DSL.using(config); var contacts = ctx.selectFrom( RELATION .join(BULK_CONTACT_EDIT_CONTACTS) .on( BULK_CONTACT_EDIT_CONTACTS.CONTACT_ID.eq(RELATION.ID) .and(BULK_CONTACT_EDIT_CONTACTS.BULK_CONTACT_EDIT_ID.eq(bulkContactEditId)) .and(BULK_CONTACT_EDIT_CONTACTS.PROCESSED.isFalse()))) .limit(batchSize) .fetchInto(RELATION); for (RelationRecord contact : contacts) { contact.setOrganizationName("OrganizationName"); contact.update(); } });
3. (备选)批量更新优化
如果你的场景是批量更新相同字段,还可以考虑用jOOQ的批量更新API,不需要逐条处理记录,性能更好也能避免记录分离的问题:
jooq().update(RELATION) .set(RELATION.ORGANIZATION_NAME, "OrganizationName") .where(RELATION.ID.in( jooq().select(BULK_CONTACT_EDIT_CONTACTS.CONTACT_ID) .from(BULK_CONTACT_EDIT_CONTACTS) .where(BULK_CONTACT_EDIT_CONTACTS.BULK_CONTACT_EDIT_ID.eq(bulkContactEditId)) .and(BULK_CONTACT_EDIT_CONTACTS.PROCESSED.isFalse()) .limit(batchSize) )) .execute();
补充说明
你提到常规jOOQ查询没问题,是因为那些查询不需要后续操作记录——查询完成后连接归还池里就结束了,不会涉及到用失效连接执行更新的场景。而UpdatableRecord需要持有有效连接才能执行CRUD操作,所以才会暴露这个问题。
内容的提问来源于stack exchange,提问作者Disheart

