From def602902a55b39fdfb6904824abd1f46960224d Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 3 Apr 2026 16:32:02 +0300 Subject: [PATCH] Detach RuleChainDetailsEntity after findById to prevent stale version conflict RuleChainEntity and RuleChainDetailsEntity map to the same DB table. When RuleChainDetailsEntity is loaded into the persistence context (e.g. during isUpdateNeeded metadata comparison) and then the same row is updated via RuleChainEntity (during saveRuleChain), the cached RuleChainDetailsEntity becomes stale. Subsequent save via RuleChainDetailsEntity fails with OptimisticLockException. Fix: detach the entity immediately after loading in findById so it does not linger in the persistence context. --- .../server/dao/sql/rule/JpaRuleChainDetailsDao.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDetailsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDetailsDao.java index d43ee02add..d3ec79c175 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDetailsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDetailsDao.java @@ -23,12 +23,14 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChainDetails; +import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.RuleChainDetailsEntity; import org.thingsboard.server.dao.rule.RuleChainDetailsDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import org.thingsboard.server.exception.DataValidationException; +import java.util.Optional; import java.util.UUID; @Slf4j @@ -39,6 +41,16 @@ public class JpaRuleChainDetailsDao extends JpaAbstractDao entity = getRepository().findById(key); + // Detaching to avoid stale version conflict with RuleChainEntity which maps to the same table. + // Without detach, a loaded RuleChainDetailsEntity stays in the persistence context and becomes stale + // when the same row is updated via RuleChainEntity (e.g. during rule chain import with circular references). + entity.ifPresent(e -> getEntityManager().detach(e)); + return DaoUtil.getData(entity); + } + @Override public RuleChainDetails save(TenantId tenantId, RuleChainDetails ruleChainDetails) { try {