Browse Source

Increment entity's version regardless of the changes

pull/11496/head
ViacheslavKlimov 2 years ago
parent
commit
6b00fb4900
  1. 3
      application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
  2. 12
      application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java
  3. 4
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  4. 19
      dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java
  5. 44
      dao/src/test/java/org/thingsboard/server/dao/sql/device/JpaDeviceDaoTest.java

3
application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java

@ -1503,11 +1503,12 @@ public class DeviceControllerTest extends AbstractControllerTest {
Assert.assertTrue(deviceBulkImportResult.getErrorsList().isEmpty()); Assert.assertTrue(deviceBulkImportResult.getErrorsList().isEmpty());
Device updatedDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class); Device updatedDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class);
savedDevice.setVersion(updatedDevice.getVersion());
Assert.assertEquals(savedDevice, updatedDevice); Assert.assertEquals(savedDevice, updatedDevice);
DeviceCredentials updatedCredentials = DeviceCredentials updatedCredentials =
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
savedCredentials.setVersion(updatedCredentials.getVersion());
Assert.assertEquals(savedCredentials, updatedCredentials); Assert.assertEquals(savedCredentials, updatedCredentials);
} }

12
application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java

@ -360,7 +360,7 @@ public class RuleChainControllerTest extends AbstractControllerTest {
@Test @Test
public void testSaveRuleChainWithOutdatedVersion() throws Exception { public void testSaveRuleChainWithOutdatedVersion() throws Exception {
RuleChain ruleChain = createRuleChain("Rule chain with invalid nodes"); RuleChain ruleChain = createRuleChain("My rule chain");
RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); RuleChainMetaData ruleChainMetaData = new RuleChainMetaData();
ruleChainMetaData.setRuleChainId(ruleChain.getId()); ruleChainMetaData.setRuleChainId(ruleChain.getId());
@ -392,11 +392,11 @@ public class RuleChainControllerTest extends AbstractControllerTest {
.andExpect(status().isConflict()); .andExpect(status().isConflict());
ruleChainMetaData.setVersion(3L); ruleChainMetaData.setVersion(3L);
doPost("/api/ruleChain/metadata", ruleChainMetaData) ruleChainMetaData = doPost("/api/ruleChain/metadata", ruleChainMetaData, RuleChainMetaData.class);
.andExpect(status().isOk()); assertThat(ruleChainMetaData.getVersion()).isEqualTo(4);
ruleChain.setVersion(3L); ruleChain.setVersion(4L);
doPost("/api/ruleChain", ruleChain) ruleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
.andExpect(status().isOk()); assertThat(ruleChain.getVersion()).isEqualTo(5);
} }
private RuleChain createRuleChain(String name) { private RuleChain createRuleChain(String name) {

4
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -168,7 +168,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
return saveRuleChainMetaData(tenantId, ruleChainMetaData, ruleNodeUpdater, true); return saveRuleChainMetaData(tenantId, ruleChainMetaData, ruleNodeUpdater, true);
} }
@Transactional
@Override @Override
public RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData, Function<RuleNode, RuleNode> ruleNodeUpdater, boolean publishSaveEvent) { public RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData, Function<RuleNode, RuleNode> ruleNodeUpdater, boolean publishSaveEvent) {
Validator.validateId(ruleChainMetaData.getRuleChainId(), "Incorrect rule chain id."); Validator.validateId(ruleChainMetaData.getRuleChainId(), "Incorrect rule chain id.");
@ -237,7 +237,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
if ((ruleChain.getFirstRuleNodeId() != null && !ruleChain.getFirstRuleNodeId().equals(firstRuleNodeId)) if ((ruleChain.getFirstRuleNodeId() != null && !ruleChain.getFirstRuleNodeId().equals(firstRuleNodeId))
|| (ruleChain.getFirstRuleNodeId() == null && firstRuleNodeId != null)) { || (ruleChain.getFirstRuleNodeId() == null && firstRuleNodeId != null)) {
ruleChain.setFirstRuleNodeId(firstRuleNodeId); ruleChain.setFirstRuleNodeId(firstRuleNodeId);
ruleChainDao.save(tenantId, ruleChain);
} }
if (ruleChainMetaData.getConnections() != null) { if (ruleChainMetaData.getConnections() != null) {
for (NodeConnectionInfo nodeConnection : ruleChainMetaData.getConnections()) { for (NodeConnectionInfo nodeConnection : ruleChainMetaData.getConnections()) {
@ -286,6 +285,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
if (!relations.isEmpty()) { if (!relations.isEmpty()) {
relationService.saveRelations(tenantId, relations); relationService.saveRelations(tenantId, relations);
} }
ruleChain = ruleChainDao.save(tenantId, ruleChain);
if (publishSaveEvent) { if (publishSaveEvent) {
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(ruleChain).entityId(ruleChain.getId()).build()); eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(ruleChain).entityId(ruleChain.getId()).build());
} }

19
dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java

@ -97,20 +97,28 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
if (versionedEntity.getVersion() == null) { if (versionedEntity.getVersion() == null) {
HasVersion existingEntity = entityManager.find(versionedEntity.getClass(), entity.getUuid()); HasVersion existingEntity = entityManager.find(versionedEntity.getClass(), entity.getUuid());
if (existingEntity != null) { if (existingEntity != null) {
versionedEntity.setVersion(existingEntity.getVersion()); // manually resetting the version to latest to allow force overwrite of the entity /*
* manually resetting the version to latest to allow force overwrite of the entity
* */
versionedEntity.setVersion(existingEntity.getVersion());
} else { } else {
return doSave(entity, true, flush); return doSave(entity, true, flush);
} }
} }
entity = entityManager.merge(entity); versionedEntity = entityManager.merge(versionedEntity);
/*
* by default, Hibernate doesn't issue an update query and thus version increment
* if the entity was not modified. to bypass this and always increment the version, we do it manually
* */
versionedEntity.setVersion(versionedEntity.getVersion() + 1);
/* /*
* flushing so that the query is executed right away and the version is incremented, * flushing and then removing the entity from the persistence context so that it is not affected
* 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 * by next flushes (e.g. when a transaction is committed) to avoid double version increment
* */ * */
entityManager.flush(); entityManager.flush();
entityManager.detach(entity); entityManager.detach(versionedEntity);
flushed = true; flushed = true;
entity = (E) versionedEntity;
} else { } else {
entity = entityManager.merge(entity); entity = entityManager.merge(entity);
} }
@ -161,6 +169,7 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
log.debug("Remove request: {}", id); log.debug("Remove request: {}", id);
} }
@Override
@Transactional @Transactional
public void removeAllByIds(Collection<UUID> ids) { public void removeAllByIds(Collection<UUID> ids) {
JpaRepository<E, UUID> repository = getRepository(); JpaRepository<E, UUID> repository = getRepository();

44
dao/src/test/java/org/thingsboard/server/dao/sql/device/JpaDeviceDaoTest.java

@ -45,6 +45,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -105,7 +106,37 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest {
@Test @Test
public void testSaveDeviceName0x00_thenSomeDatabaseException() { public void testSaveDeviceName0x00_thenSomeDatabaseException() {
Device device = getDevice(tenantId1, customerId1, "\u0000"); Device device = getDevice(tenantId1, customerId1, "\u0000");
assertThatThrownBy(() -> deviceIds.add(deviceDao.save(TenantId.fromUUID(tenantId1), device).getUuidId())); assertThatThrownBy(() -> deviceIds.add(saveDevice(tenantId1, device).getUuidId()));
}
@Test
public void testSaveDevice_versionIncrement() {
Device device = getDevice(tenantId1, customerId1, "1ewfewf2");
device = saveDevice(tenantId1, device);
deviceIds.add(device.getUuidId());
assertThat(device.getVersion()).isEqualTo(1);
device.setName(device.getName() + "x");
device = saveDevice(tenantId1, device);
assertThat(device.getVersion()).isEqualTo(2);
device.setName(device.getName() + "x");
device = saveDevice(tenantId1, device);
assertThat(device.getVersion()).isEqualTo(3);
}
@Test
public void testSaveDevice_versionIncrement_noChanges() {
Device device = getDevice(tenantId1, customerId1, "1ewfewf2");
device = saveDevice(tenantId1, device);
deviceIds.add(device.getUuidId());
assertThat(device.getVersion()).isEqualTo(1);
device = saveDevice(tenantId1, device);
assertThat(device.getVersion()).isEqualTo(2);
device = saveDevice(tenantId1, device);
assertThat(device.getVersion()).isEqualTo(3);
} }
@Test @Test
@ -126,7 +157,7 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest {
UUID customerId = Uuids.timeBased(); UUID customerId = Uuids.timeBased();
// send to method getDevice() number = 40, because make random name is bad and name "SEARCH_TEXT_40" don't used // send to method getDevice() number = 40, because make random name is bad and name "SEARCH_TEXT_40" don't used
Device device = getDevice(tenantId, customerId, 40); Device device = getDevice(tenantId, customerId, 40);
deviceIds.add(deviceDao.save(TenantId.fromUUID(tenantId), device).getUuidId()); deviceIds.add(saveDevice(tenantId, device).getUuidId());
UUID uuid = device.getId().getId(); UUID uuid = device.getId().getId();
Device entity = deviceDao.findById(TenantId.fromUUID(tenantId), uuid); Device entity = deviceDao.findById(TenantId.fromUUID(tenantId), uuid);
@ -156,8 +187,8 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest {
private List<UUID> createDevices(UUID tenantId1, UUID tenantId2, UUID customerId1, UUID customerId2, int count) { private List<UUID> createDevices(UUID tenantId1, UUID tenantId2, UUID customerId1, UUID customerId2, int count) {
List<UUID> savedDevicesUUID = new ArrayList<>(); List<UUID> savedDevicesUUID = new ArrayList<>();
for (int i = 0; i < count / 2; i++) { for (int i = 0; i < count / 2; i++) {
savedDevicesUUID.add(deviceDao.save(TenantId.fromUUID(tenantId1), getDevice(tenantId1, customerId1, i)).getUuidId()); savedDevicesUUID.add(saveDevice(tenantId1, getDevice(tenantId1, customerId1, i)).getUuidId());
savedDevicesUUID.add(deviceDao.save(TenantId.fromUUID(tenantId2), getDevice(tenantId2, customerId2, i + count / 2)).getUuidId()); savedDevicesUUID.add(saveDevice(tenantId2, getDevice(tenantId2, customerId2, i + count / 2)).getUuidId());
} }
return savedDevicesUUID; return savedDevicesUUID;
} }
@ -175,4 +206,9 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest {
device.setDeviceProfileId(savedDeviceProfile.getId()); device.setDeviceProfileId(savedDeviceProfile.getId());
return device; return device;
} }
private Device saveDevice(UUID tenantId, Device device) {
return deviceDao.save(TenantId.fromUUID(tenantId), device);
}
} }

Loading…
Cancel
Save