Merge pull request #11466 from thingsboard/fix/versioned-entity

Fix entity's version incremented twice
This commit is contained in:
Viacheslav Klimov 2024-08-22 14:37:15 +03:00 committed by GitHub
commit 0c15791c2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 23 additions and 26 deletions

View File

@ -25,6 +25,7 @@ import jakarta.persistence.Enumerated;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.hibernate.annotations.JdbcType; import org.hibernate.annotations.JdbcType;
import org.hibernate.dialect.PostgreSQLJsonPGObjectJsonbType; import org.hibernate.dialect.PostgreSQLJsonPGObjectJsonbType;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
@ -48,6 +49,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Entity @Entity
@Table(name = ModelConstants.DEVICE_PROFILE_TABLE_NAME) @Table(name = ModelConstants.DEVICE_PROFILE_TABLE_NAME)
@ToString(callSuper = true)
public final class DeviceProfileEntity extends BaseVersionedEntity<DeviceProfile> { public final class DeviceProfileEntity extends BaseVersionedEntity<DeviceProfile> {
@Column(name = ModelConstants.DEVICE_PROFILE_TENANT_ID_PROPERTY) @Column(name = ModelConstants.DEVICE_PROFILE_TENANT_ID_PROPERTY)

View File

@ -58,6 +58,10 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
@Override @Override
@Transactional @Transactional
public D save(TenantId tenantId, D domain) { public D save(TenantId tenantId, D domain) {
return save(tenantId, domain, false);
}
private D save(TenantId tenantId, D domain, boolean flush) {
E entity; E entity;
try { try {
entity = getEntityClass().getConstructor(domain.getClass()).newInstance(domain); entity = getEntityClass().getConstructor(domain.getClass()).newInstance(domain);
@ -73,14 +77,15 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
entity.setCreatedTime(Uuids.unixTimestamp(uuid)); entity.setCreatedTime(Uuids.unixTimestamp(uuid));
} }
try { try {
entity = doSave(entity, isNew); entity = doSave(entity, isNew, flush);
} catch (OptimisticLockException e) { } catch (OptimisticLockException e) {
throw new EntityVersionMismatchException((getEntityType() != null ? getEntityType().getNormalName() : "Entity") + " was already changed by someone else", e); throw new EntityVersionMismatchException((getEntityType() != null ? getEntityType().getNormalName() : "Entity") + " was already changed by someone else", e);
} }
return DaoUtil.getData(entity); 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(); EntityManager entityManager = getEntityManager();
if (isNew) { if (isNew) {
if (entity instanceof HasVersion versionedEntity) { if (entity instanceof HasVersion versionedEntity) {
@ -94,24 +99,32 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
if (existingEntity != null) { if (existingEntity != null) {
versionedEntity.setVersion(existingEntity.getVersion()); // manually resetting the version to latest to allow force overwrite of the entity versionedEntity.setVersion(existingEntity.getVersion()); // manually resetting the version to latest to allow force overwrite of the entity
} else { } else {
return doSave(entity, true); return doSave(entity, true, flush);
} }
} }
entity = entityManager.merge(entity); 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.flush();
entityManager.detach(entity);
flushed = true;
} else { } else {
entity = entityManager.merge(entity); entity = entityManager.merge(entity);
} }
} }
if (flush && !flushed) {
entityManager.flush();
}
return entity; return entity;
} }
@Override @Override
@Transactional @Transactional
public D saveAndFlush(TenantId tenantId, D domain) { public D saveAndFlush(TenantId tenantId, D domain) {
D d = save(tenantId, domain); return save(tenantId, domain, true);
getRepository().flush();
return d;
} }
@Override @Override

View File

@ -22,9 +22,9 @@ import org.thingsboard.server.dao.util.SqlDao;
public abstract class JpaPartitionedAbstractDao<E extends BaseEntity<D>, D> extends JpaAbstractDao<E, D> { public abstract class JpaPartitionedAbstractDao<E extends BaseEntity<D>, D> extends JpaAbstractDao<E, D> {
@Override @Override
protected E doSave(E entity, boolean isNew) { protected E doSave(E entity, boolean isNew, boolean flush) {
createPartition(entity); createPartition(entity);
return super.doSave(entity, isNew); return super.doSave(entity, isNew, flush);
} }
public abstract void createPartition(E entity); public abstract void createPartition(E entity);

View File

@ -19,7 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityInfo;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
@ -58,14 +57,6 @@ public class JpaAssetProfileDao extends JpaAbstractDao<AssetProfileEntity, Asset
return assetProfileRepository.findAssetProfileInfoById(assetProfileId); return assetProfileRepository.findAssetProfileInfoById(assetProfileId);
} }
@Transactional
@Override
public AssetProfile saveAndFlush(TenantId tenantId, AssetProfile assetProfile) {
AssetProfile result = save(tenantId, assetProfile);
assetProfileRepository.flush();
return result;
}
@Override @Override
public PageData<AssetProfile> findAssetProfiles(TenantId tenantId, PageLink pageLink) { public PageData<AssetProfile> findAssetProfiles(TenantId tenantId, PageLink pageLink) {
return DaoUtil.toPageData( return DaoUtil.toPageData(

View File

@ -19,7 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
@ -62,14 +61,6 @@ public class JpaDeviceProfileDao extends JpaAbstractDao<DeviceProfileEntity, Dev
return deviceProfileRepository.findDeviceProfileInfoById(deviceProfileId); return deviceProfileRepository.findDeviceProfileInfoById(deviceProfileId);
} }
@Transactional
@Override
public DeviceProfile saveAndFlush(TenantId tenantId, DeviceProfile deviceProfile) {
DeviceProfile result = save(tenantId, deviceProfile);
deviceProfileRepository.flush();
return result;
}
@Override @Override
public PageData<DeviceProfile> findDeviceProfiles(TenantId tenantId, PageLink pageLink) { public PageData<DeviceProfile> findDeviceProfiles(TenantId tenantId, PageLink pageLink) {
return DaoUtil.toPageData( return DaoUtil.toPageData(