Put to cache after save for versioned entities

This commit is contained in:
ViacheslavKlimov 2024-07-16 10:06:34 +03:00
parent 4302b63fd9
commit d0546ae83c
29 changed files with 224 additions and 144 deletions

View File

@ -54,6 +54,11 @@ public abstract class CaffeineTbTransactionalCache<K extends Serializable, V ext
return SimpleTbCacheValueWrapper.wrap(cache.get(key));
}
@Override
public TbCacheValueWrapper<V> get(K key, boolean transactionMode) {
return get(key);
}
@Override
public void put(K key, V value) {
lock.lock();

View File

@ -31,7 +31,7 @@ public class RedisTbCacheTransaction<K extends Serializable, V extends Serializa
@Override
public void put(K key, V value) {
cache.put(key, value, connection);
cache.put(key, value, connection, true);
}
@Override

View File

@ -77,9 +77,14 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
@Override
public TbCacheValueWrapper<V> get(K key) {
return get(key, false);
}
@Override
public TbCacheValueWrapper<V> get(K key, boolean transactionMode) {
try (var connection = connectionFactory.getConnection()) {
byte[] rawKey = getRawKey(key);
byte[] rawValue = doGet(connection, rawKey);
byte[] rawValue = doGet(connection, rawKey, transactionMode);
if (rawValue == null || rawValue.length == 0) {
return null;
} else if (Arrays.equals(rawValue, BINARY_NULL_VALUE)) {
@ -96,18 +101,18 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
}
}
protected byte[] doGet(RedisConnection connection, byte[] rawKey) {
protected byte[] doGet(RedisConnection connection, byte[] rawKey, boolean transactionMode) {
return connection.stringCommands().get(rawKey);
}
@Override
public void put(K key, V value) {
try (var connection = connectionFactory.getConnection()) {
put(key, value, connection);
put(key, value, connection, false);
}
}
public void put(K key, V value, RedisConnection connection) {
public void put(K key, V value, RedisConnection connection, boolean transactionMode) {
put(connection, key, value, RedisStringCommands.SetOption.UPSERT);
}

View File

@ -27,6 +27,8 @@ public interface TbTransactionalCache<K extends Serializable, V extends Serializ
TbCacheValueWrapper<V> get(K key);
TbCacheValueWrapper<V> get(K key, boolean transactionMode);
void put(K key, V value);
void putIfAbsent(K key, V value);
@ -60,7 +62,7 @@ public interface TbTransactionalCache<K extends Serializable, V extends Serializ
}
default V getAndPutInTransaction(K key, Supplier<V> dbCall, boolean cacheNullValue) {
TbCacheValueWrapper<V> cacheValueWrapper = get(key);
TbCacheValueWrapper<V> cacheValueWrapper = get(key, true);
if (cacheValueWrapper != null) {
return cacheValueWrapper.get();
}
@ -95,7 +97,7 @@ public interface TbTransactionalCache<K extends Serializable, V extends Serializ
}
default <R> R getAndPutInTransaction(K key, Supplier<R> dbCall, Function<V, R> cacheValueToResult, Function<R, V> dbValueToCacheValue, boolean cacheNullValue) {
TbCacheValueWrapper<V> cacheValueWrapper = get(key);
TbCacheValueWrapper<V> cacheValueWrapper = get(key, true);
if (cacheValueWrapper != null) {
var cacheValue = cacheValueWrapper.get();
return cacheValue == null ? null : cacheValueToResult.apply(cacheValue);

View File

@ -88,7 +88,10 @@ public abstract class VersionedRedisTbCache<K extends Serializable, V extends Se
}
@Override
protected byte[] doGet(RedisConnection connection, byte[] rawKey) {
protected byte[] doGet(RedisConnection connection, byte[] rawKey, boolean transactionMode) {
if (transactionMode) {
return super.doGet(connection, rawKey, true);
}
return connection.stringCommands().getRange(rawKey, VERSION_SIZE, VALUE_END_OFFSET);
}
@ -104,7 +107,11 @@ public abstract class VersionedRedisTbCache<K extends Serializable, V extends Se
}
@Override
public void put(K key, V value, RedisConnection connection) {
public void put(K key, V value, RedisConnection connection, boolean transactionMode) {
if (transactionMode) {
super.put(key, value, connection, true); // because scripting commands are not supported in transaction mode
return;
}
Long version = value != null ? value.getVersion() : 0;
byte[] rawKey = getRawKey(key);
doPut(rawKey, value, version, cacheTtl, connection);

View File

@ -27,11 +27,17 @@ public interface VersionedTbCache<K extends Serializable, V extends Serializable
TbCacheValueWrapper<V> get(K key);
default V get(K key, Supplier<V> supplier) {
return get(key, supplier, true);
}
default V get(K key, Supplier<V> supplier, boolean putToCache) {
return Optional.ofNullable(get(key))
.map(TbCacheValueWrapper::get)
.orElseGet(() -> {
V value = supplier.get();
put(key, value);
if (putToCache) {
put(key, value);
}
return value;
});
}

View File

@ -16,6 +16,7 @@
package org.thingsboard.server.cache.device;
import lombok.Data;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
@ -26,5 +27,6 @@ public class DeviceCacheEvictEvent {
private final DeviceId deviceId;
private final String newName;
private final String oldName;
private Device savedDevice;
}

View File

@ -18,13 +18,13 @@ package org.thingsboard.server.cache.device;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.cache.VersionedCaffeineTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.Device;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("DeviceCache")
public class DeviceCaffeineCache extends CaffeineTbTransactionalCache<DeviceCacheKey, Device> {
public class DeviceCaffeineCache extends VersionedCaffeineTbCache<DeviceCacheKey, Device> {
public DeviceCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.DEVICE_CACHE);

View File

@ -21,9 +21,9 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.RedisTbTransactionalCache;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbRedisSerializer;
import org.thingsboard.server.cache.VersionedRedisTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.util.ProtoUtils;
@ -31,7 +31,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("DeviceCache")
public class DeviceRedisCache extends RedisTbTransactionalCache<DeviceCacheKey, Device> {
public class DeviceRedisCache extends VersionedRedisTbCache<DeviceCacheKey, Device> {
public DeviceRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.DEVICE_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbRedisSerializer<>() {

View File

@ -38,15 +38,15 @@ public class AssetProfileCacheKey implements Serializable {
this.defaultProfile = defaultProfile;
}
public static AssetProfileCacheKey fromName(TenantId tenantId, String name) {
public static AssetProfileCacheKey forName(TenantId tenantId, String name) {
return new AssetProfileCacheKey(tenantId, name, null, false);
}
public static AssetProfileCacheKey fromId(AssetProfileId id) {
public static AssetProfileCacheKey forId(AssetProfileId id) {
return new AssetProfileCacheKey(null, null, id, false);
}
public static AssetProfileCacheKey defaultProfile(TenantId tenantId) {
public static AssetProfileCacheKey forDefaultProfile(TenantId tenantId) {
return new AssetProfileCacheKey(tenantId, null, null, true);
}

View File

@ -18,13 +18,13 @@ package org.thingsboard.server.dao.asset;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.cache.VersionedCaffeineTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.asset.AssetProfile;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("AssetProfileCache")
public class AssetProfileCaffeineCache extends CaffeineTbTransactionalCache<AssetProfileCacheKey, AssetProfile> {
public class AssetProfileCaffeineCache extends VersionedCaffeineTbCache<AssetProfileCacheKey, AssetProfile> {
public AssetProfileCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.ASSET_PROFILE_CACHE);

View File

@ -15,11 +15,16 @@
*/
package org.thingsboard.server.dao.asset;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.TenantId;
@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class AssetProfileEvictEvent {
private final TenantId tenantId;
@ -27,5 +32,6 @@ public class AssetProfileEvictEvent {
private final String oldName;
private final AssetProfileId assetProfileId;
private final boolean defaultProfile;
private AssetProfile savedAssetProfile;
}

View File

@ -19,17 +19,18 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.RedisTbTransactionalCache;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbJsonRedisSerializer;
import org.thingsboard.server.cache.VersionedRedisTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.asset.AssetProfile;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("AssetProfileCache")
public class AssetProfileRedisCache extends RedisTbTransactionalCache<AssetProfileCacheKey, AssetProfile> {
public class AssetProfileRedisCache extends VersionedRedisTbCache<AssetProfileCacheKey, AssetProfile> {
public AssetProfileRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.ASSET_PROFILE_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbJsonRedisSerializer<>(AssetProfile.class));
}
}

View File

@ -33,7 +33,7 @@ import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.entity.CachedVersionedEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -53,7 +53,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
@Service("AssetProfileDaoService")
@Slf4j
public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetProfileCacheKey, AssetProfile, AssetProfileEvictEvent> implements AssetProfileService {
public class AssetProfileServiceImpl extends CachedVersionedEntityService<AssetProfileCacheKey, AssetProfile, AssetProfileEvictEvent> implements AssetProfileService {
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
@ -81,18 +81,20 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@TransactionalEventListener(classes = AssetProfileEvictEvent.class)
@Override
public void handleEvictEvent(AssetProfileEvictEvent event) {
List<AssetProfileCacheKey> keys = new ArrayList<>(2);
keys.add(AssetProfileCacheKey.fromName(event.getTenantId(), event.getNewName()));
if (event.getAssetProfileId() != null) {
keys.add(AssetProfileCacheKey.fromId(event.getAssetProfileId()));
List<AssetProfileCacheKey> toEvict = new ArrayList<>(2);
toEvict.add(AssetProfileCacheKey.forName(event.getTenantId(), event.getNewName()));
if (event.getSavedAssetProfile() != null) {
cache.put(AssetProfileCacheKey.forId(event.getSavedAssetProfile().getId()), event.getSavedAssetProfile());
} else if (event.getAssetProfileId() != null) {
toEvict.add(AssetProfileCacheKey.forId(event.getAssetProfileId()));
}
if (event.isDefaultProfile()) {
keys.add(AssetProfileCacheKey.defaultProfile(event.getTenantId()));
toEvict.add(AssetProfileCacheKey.forDefaultProfile(event.getTenantId()));
}
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
keys.add(AssetProfileCacheKey.fromName(event.getTenantId(), event.getOldName()));
toEvict.add(AssetProfileCacheKey.forName(event.getTenantId(), event.getOldName()));
}
cache.evict(keys);
cache.evict(toEvict);
}
@Override
@ -104,8 +106,8 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
public AssetProfile findAssetProfileById(TenantId tenantId, AssetProfileId assetProfileId, boolean putInCache) {
log.trace("Executing findAssetProfileById [{}]", assetProfileId);
Validator.validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id);
return cache.getOrFetchFromDB(AssetProfileCacheKey.fromId(assetProfileId),
() -> assetProfileDao.findById(tenantId, assetProfileId.getId()), true, putInCache);
return cache.get(AssetProfileCacheKey.forId(assetProfileId),
() -> assetProfileDao.findById(tenantId, assetProfileId.getId()), putInCache);
}
@Override
@ -117,7 +119,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
public AssetProfile findAssetProfileByName(TenantId tenantId, String profileName, boolean putInCache) {
log.trace("Executing findAssetProfileByName [{}][{}]", tenantId, profileName);
Validator.validateString(profileName, s -> INCORRECT_ASSET_PROFILE_NAME + s);
return cache.getOrFetchFromDB(AssetProfileCacheKey.fromName(tenantId, profileName),
return cache.getOrFetchFromDB(AssetProfileCacheKey.forName(tenantId, profileName),
() -> assetProfileDao.findByName(tenantId, profileName), false, putInCache);
}
@ -147,7 +149,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
imageService.replaceBase64WithImageUrl(assetProfile, "asset profile");
savedAssetProfile = assetProfileDao.saveAndFlush(assetProfile.getTenantId(), assetProfile);
publishEvictEvent(new AssetProfileEvictEvent(savedAssetProfile.getTenantId(), savedAssetProfile.getName(),
oldAssetProfile != null ? oldAssetProfile.getName() : null, savedAssetProfile.getId(), savedAssetProfile.isDefault()));
oldAssetProfile != null ? oldAssetProfile.getName() : null, savedAssetProfile.getId(), savedAssetProfile.isDefault(), savedAssetProfile));
if (publishSaveEvent) {
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedAssetProfile.getTenantId()).entity(savedAssetProfile)
.entityId(savedAssetProfile.getId()).created(oldAssetProfile == null).build());
@ -267,7 +269,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
public AssetProfile findDefaultAssetProfile(TenantId tenantId) {
log.trace("Executing findDefaultAssetProfile tenantId [{}]", tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return cache.getAndPutInTransaction(AssetProfileCacheKey.defaultProfile(tenantId),
return cache.getAndPutInTransaction(AssetProfileCacheKey.forDefaultProfile(tenantId),
() -> assetProfileDao.findDefaultAssetProfile(tenantId), true);
}
@ -353,4 +355,5 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
return profile == null ? null : new AssetProfileInfo(profile.getId(), profile.getTenantId(), profile.getName(), profile.getImage(),
profile.getDefaultDashboardId());
}
}

View File

@ -16,11 +16,11 @@
package org.thingsboard.server.dao.device;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.util.SecurityUtil;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.common.util.JacksonUtil;
@ -45,20 +45,18 @@ import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.validator.DeviceCredentialsDataValidator;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateString;
@Service
@Slf4j
@RequiredArgsConstructor
public class DeviceCredentialsServiceImpl extends AbstractCachedEntityService<String, DeviceCredentials, DeviceCredentialsEvictEvent> implements DeviceCredentialsService {
@Autowired
private DeviceCredentialsDao deviceCredentialsDao;
@Autowired
private DataValidator<DeviceCredentials> credentialsValidator;
private final DeviceCredentialsDao deviceCredentialsDao;
private final DeviceCredentialsDataValidator credentialsValidator;
@TransactionalEventListener(classes = DeviceCredentialsEvictEvent.class)
@Override

View File

@ -41,19 +41,19 @@ public class DeviceProfileCacheKey implements Serializable {
this.provisionDeviceKey = provisionDeviceKey;
}
public static DeviceProfileCacheKey fromName(TenantId tenantId, String name) {
public static DeviceProfileCacheKey forName(TenantId tenantId, String name) {
return new DeviceProfileCacheKey(tenantId, name, null, false, null);
}
public static DeviceProfileCacheKey fromId(DeviceProfileId id) {
public static DeviceProfileCacheKey forId(DeviceProfileId id) {
return new DeviceProfileCacheKey(null, null, id, false, null);
}
public static DeviceProfileCacheKey defaultProfile(TenantId tenantId) {
public static DeviceProfileCacheKey forDefaultProfile(TenantId tenantId) {
return new DeviceProfileCacheKey(tenantId, null, null, true, null);
}
public static DeviceProfileCacheKey fromProvisionDeviceKey(String provisionDeviceKey) {
public static DeviceProfileCacheKey forProvisionKey(String provisionDeviceKey) {
return new DeviceProfileCacheKey(null, null, null, false, provisionDeviceKey);
}

View File

@ -18,13 +18,13 @@ package org.thingsboard.server.dao.device;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.cache.VersionedCaffeineTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.DeviceProfile;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("DeviceProfileCache")
public class DeviceProfileCaffeineCache extends CaffeineTbTransactionalCache<DeviceProfileCacheKey, DeviceProfile> {
public class DeviceProfileCaffeineCache extends VersionedCaffeineTbCache<DeviceProfileCacheKey, DeviceProfile> {
public DeviceProfileCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.DEVICE_PROFILE_CACHE);

View File

@ -15,11 +15,16 @@
*/
package org.thingsboard.server.dao.device;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class DeviceProfileEvictEvent {
private final TenantId tenantId;
@ -28,5 +33,6 @@ public class DeviceProfileEvictEvent {
private final DeviceProfileId deviceProfileId;
private final boolean defaultProfile;
private final String provisionDeviceKey;
private DeviceProfile savedDeviceProfile;
}

View File

@ -21,9 +21,9 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.RedisTbTransactionalCache;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbRedisSerializer;
import org.thingsboard.server.cache.VersionedRedisTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.util.ProtoUtils;
@ -31,7 +31,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("DeviceProfileCache")
public class DeviceProfileRedisCache extends RedisTbTransactionalCache<DeviceProfileCacheKey, DeviceProfile> {
public class DeviceProfileRedisCache extends VersionedRedisTbCache<DeviceProfileCacheKey, DeviceProfile> {
public DeviceProfileRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.DEVICE_PROFILE_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbRedisSerializer<DeviceProfileCacheKey, DeviceProfile>() {
@ -50,4 +50,5 @@ public class DeviceProfileRedisCache extends RedisTbTransactionalCache<DevicePro
}
});
}
}

View File

@ -15,8 +15,8 @@
*/
package org.thingsboard.server.dao.device;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -42,14 +42,14 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.entity.CachedVersionedEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.resource.ImageService;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.service.validator.DeviceProfileDataValidator;
import java.io.ByteArrayInputStream;
import java.security.cert.Certificate;
@ -69,7 +69,8 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
@Service("DeviceProfileDaoService")
@Slf4j
public class DeviceProfileServiceImpl extends AbstractCachedEntityService<DeviceProfileCacheKey, DeviceProfile, DeviceProfileEvictEvent> implements DeviceProfileService {
@RequiredArgsConstructor
public class DeviceProfileServiceImpl extends CachedVersionedEntityService<DeviceProfileCacheKey, DeviceProfile, DeviceProfileEvictEvent> implements DeviceProfileService {
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
private static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId ";
@ -87,7 +88,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
private DeviceService deviceService;
@Autowired
private DataValidator<DeviceProfile> deviceProfileValidator;
private DeviceProfileDataValidator deviceProfileValidator;
@Autowired
private ImageService imageService;
@ -95,21 +96,23 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@TransactionalEventListener(classes = DeviceProfileEvictEvent.class)
@Override
public void handleEvictEvent(DeviceProfileEvictEvent event) {
List<DeviceProfileCacheKey> keys = new ArrayList<>(2);
keys.add(DeviceProfileCacheKey.fromName(event.getTenantId(), event.getNewName()));
if (event.getDeviceProfileId() != null) {
keys.add(DeviceProfileCacheKey.fromId(event.getDeviceProfileId()));
List<DeviceProfileCacheKey> toEvict = new ArrayList<>(2);
toEvict.add(DeviceProfileCacheKey.forName(event.getTenantId(), event.getNewName()));
if (event.getSavedDeviceProfile() != null) {
cache.put(DeviceProfileCacheKey.forId(event.getSavedDeviceProfile().getId()), event.getSavedDeviceProfile());
} else if (event.getDeviceProfileId() != null) {
toEvict.add(DeviceProfileCacheKey.forId(event.getDeviceProfileId()));
}
if (event.isDefaultProfile()) {
keys.add(DeviceProfileCacheKey.defaultProfile(event.getTenantId()));
toEvict.add(DeviceProfileCacheKey.forDefaultProfile(event.getTenantId()));
}
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
keys.add(DeviceProfileCacheKey.fromName(event.getTenantId(), event.getOldName()));
toEvict.add(DeviceProfileCacheKey.forName(event.getTenantId(), event.getOldName()));
}
if (StringUtils.isNotEmpty(event.getProvisionDeviceKey())) {
keys.add(DeviceProfileCacheKey.fromProvisionDeviceKey(event.getProvisionDeviceKey()));
toEvict.add(DeviceProfileCacheKey.forProvisionKey(event.getProvisionDeviceKey()));
}
cache.evict(keys);
cache.evict(toEvict);
}
@Override
@ -121,8 +124,8 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId, boolean putInCache) {
log.trace("Executing findDeviceProfileById [{}]", deviceProfileId);
validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id);
return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromId(deviceProfileId),
() -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true, putInCache);
return cache.get(DeviceProfileCacheKey.forId(deviceProfileId),
() -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), putInCache);
}
@Override
@ -134,7 +137,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName, boolean putInCache) {
log.trace("Executing findDeviceProfileByName [{}][{}]", tenantId, profileName);
validateString(profileName, pn -> INCORRECT_DEVICE_PROFILE_NAME + pn);
return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromName(tenantId, profileName),
return cache.getOrFetchFromDB(DeviceProfileCacheKey.forName(tenantId, profileName),
() -> deviceProfileDao.findByName(tenantId, profileName), true, putInCache);
}
@ -142,7 +145,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
public DeviceProfile findDeviceProfileByProvisionDeviceKey(String provisionDeviceKey) {
log.trace("Executing findDeviceProfileByProvisionDeviceKey provisionKey [{}]", provisionDeviceKey);
validateString(provisionDeviceKey, dk -> INCORRECT_PROVISION_DEVICE_KEY + dk);
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromProvisionDeviceKey(provisionDeviceKey),
return cache.getAndPutInTransaction(DeviceProfileCacheKey.forProvisionKey(provisionDeviceKey),
() -> deviceProfileDao.findByProvisionDeviceKey(provisionDeviceKey), false);
}
@ -179,7 +182,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile);
publishEvictEvent(new DeviceProfileEvictEvent(savedDeviceProfile.getTenantId(), savedDeviceProfile.getName(),
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, savedDeviceProfile.getId(), savedDeviceProfile.isDefault(),
oldDeviceProfile != null ? oldDeviceProfile.getProvisionDeviceKey() : null));
oldDeviceProfile != null ? oldDeviceProfile.getProvisionDeviceKey() : null, savedDeviceProfile));
if (publishSaveEvent) {
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedDeviceProfile.getTenantId()).entityId(savedDeviceProfile.getId())
.entity(savedDeviceProfile).oldEntity(oldDeviceProfile).created(oldDeviceProfile == null).build());
@ -241,13 +244,9 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
null, deviceProfile.getId(), deviceProfile.isDefault(),
deviceProfile.getProvisionDeviceKey()));
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(deviceProfileId).entity(deviceProfile).build());
} catch (Exception t) {
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_device_profile")) {
throw new DataValidationException("The device profile referenced by the devices cannot be deleted!");
} else {
throw t;
}
} catch (Exception e) {
checkConstraintViolation(e, "fk_device_profile", "The device profile referenced by the devices cannot be deleted!");
throw e;
}
}
@ -316,7 +315,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
public DeviceProfile findDefaultDeviceProfile(TenantId tenantId) {
log.trace("Executing findDefaultDeviceProfile tenantId [{}]", tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return cache.getAndPutInTransaction(DeviceProfileCacheKey.defaultProfile(tenantId),
return cache.getAndPutInTransaction(DeviceProfileCacheKey.forDefaultProfile(tenantId),
() -> deviceProfileDao.findDefaultDeviceProfile(tenantId), true);
}

View File

@ -18,8 +18,8 @@ package org.thingsboard.server.dao.device;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;
@ -68,7 +68,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.entity.CachedVersionedEntityService;
import org.thingsboard.server.dao.entity.EntityCountService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
@ -76,8 +76,8 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.validator.DeviceDataValidator;
import org.thingsboard.server.dao.sql.JpaExecutorService;
import org.thingsboard.server.dao.tenant.TenantService;
@ -94,38 +94,23 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
@Service("DeviceDaoService")
@Slf4j
public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKey, Device, DeviceCacheEvictEvent> implements DeviceService {
@RequiredArgsConstructor
public class DeviceServiceImpl extends CachedVersionedEntityService<DeviceCacheKey, Device, DeviceCacheEvictEvent> implements DeviceService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId ";
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_DEVICE_ID = "Incorrect deviceId ";
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
@Autowired
private DeviceDao deviceDao;
@Autowired
private DeviceCredentialsService deviceCredentialsService;
@Autowired
private DeviceProfileService deviceProfileService;
@Autowired
private EventService eventService;
@Autowired
private TenantService tenantService;
@Autowired
private DataValidator<Device> deviceValidator;
@Autowired
private EntityCountService countService;
@Autowired
private JpaExecutorService executor;
private final DeviceDao deviceDao;
private final DeviceCredentialsService deviceCredentialsService;
private final DeviceProfileService deviceProfileService;
private final EventService eventService;
private final TenantService tenantService;
private final DeviceDataValidator deviceValidator;
private final EntityCountService countService;
private final JpaExecutorService executor;
@Override
public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) {
@ -139,11 +124,11 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, id -> INCORRECT_DEVICE_ID + id);
if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
return cache.getAndPutInTransaction(new DeviceCacheKey(deviceId),
() -> deviceDao.findById(tenantId, deviceId.getId()), true);
return cache.get(new DeviceCacheKey(deviceId),
() -> deviceDao.findById(tenantId, deviceId.getId()));
} else {
return cache.getAndPutInTransaction(new DeviceCacheKey(tenantId, deviceId),
() -> deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId()), true);
return cache.get(new DeviceCacheKey(tenantId, deviceId),
() -> deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId()));
}
}
@ -251,12 +236,13 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
device.setType(deviceProfile.getName());
device.setDeviceData(syncDeviceData(deviceProfile, device.getDeviceData()));
Device savedDevice = deviceDao.saveAndFlush(device.getTenantId(), device);
publishEvictEvent(deviceCacheEvictEvent);
if (device.getId() == null) {
countService.publishCountEntityEvictEvent(savedDevice.getTenantId(), EntityType.DEVICE);
}
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedDevice.getTenantId()).entityId(savedDevice.getId())
.entity(savedDevice).oldEntity(oldDevice).created(device.getId() == null).build());
deviceCacheEvictEvent.setSavedDevice(savedDevice);
publishEvictEvent(deviceCacheEvictEvent);
return savedDevice;
} catch (Exception t) {
handleEvictEvent(deviceCacheEvictEvent);
@ -270,16 +256,20 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@TransactionalEventListener(classes = DeviceCacheEvictEvent.class)
@Override
public void handleEvictEvent(DeviceCacheEvictEvent event) {
List<DeviceCacheKey> keys = new ArrayList<>(3);
keys.add(new DeviceCacheKey(event.getTenantId(), event.getNewName()));
if (event.getDeviceId() != null) {
keys.add(new DeviceCacheKey(event.getDeviceId()));
keys.add(new DeviceCacheKey(event.getTenantId(), event.getDeviceId()));
}
List<DeviceCacheKey> toEvict = new ArrayList<>(3);
toEvict.add(new DeviceCacheKey(event.getTenantId(), event.getNewName()));
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
keys.add(new DeviceCacheKey(event.getTenantId(), event.getOldName()));
toEvict.add(new DeviceCacheKey(event.getTenantId(), event.getOldName()));
}
cache.evict(keys);
Device savedDevice = event.getSavedDevice();
if (savedDevice != null) {
cache.put(new DeviceCacheKey(event.getDeviceId()), savedDevice);
cache.put(new DeviceCacheKey(event.getTenantId(), event.getDeviceId()), savedDevice);
} else {
toEvict.add(new DeviceCacheKey(event.getDeviceId()));
toEvict.add(new DeviceCacheKey(event.getTenantId(), event.getDeviceId()));
}
cache.evict(toEvict);
}
private DeviceData syncDeviceData(DeviceProfile deviceProfile, DeviceData deviceData) {

View File

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.cache.VersionedTbCache;
import org.thingsboard.server.common.data.HasVersion;
import java.io.Serializable;
public abstract class CachedVersionedEntityService<K extends Serializable, V extends Serializable & HasVersion, E> extends AbstractCachedEntityService<K, V, E> {
@Autowired
protected VersionedTbCache<K, V> cache;
}

View File

@ -19,6 +19,7 @@ import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.HasVersion;
import java.io.Serializable;
import java.util.List;
@ -26,11 +27,16 @@ import java.util.List;
@Getter
@EqualsAndHashCode
@Builder
public class EntityViewCacheValue implements Serializable {
public class EntityViewCacheValue implements Serializable, HasVersion {
private static final long serialVersionUID = 1959004642076413174L;
private final EntityView entityView;
private final List<EntityView> entityViews;
@Override
public Long getVersion() {
return entityView != null ? entityView.getVersion() : 0;
}
}

View File

@ -18,12 +18,12 @@ package org.thingsboard.server.dao.entityview;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.cache.VersionedCaffeineTbCache;
import org.thingsboard.server.common.data.CacheConstants;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("EntityViewCache")
public class EntityViewCaffeineCache extends CaffeineTbTransactionalCache<EntityViewCacheKey, EntityViewCacheValue> {
public class EntityViewCaffeineCache extends VersionedCaffeineTbCache<EntityViewCacheKey, EntityViewCacheValue> {
public EntityViewCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.ENTITY_VIEW_CACHE);

View File

@ -15,21 +15,25 @@
*/
package org.thingsboard.server.dao.entityview;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
@Data
@RequiredArgsConstructor
@AllArgsConstructor
class EntityViewEvictEvent {
private final TenantId tenantId;
private final EntityViewId id;
private final EntityViewId entityViewId;
private final EntityId newEntityId;
private final EntityId oldEntityId;
private final String newName;
private final String oldName;
private EntityView savedEntityView;
}

View File

@ -19,14 +19,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.RedisTbTransactionalCache;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbJsonRedisSerializer;
import org.thingsboard.server.cache.VersionedRedisTbCache;
import org.thingsboard.server.common.data.CacheConstants;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("EntityViewCache")
public class EntityViewRedisCache extends RedisTbTransactionalCache<EntityViewCacheKey, EntityViewCacheValue> {
public class EntityViewRedisCache extends VersionedRedisTbCache<EntityViewCacheKey, EntityViewCacheValue> {
public EntityViewRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.ENTITY_VIEW_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbJsonRedisSerializer<>(EntityViewCacheValue.class));

View File

@ -19,6 +19,7 @@ import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -43,16 +44,15 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.entity.CachedVersionedEntityService;
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.validator.EntityViewDataValidator;
import org.thingsboard.server.dao.sql.JpaExecutorService;
import jakarta.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -69,7 +69,7 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
*/
@Service("EntityViewDaoService")
@Slf4j
public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityViewCacheKey, EntityViewCacheValue, EntityViewEvictEvent> implements EntityViewService {
public class EntityViewServiceImpl extends CachedVersionedEntityService<EntityViewCacheKey, EntityViewCacheValue, EntityViewEvictEvent> implements EntityViewService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
@ -80,7 +80,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
private EntityViewDao entityViewDao;
@Autowired
private DataValidator<EntityView> entityViewValidator;
private EntityViewDataValidator entityViewValidator;
@Autowired
protected JpaExecutorService service;
@ -88,17 +88,21 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@TransactionalEventListener(classes = EntityViewEvictEvent.class)
@Override
public void handleEvictEvent(EntityViewEvictEvent event) {
List<EntityViewCacheKey> keys = new ArrayList<>(5);
keys.add(EntityViewCacheKey.byName(event.getTenantId(), event.getNewName()));
keys.add(EntityViewCacheKey.byId(event.getId()));
keys.add(EntityViewCacheKey.byEntityId(event.getTenantId(), event.getNewEntityId()));
List<EntityViewCacheKey> toEvict = new ArrayList<>(5);
toEvict.add(EntityViewCacheKey.byName(event.getTenantId(), event.getNewName()));
if (event.getSavedEntityView() != null) {
cache.put(EntityViewCacheKey.byId(event.getSavedEntityView().getId()), new EntityViewCacheValue(event.getSavedEntityView(), null));
} else if (event.getEntityViewId() != null) {
toEvict.add(EntityViewCacheKey.byId(event.getEntityViewId()));
}
toEvict.add(EntityViewCacheKey.byEntityId(event.getTenantId(), event.getNewEntityId()));
if (event.getOldEntityId() != null && !event.getOldEntityId().equals(event.getNewEntityId())) {
keys.add(EntityViewCacheKey.byEntityId(event.getTenantId(), event.getOldEntityId()));
toEvict.add(EntityViewCacheKey.byEntityId(event.getTenantId(), event.getOldEntityId()));
}
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
keys.add(EntityViewCacheKey.byName(event.getTenantId(), event.getOldName()));
toEvict.add(EntityViewCacheKey.byName(event.getTenantId(), event.getOldName()));
}
cache.evict(keys);
cache.evict(toEvict);
}
@Override
@ -113,11 +117,11 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
if (doValidate) {
old = entityViewValidator.validate(entityView, EntityView::getTenantId);
} else if (entityView.getId() != null) {
old = findEntityViewById(entityView.getTenantId(), entityView.getId());
old = findEntityViewById(entityView.getTenantId(), entityView.getId(), false);
}
try {
EntityView saved = entityViewDao.save(entityView.getTenantId(), entityView);
publishEvictEvent(new EntityViewEvictEvent(saved.getTenantId(), saved.getId(), saved.getEntityId(), old != null ? old.getEntityId() : null, saved.getName(), old != null ? old.getName() : null));
publishEvictEvent(new EntityViewEvictEvent(saved.getTenantId(), saved.getId(), saved.getEntityId(), old != null ? old.getEntityId() : null, saved.getName(), old != null ? old.getName() : null, saved));
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(saved.getTenantId())
.entityId(saved.getId()).created(entityView.getId() == null).build());
return saved;
@ -172,9 +176,11 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
public EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId, boolean putInCache) {
log.trace("Executing findEntityViewById [{}]", entityViewId);
validateId(entityViewId, id -> INCORRECT_ENTITY_VIEW_ID + id);
return cache.getOrFetchFromDB(EntityViewCacheKey.byId(entityViewId),
() -> entityViewDao.findById(tenantId, entityViewId.getId())
, EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true, putInCache);
EntityViewCacheValue value = cache.get(EntityViewCacheKey.byId(entityViewId), () -> {
EntityView entityView = entityViewDao.findById(tenantId, entityViewId.getId());
return new EntityViewCacheValue(entityView, null);
}, putInCache);
return value != null ? value.getEntityView() : null;
}
@Override
@ -233,7 +239,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
PageLink pageLink) {
log.trace("Executing findEntityViewByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," +
" pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, id ->INCORRECT_TENANT_ID + id);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
return entityViewDao.findEntityViewsByTenantIdAndCustomerId(tenantId.getId(),

View File

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.service.validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.StringUtils;
@ -32,7 +33,7 @@ public class DeviceCredentialsDataValidator extends DataValidator<DeviceCredenti
@Autowired
private DeviceCredentialsDao deviceCredentialsDao;
@Autowired
@Autowired @Lazy
private DeviceService deviceService;
@Override

View File

@ -79,7 +79,7 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
try {
entity = doSave(entity, isNew);
} catch (OptimisticLockException e) {
throw new EntityVersionMismatchException("The 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);
}
@ -145,7 +145,9 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
@Override
@Transactional
public void removeById(TenantId tenantId, UUID id) {
getRepository().deleteById(id);
JpaRepository<E, UUID> repository = getRepository();
repository.deleteById(id);
repository.flush();
log.debug("Remove request: {}", id);
}
@ -153,6 +155,7 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
public void removeAllByIds(Collection<UUID> ids) {
JpaRepository<E, UUID> repository = getRepository();
ids.forEach(repository::deleteById);
repository.flush();
}
@Override