From def602902a55b39fdfb6904824abd1f46960224d Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 3 Apr 2026 16:32:02 +0300 Subject: [PATCH 1/2] 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 { From bbf47434fdc5d931881f369b3d2624ec6b1e89bc Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 3 Apr 2026 17:42:17 +0300 Subject: [PATCH 2/2] Simplify RuleChainDetailsDao interface and refactor save to doSave --- .../server/dao/rule/RuleChainDetailsDao.java | 10 ++-------- .../server/dao/sql/rule/JpaRuleChainDetailsDao.java | 4 ++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDetailsDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDetailsDao.java index 17e90d84bd..f7ab9d8662 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDetailsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDetailsDao.java @@ -15,15 +15,9 @@ */ package org.thingsboard.server.dao.rule; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChainDetails; +import org.thingsboard.server.dao.Dao; -import java.util.UUID; - -public interface RuleChainDetailsDao { - - RuleChainDetails findById(TenantId tenantId, UUID id); - - RuleChainDetails save(TenantId tenantId, RuleChainDetails ruleChainDetails); +public interface RuleChainDetailsDao extends Dao { } 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 d3ec79c175..3381bae386 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 @@ -52,9 +52,9 @@ public class JpaRuleChainDetailsDao extends JpaAbstractDao