From f0186ca996993cf282571ab86f907067bc03abf9 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 22 Aug 2024 12:31:52 +0300 Subject: [PATCH] Fix entity update query executed twice (resulting in double version increment) --- .../dao/model/sql/DeviceProfileEntity.java | 2 ++ .../server/dao/sql/JpaAbstractDao.java | 25 ++++++++++++++----- .../dao/sql/JpaPartitionedAbstractDao.java | 4 +-- .../dao/sql/asset/JpaAssetProfileDao.java | 9 ------- .../dao/sql/device/JpaDeviceProfileDao.java | 9 ------- 5 files changed, 23 insertions(+), 26 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java index c5e6bc4968..7b3bbe2db2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java @@ -25,6 +25,7 @@ import jakarta.persistence.Enumerated; import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.ToString; import org.hibernate.annotations.JdbcType; import org.hibernate.dialect.PostgreSQLJsonPGObjectJsonbType; import org.thingsboard.common.util.JacksonUtil; @@ -48,6 +49,7 @@ import java.util.UUID; @EqualsAndHashCode(callSuper = true) @Entity @Table(name = ModelConstants.DEVICE_PROFILE_TABLE_NAME) +@ToString(callSuper = true) public final class DeviceProfileEntity extends BaseVersionedEntity { @Column(name = ModelConstants.DEVICE_PROFILE_TENANT_ID_PROPERTY) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index 8002730686..801898386e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -58,6 +58,10 @@ public abstract class JpaAbstractDao, D> @Override @Transactional public D save(TenantId tenantId, D domain) { + return save(tenantId, domain, false); + } + + private D save(TenantId tenantId, D domain, boolean flush) { E entity; try { entity = getEntityClass().getConstructor(domain.getClass()).newInstance(domain); @@ -73,14 +77,15 @@ public abstract class JpaAbstractDao, D> entity.setCreatedTime(Uuids.unixTimestamp(uuid)); } try { - entity = doSave(entity, isNew); + entity = doSave(entity, isNew, flush); } catch (OptimisticLockException e) { throw new EntityVersionMismatchException((getEntityType() != null ? getEntityType().getNormalName() : "Entity") + " was already changed by someone else", e); } return DaoUtil.getData(entity); } - protected E doSave(E entity, boolean isNew) { + protected E doSave(E entity, boolean isNew, boolean flush) { + boolean flushed = false; EntityManager entityManager = getEntityManager(); if (isNew) { if (entity instanceof HasVersion versionedEntity) { @@ -94,24 +99,32 @@ public abstract class JpaAbstractDao, D> if (existingEntity != null) { versionedEntity.setVersion(existingEntity.getVersion()); // manually resetting the version to latest to allow force overwrite of the entity } else { - return doSave(entity, true); + return doSave(entity, true, flush); } } entity = entityManager.merge(entity); + /* + * flushing so that the query is executed right away and the version is incremented, + * then removing the entity from the persistence context so that it is not affected + * by next flushes (e.g. when a transaction is committed) to avoid double version increment + * */ entityManager.flush(); + entityManager.detach(entity); + flushed = true; } else { entity = entityManager.merge(entity); } } + if (flush && !flushed) { + entityManager.flush(); + } return entity; } @Override @Transactional public D saveAndFlush(TenantId tenantId, D domain) { - D d = save(tenantId, domain); - getRepository().flush(); - return d; + return save(tenantId, domain, true); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java index 6afb8b4ede..a74f32d8c4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java @@ -22,9 +22,9 @@ import org.thingsboard.server.dao.util.SqlDao; public abstract class JpaPartitionedAbstractDao, D> extends JpaAbstractDao { @Override - protected E doSave(E entity, boolean isNew) { + protected E doSave(E entity, boolean isNew, boolean flush) { createPartition(entity); - return super.doSave(entity, isNew); + return super.doSave(entity, isNew, flush); } public abstract void createPartition(E entity); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java index d6025fc07b..6ed74a5aa9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java @@ -19,7 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.AssetProfile; @@ -58,14 +57,6 @@ public class JpaAssetProfileDao extends JpaAbstractDao findAssetProfiles(TenantId tenantId, PageLink pageLink) { return DaoUtil.toPageData( diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index 0f597756eb..4e1594eeff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -19,7 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; @@ -62,14 +61,6 @@ public class JpaDeviceProfileDao extends JpaAbstractDao findDeviceProfiles(TenantId tenantId, PageLink pageLink) { return DaoUtil.toPageData(