EclipseLink 2.6.4多Merge后提交无UPDATE执行问题排查求助
我们使用EclipseLink 2.6.4结合JTA,配置了eclipselink.persistence-context.flush-mode=commit。在对多个不同对象执行merge操作后提交事务时,发现ODSMaterialLot对象没有触发数据库UPDATE(其他持久化操作如INSERT、其他实体的UPDATE正常),且已确认新值与数据库原有数据不同。
业务逻辑中存在多次事务启停,该事务前后的其他提交均正常。若每次merge后立即提交事务,ODSMaterialLot的UPDATE可正常执行,但这种方式无法保证操作原子性(失败时无法回滚所有动作),因此不可行。
已开启EclipseLink FINEST级别日志,两种场景的日志片段如下:
场景1:多次查询+Merge后一次性提交(无ODSMaterialLot的UPDATE SQL)
[EL Finer]: 2025-01-17 17:15:23.065--UnitOfWork(1790824463)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--begin unit of work commit [EL Finer]: 2025-01-17 17:15:23.065--ClientSession(551861325)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--TX beginTransaction, status=STATUS_ACTIVE [EL Finest]: 2025-01-17 17:15:23.065--UnitOfWork(1790824463)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--Execute query UpdateObjectQuery(movilitas.com.archiving.jpa.entity.archive.ArchivingManagement@4633dca9) [EL Finest]: 2025-01-17 17:15:23.066--UnitOfWork(1790824463)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--Execute query InsertObjectQuery(movilitas.com.archiving.jpa.entity.archive.ArchivingData@1f0b544c) [EL Fine]: 2025-01-17 17:15:23.066--ClientSession(551861325)--Connection(938909931)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--INSERT INTO O95_ARCH_DATA (UIID, ARCH_TYPE, CHANGEDBY, CHANGEDON, CREATEDBY, CREATEDON, HISTORY, OBJECT_ID, OBJECT_JSON, OBJECT_NAME, OBJECT_RAW, OBJECT_XML, STATUS, TENANT, ARCH_MGMT) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) bind => [961, RAW, Guest, 2025-01-17 17:15:23.039, Guest, 2025-01-17 17:15:23.039, WIP->ODS_DIRTY, ELE_MaterialLot_10, null, MaterialLot, [B@34807356, null, ODS_DIRTY, default, 761] [EL Finest]: 2025-01-17 17:15:23.067--UnitOfWork(1790824463)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--Execute query UpdateObjectQuery(movilitas.com.archiving.jpa.entity.archive.material.materialLot.ODSMaterialLot@348b939e) [EL Finest]: 2025-01-17 17:15:23.067--UnitOfWork(1790824463)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--Execute query UpdateObjectQuery(movilitas.com.archiving.jpa.entity.archive.material.materialLot.ODSMaterialLotProperty@6f21d04d) [EL Finest]: 2025-01-17 17:15:23.067--UnitOfWork(1790824463)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--Execute query UpdateObjectQuery(movilitas.com.archiving.jpa.entity.archive.material.materialLot.ODSMaterialLotProperty@29ecc706) [EL Finest]: 2025-01-17 17:15:23.068--ServerSession(101077745)--Connection(981032147)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--Connection released to connection pool [default]. [EL Finer]: 2025-01-17 17:15:23.069--UnitOfWork(1790824463)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--TX afterCompletion callback, status=COMMITTED [EL Finest]: 2025-01-17 17:15:23.075--ServerSession(101077745)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--Propagating command asynchronously [EL Finer]: 2025-01-17 17:15:23.077--UnitOfWork(1790824463)--Thread(Thread[HTTP Worker [@1421579142],5,Dedicated_Application_Thread])--end unit of work commit
场景2:每次Merge后立即提交(正常生成ODSMaterialLot的UPDATE SQL)
[EL Finer]: 2025-01-17 17:39:16.277--UnitOfWork(1013035015)--Thread(Thread[HTTP Worker [@1540943114],5,Dedicated_Application_Thread])--begin unit of work commit [EL Finer]: 2025-01-17 17:39:16.277--ClientSession(1157391672)--Thread(Thread[HTTP Worker [@1540943114],5,Dedicated_Application_Thread])--TX beginTransaction, status=STATUS_ACTIVE [EL Finest]: 2025-01-17 17:39:16.277--UnitOfWork(1013035015)--Thread(Thread[HTTP Worker [@1540943114],5,Dedicated_Application_Thread])--Execute query UpdateObjectQuery(movilitas.com.archiving.jpa.entity.archive.material.materialLot.ODSMaterialLot@339a518b) [EL Finest]: 2025-01-17 17:39:16.277--ServerSession(1735080251)--Connection(662352849)--Thread(Thread[HTTP Worker [@1540943114],5,Dedicated_Application_Thread])--Connection acquired from connection pool [default]. [EL Finest]: 2025-01-17 17:39:16.277--ClientSession(1157391672)--Thread(Thread[HTTP Worker [@1540943114],5,Dedicated_Application_Thread])--reconnecting to external connection pool [EL Fine]: 2025-01-17 17:39:16.277--ClientSession(1157391672)--Connection(1173906560)--Thread(Thread[HTTP Worker [@1540943114],5,Dedicated_Application_Thread])--UPDATE O95_MALT SET CHANGEDON = ?, CUST01 = ?, CUST02 = ?, CUST03 = ?, CUST04 = ?, CUST05 = ?, CUST06 = ? WHERE (UIID = ?) bind => [2025-01-15 09:58:40.623, 0, 1, 2, 3, 4, 5, 39013] [EL Finest]: 2025-01-17 17:39:16.281--ServerSession(1735080251)--Connection(662352849)--Thread(Thread[HTTP Worker [@1540943114],5,Dedicated_Application_Thread])--Connection released to connection pool [default]. [EL Finer]: 2025-01-17 17:39:16.282--UnitOfWork(1013035015)--Thread(Thread[HTTP Worker [@1540943114],5,Dedicated_Application_Thread])--TX afterCompletion callback, status=COMMITTED
两种场景上下文和数据完全一致,已尝试EntityManager.clear()无效,提交前实体字段均为更新后的值,但提交后数据库无更新,新线程查询返回旧值。
根因分析
从日志对比可以看出,场景1中EclipseLink触发了UpdateObjectQuery但未生成对应的SQL UPDATE语句,说明EclipseLink认为ODSMaterialLot实体没有变更需要同步到数据库,但实际业务中实体已被修改。可能的核心原因如下:
实体变更未被正确追踪
- 如果
merge操作后,修改的是原来的detached实体而非merge返回的managed实体,EclipseLink的变更检测机制无法感知到修改(仅managed实体的变更会被追踪)。 - 若
merge后执行了针对ODSMaterialLot的查询(如find),可能从一级缓存/二级缓存加载旧实体,覆盖已修改的managed实体。
- 如果
查询或实体设置了只读属性
- 如果查询
ODSMaterialLot时使用了eclipselink.read-only=true的查询提示,或实体类标注了@ReadOnly,即使执行merge,EclipseLink也不会追踪该实体的变更,提交时不会生成UPDATE。
- 如果查询
EclipseLink 2.6.4版本BUG
- 2.6.4是较老版本,存在多事务、多merge混合场景下的变更检测BUG,可能导致变更丢失。
二级缓存干扰
- 若启用了二级缓存,缓存中的旧实体可能在
merge后被加载,覆盖当前事务中的变更,导致EclipseLink认为实体未修改。
- 若启用了二级缓存,缓存中的旧实体可能在
解决方案
1. 规范Merge操作流程
确保修改的是merge返回的managed实体,而非原始的detached实体:
// 正确方式 ODSMaterialLot detachedLot = ...; // 从外部获取的脱离上下文实体 ODSMaterialLot managedLot = entityManager.merge(detachedLot); managedLot.setCust01(0); // 修改托管实体的属性 managedLot.setCust02(1); // ...其他修改
2. 检查并移除只读设置
- 排查所有查询
ODSMaterialLot的代码,确保没有设置eclipselink.read-only提示; - 检查
ODSMaterialLot实体类,确认没有标注@ReadOnly注解; - 若必须使用只读查询,在需要更新的场景中覆盖该设置:
Query query = entityManager.createQuery("SELECT l FROM ODSMaterialLot l WHERE l.uiid = :uiid"); query.setParameter("uiid", uiid); query.setHint("eclipselink.read-only", false); // 强制关闭只读 ODSMaterialLot lot = (ODSMaterialLot) query.getSingleResult();
3. 禁用/刷新二级缓存
- 临时禁用二级缓存(在persistence.xml中设置
<shared-cache-mode>NONE</shared-cache-mode>),验证是否解决问题; - 若确认是缓存问题,在
merge后手动清除该实体的缓存:entityManager.getEntityManagerFactory().getCache().evict(ODSMaterialLot.class, lotUiid);
4. 升级EclipseLink版本
将EclipseLink升级到2.6.x系列的最新版本(如2.6.10),或直接升级到更高的稳定版本(如2.7.x),修复旧版本的已知BUG。
5. 启用变更检测日志
在persistence.xml中添加以下配置,获取更详细的变更检测日志,定位具体问题:
<property name="eclipselink.logging.level" value="FINE"/> <property name="eclipselink.logging.level.sql" value="FINE"/> <property name="eclipselink.logging.level.transaction" value="FINE"/> <property name="eclipselink.logging.level.cache" value="FINE"/>
内容的提问来源于stack exchange,提问作者EmilienLF

