Device Profile Cache
This commit is contained in:
parent
ffe5b5ee91
commit
c69ba6d02d
@ -1,41 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright © 2016-2022 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.cache;
|
|
||||||
|
|
||||||
public class CacheKeyUtil {
|
|
||||||
|
|
||||||
public static String toString(Object... keyElements) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
boolean first = true;
|
|
||||||
for (Object keyElement : keyElements) {
|
|
||||||
first = addElement(sb, first, keyElement);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean addElement(StringBuilder sb, boolean first, Object element) {
|
|
||||||
if (element != null) {
|
|
||||||
if (!first) {
|
|
||||||
sb.append("_");
|
|
||||||
}
|
|
||||||
sb.append(element);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -19,8 +19,6 @@ import lombok.Builder;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.thingsboard.server.cache.CacheKeyUtil;
|
|
||||||
import org.thingsboard.server.common.data.id.DeviceId;
|
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -31,12 +29,14 @@ import java.io.Serializable;
|
|||||||
@Builder
|
@Builder
|
||||||
public class AssetCacheKey implements Serializable {
|
public class AssetCacheKey implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 4196610233744512673L;
|
||||||
|
|
||||||
private final TenantId tenantId;
|
private final TenantId tenantId;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return CacheKeyUtil.toString(tenantId, name);
|
return tenantId + "_" + name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import lombok.Builder;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.thingsboard.server.cache.CacheKeyUtil;
|
|
||||||
import org.thingsboard.server.common.data.id.DeviceId;
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
|
||||||
@ -45,7 +44,11 @@ public class DeviceCacheKey implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return CacheKeyUtil.toString(tenantId, deviceId, deviceName);
|
if (deviceId != null) {
|
||||||
|
return tenantId + "_" + deviceId;
|
||||||
|
} else {
|
||||||
|
return tenantId + "_n_" + deviceName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2022 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.device;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||||
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class DeviceProfileCacheKey implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 8220455917177676472L;
|
||||||
|
|
||||||
|
private final TenantId tenantId;
|
||||||
|
private final String name;
|
||||||
|
private final DeviceProfileId deviceProfileId;
|
||||||
|
private final boolean defaultProfile;
|
||||||
|
|
||||||
|
private DeviceProfileCacheKey(TenantId tenantId, String name, DeviceProfileId deviceProfileId, boolean defaultProfile) {
|
||||||
|
this.tenantId = tenantId;
|
||||||
|
this.name = name;
|
||||||
|
this.deviceProfileId = deviceProfileId;
|
||||||
|
this.defaultProfile = defaultProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DeviceProfileCacheKey fromName(TenantId tenantId, String name) {
|
||||||
|
return new DeviceProfileCacheKey(tenantId, name, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DeviceProfileCacheKey fromId(DeviceProfileId id) {
|
||||||
|
return new DeviceProfileCacheKey(null, null, id, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DeviceProfileCacheKey defaultProfile(TenantId tenantId) {
|
||||||
|
return new DeviceProfileCacheKey(tenantId, null, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (deviceProfileId != null) {
|
||||||
|
return deviceProfileId.toString();
|
||||||
|
} else if (defaultProfile) {
|
||||||
|
return tenantId.toString();
|
||||||
|
} else {
|
||||||
|
return tenantId + "_" + name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2022 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.device;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.thingsboard.server.common.data.CacheConstants;
|
||||||
|
import org.thingsboard.server.common.data.Device;
|
||||||
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
|
import org.thingsboard.server.dao.cache.CaffeineTbTransactionalCache;
|
||||||
|
|
||||||
|
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
|
||||||
|
@Service("DeviceProfileCache")
|
||||||
|
public class DeviceProfileCaffeineCache extends CaffeineTbTransactionalCache<DeviceProfileCacheKey, DeviceProfile> {
|
||||||
|
|
||||||
|
public DeviceProfileCaffeineCache(CacheManager cacheManager) {
|
||||||
|
super(cacheManager, CacheConstants.DEVICE_PROFILE_CACHE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2022 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.device;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||||
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class DeviceProfileEvictEvent {
|
||||||
|
|
||||||
|
private final TenantId tenantId;
|
||||||
|
private final String newName;
|
||||||
|
private final String oldName;
|
||||||
|
private final DeviceProfileId deviceProfileId;
|
||||||
|
private final boolean defaultProfile;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2022 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.device;
|
||||||
|
|
||||||
|
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.TBRedisCacheConfiguration;
|
||||||
|
import org.thingsboard.server.common.data.CacheConstants;
|
||||||
|
import org.thingsboard.server.common.data.Device;
|
||||||
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
|
import org.thingsboard.server.dao.cache.RedisTbTransactionalCache;
|
||||||
|
import org.thingsboard.server.dao.cache.TbRedisSerializer;
|
||||||
|
|
||||||
|
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
|
||||||
|
@Service("DeviceProfileCache")
|
||||||
|
public class DeviceProfileRedisCache extends RedisTbTransactionalCache<DeviceProfileCacheKey, DeviceProfile> {
|
||||||
|
|
||||||
|
public DeviceProfileRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
|
||||||
|
super(CacheConstants.DEVICE_PROFILE_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbRedisSerializer<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,17 +17,23 @@ package org.thingsboard.server.dao.device;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.hibernate.exception.ConstraintViolationException;
|
import org.hibernate.exception.ConstraintViolationException;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.Cache;
|
import org.springframework.cache.Cache;
|
||||||
import org.springframework.cache.CacheManager;
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.transaction.event.TransactionalEventListener;
|
||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
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.DeviceProfileProvisionType;
|
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
|
||||||
import org.thingsboard.server.common.data.DeviceProfileType;
|
import org.thingsboard.server.common.data.DeviceProfileType;
|
||||||
import org.thingsboard.server.common.data.DeviceTransportType;
|
import org.thingsboard.server.common.data.DeviceTransportType;
|
||||||
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
|
import org.thingsboard.server.common.data.TenantProfile;
|
||||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
|
||||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
|
||||||
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
|
||||||
@ -36,14 +42,20 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
|
|||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.page.PageData;
|
import org.thingsboard.server.common.data.page.PageData;
|
||||||
import org.thingsboard.server.common.data.page.PageLink;
|
import org.thingsboard.server.common.data.page.PageLink;
|
||||||
|
import org.thingsboard.server.dao.asset.AssetCacheKey;
|
||||||
|
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
|
||||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
||||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||||
import org.thingsboard.server.dao.service.DataValidator;
|
import org.thingsboard.server.dao.service.DataValidator;
|
||||||
import org.thingsboard.server.dao.service.PaginatedRemover;
|
import org.thingsboard.server.dao.service.PaginatedRemover;
|
||||||
import org.thingsboard.server.dao.service.Validator;
|
import org.thingsboard.server.dao.service.Validator;
|
||||||
|
import org.thingsboard.server.dao.tenant.TenantProfileCacheKey;
|
||||||
|
import org.thingsboard.server.dao.tenant.TenantProfileEvictEvent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
@ -52,7 +64,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DeviceProfileServiceImpl extends AbstractEntityService implements DeviceProfileService {
|
public class DeviceProfileServiceImpl extends AbstractCachedEntityService<DeviceProfileCacheKey, DeviceProfile, DeviceProfileEvictEvent> implements DeviceProfileService {
|
||||||
|
|
||||||
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
||||||
private static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId ";
|
private static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId ";
|
||||||
@ -67,51 +79,60 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
@Autowired
|
@Autowired
|
||||||
private DeviceService deviceService;
|
private DeviceService deviceService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private CacheManager cacheManager;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private DataValidator<DeviceProfile> deviceProfileValidator;
|
private DataValidator<DeviceProfile> deviceProfileValidator;
|
||||||
|
|
||||||
private final Lock findOrCreateLock = new ReentrantLock();
|
private final Lock findOrCreateLock = new ReentrantLock();
|
||||||
|
|
||||||
@Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}")
|
@TransactionalEventListener(classes = DeviceProfileEvictEvent.class)
|
||||||
|
@Override
|
||||||
|
public void handleEvictEvent(DeviceProfileEvictEvent event) {
|
||||||
|
List<DeviceProfileCacheKey> keys = new ArrayList<>(2);
|
||||||
|
keys.add(DeviceProfileCacheKey.fromId(event.getDeviceProfileId()));
|
||||||
|
keys.add(DeviceProfileCacheKey.fromName(event.getTenantId(), event.getNewName()));
|
||||||
|
if (event.isDefaultProfile()) {
|
||||||
|
keys.add(DeviceProfileCacheKey.defaultProfile(event.getTenantId()));
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
|
||||||
|
keys.add(DeviceProfileCacheKey.fromName(event.getTenantId(), event.getOldName()));
|
||||||
|
}
|
||||||
|
cache.evict(keys);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||||
log.trace("Executing findDeviceProfileById [{}]", deviceProfileId);
|
log.trace("Executing findDeviceProfileById [{}]", deviceProfileId);
|
||||||
Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||||
return deviceProfileDao.findById(tenantId, deviceProfileId.getId());
|
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromId(deviceProfileId),
|
||||||
|
() -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName) {
|
public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName) {
|
||||||
log.trace("Executing findDeviceProfileByName [{}][{}]", tenantId, profileName);
|
log.trace("Executing findDeviceProfileByName [{}][{}]", tenantId, profileName);
|
||||||
Validator.validateString(profileName, INCORRECT_DEVICE_PROFILE_NAME + profileName);
|
Validator.validateString(profileName, INCORRECT_DEVICE_PROFILE_NAME + profileName);
|
||||||
return deviceProfileDao.findByName(tenantId, profileName);
|
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromName(tenantId, profileName),
|
||||||
|
() -> deviceProfileDao.findByName(tenantId, profileName), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{'info', #deviceProfileId.id}")
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
public DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||||
log.trace("Executing findDeviceProfileById [{}]", deviceProfileId);
|
log.trace("Executing findDeviceProfileById [{}]", deviceProfileId);
|
||||||
Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||||
return deviceProfileDao.findDeviceProfileInfoById(tenantId, deviceProfileId.getId());
|
return toDeviceProfileInfo(findDeviceProfileById(tenantId, deviceProfileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceProfile saveDeviceProfile(DeviceProfile deviceProfile) {
|
public DeviceProfile saveDeviceProfile(DeviceProfile deviceProfile) {
|
||||||
log.trace("Executing saveDeviceProfile [{}]", deviceProfile);
|
log.trace("Executing saveDeviceProfile [{}]", deviceProfile);
|
||||||
deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId);
|
DeviceProfile oldDeviceProfile = deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId);
|
||||||
DeviceProfile oldDeviceProfile = null;
|
|
||||||
if (deviceProfile.getId() != null) {
|
|
||||||
oldDeviceProfile = deviceProfileDao.findById(deviceProfile.getTenantId(), deviceProfile.getId().getId());
|
|
||||||
}
|
|
||||||
DeviceProfile savedDeviceProfile;
|
DeviceProfile savedDeviceProfile;
|
||||||
try {
|
try {
|
||||||
savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile);
|
savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile);
|
||||||
} catch (Exception t) {
|
} catch (Exception t) {
|
||||||
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
|
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
|
||||||
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_profile_name_unq_key")) {
|
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_profile_name_unq_key")) {
|
||||||
|
//TODO: refactor this to return existing device profile. If they are equal - no need to throw exception. Then we can make this call @Transactional and tests will not fail.
|
||||||
throw new DataValidationException("Device profile with such name already exists!");
|
throw new DataValidationException("Device profile with such name already exists!");
|
||||||
} else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_provision_key_unq_key")) {
|
} else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_provision_key_unq_key")) {
|
||||||
throw new DataValidationException("Device profile with such provision device key already exists!");
|
throw new DataValidationException("Device profile with such provision device key already exists!");
|
||||||
@ -119,14 +140,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
throw t;
|
throw t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cache cache = cacheManager.getCache(DEVICE_PROFILE_CACHE);
|
publishEvictEvent(new DeviceProfileEvictEvent(savedDeviceProfile.getTenantId(), savedDeviceProfile.getName(),
|
||||||
cache.evict(Collections.singletonList(savedDeviceProfile.getId().getId()));
|
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, savedDeviceProfile.getId(), savedDeviceProfile.isDefault()));
|
||||||
cache.evict(Arrays.asList("info", savedDeviceProfile.getId().getId()));
|
|
||||||
cache.evict(Arrays.asList(deviceProfile.getTenantId().getId(), deviceProfile.getName()));
|
|
||||||
if (savedDeviceProfile.isDefault()) {
|
|
||||||
cache.evict(Arrays.asList("default", savedDeviceProfile.getTenantId().getId()));
|
|
||||||
cache.evict(Arrays.asList("default", "info", savedDeviceProfile.getTenantId().getId()));
|
|
||||||
}
|
|
||||||
if (oldDeviceProfile != null && !oldDeviceProfile.getName().equals(deviceProfile.getName())) {
|
if (oldDeviceProfile != null && !oldDeviceProfile.getName().equals(deviceProfile.getName())) {
|
||||||
PageLink pageLink = new PageLink(100);
|
PageLink pageLink = new PageLink(100);
|
||||||
PageData<Device> pageData;
|
PageData<Device> pageData;
|
||||||
@ -142,6 +157,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
return savedDeviceProfile;
|
return savedDeviceProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.SUPPORTS)
|
||||||
@Override
|
@Override
|
||||||
public void deleteDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
public void deleteDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||||
log.trace("Executing deleteDeviceProfile [{}]", deviceProfileId);
|
log.trace("Executing deleteDeviceProfile [{}]", deviceProfileId);
|
||||||
@ -157,6 +173,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
DeviceProfileId deviceProfileId = deviceProfile.getId();
|
DeviceProfileId deviceProfileId = deviceProfile.getId();
|
||||||
try {
|
try {
|
||||||
deviceProfileDao.removeById(tenantId, deviceProfileId.getId());
|
deviceProfileDao.removeById(tenantId, deviceProfileId.getId());
|
||||||
|
publishEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(),
|
||||||
|
null, deviceProfile.getId(), deviceProfile.isDefault()));
|
||||||
} catch (Exception t) {
|
} catch (Exception t) {
|
||||||
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
|
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
|
||||||
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_device_profile")) {
|
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_device_profile")) {
|
||||||
@ -166,10 +184,6 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
deleteEntityRelations(tenantId, deviceProfileId);
|
deleteEntityRelations(tenantId, deviceProfileId);
|
||||||
Cache cache = cacheManager.getCache(DEVICE_PROFILE_CACHE);
|
|
||||||
cache.evict(Collections.singletonList(deviceProfileId.getId()));
|
|
||||||
cache.evict(Arrays.asList("info", deviceProfileId.getId()));
|
|
||||||
cache.evict(Arrays.asList(tenantId.getId(), deviceProfile.getName()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -188,7 +202,6 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
return deviceProfileDao.findDeviceProfileInfos(tenantId, pageLink, transportType);
|
return deviceProfileDao.findDeviceProfileInfos(tenantId, pageLink, transportType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#tenantId.id, #name}")
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String name) {
|
public DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String name) {
|
||||||
log.trace("Executing findOrCreateDefaultDeviceProfile");
|
log.trace("Executing findOrCreateDefaultDeviceProfile");
|
||||||
@ -207,6 +220,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
return deviceProfile;
|
return deviceProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.SUPPORTS)
|
||||||
@Override
|
@Override
|
||||||
public DeviceProfile createDefaultDeviceProfile(TenantId tenantId) {
|
public DeviceProfile createDefaultDeviceProfile(TenantId tenantId) {
|
||||||
log.trace("Executing createDefaultDeviceProfile tenantId [{}]", tenantId);
|
log.trace("Executing createDefaultDeviceProfile tenantId [{}]", tenantId);
|
||||||
@ -234,56 +248,49 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
return saveDeviceProfile(deviceProfile);
|
return saveDeviceProfile(deviceProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{'default', #tenantId.id}")
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceProfile findDefaultDeviceProfile(TenantId tenantId) {
|
public DeviceProfile findDefaultDeviceProfile(TenantId tenantId) {
|
||||||
log.trace("Executing findDefaultDeviceProfile tenantId [{}]", tenantId);
|
log.trace("Executing findDefaultDeviceProfile tenantId [{}]", tenantId);
|
||||||
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
|
||||||
return deviceProfileDao.findDefaultDeviceProfile(tenantId);
|
return cache.getAndPutInTransaction(DeviceProfileCacheKey.defaultProfile(tenantId),
|
||||||
|
() -> deviceProfileDao.findDefaultDeviceProfile(tenantId), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{'default', 'info', #tenantId.id}")
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceProfileInfo findDefaultDeviceProfileInfo(TenantId tenantId) {
|
public DeviceProfileInfo findDefaultDeviceProfileInfo(TenantId tenantId) {
|
||||||
log.trace("Executing findDefaultDeviceProfileInfo tenantId [{}]", tenantId);
|
log.trace("Executing findDefaultDeviceProfileInfo tenantId [{}]", tenantId);
|
||||||
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
|
||||||
return deviceProfileDao.findDefaultDeviceProfileInfo(tenantId);
|
return toDeviceProfileInfo(findDefaultDeviceProfile(tenantId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.SUPPORTS)
|
||||||
@Override
|
@Override
|
||||||
public boolean setDefaultDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
public boolean setDefaultDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
|
||||||
log.trace("Executing setDefaultDeviceProfile [{}]", deviceProfileId);
|
log.trace("Executing setDefaultDeviceProfile [{}]", deviceProfileId);
|
||||||
Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
|
||||||
DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId());
|
DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId());
|
||||||
if (!deviceProfile.isDefault()) {
|
if (!deviceProfile.isDefault()) {
|
||||||
Cache cache = cacheManager.getCache(DEVICE_PROFILE_CACHE);
|
|
||||||
deviceProfile.setDefault(true);
|
deviceProfile.setDefault(true);
|
||||||
DeviceProfile previousDefaultDeviceProfile = findDefaultDeviceProfile(tenantId);
|
DeviceProfile previousDefaultDeviceProfile = findDefaultDeviceProfile(tenantId);
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
if (previousDefaultDeviceProfile == null) {
|
if (previousDefaultDeviceProfile == null) {
|
||||||
deviceProfileDao.save(tenantId, deviceProfile);
|
deviceProfileDao.save(tenantId, deviceProfile);
|
||||||
|
publishEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(), null, deviceProfile.getId(), true));
|
||||||
changed = true;
|
changed = true;
|
||||||
} else if (!previousDefaultDeviceProfile.getId().equals(deviceProfile.getId())) {
|
} else if (!previousDefaultDeviceProfile.getId().equals(deviceProfile.getId())) {
|
||||||
previousDefaultDeviceProfile.setDefault(false);
|
previousDefaultDeviceProfile.setDefault(false);
|
||||||
deviceProfileDao.save(tenantId, previousDefaultDeviceProfile);
|
deviceProfileDao.save(tenantId, previousDefaultDeviceProfile);
|
||||||
deviceProfileDao.save(tenantId, deviceProfile);
|
deviceProfileDao.save(tenantId, deviceProfile);
|
||||||
cache.evict(Collections.singletonList(previousDefaultDeviceProfile.getId().getId()));
|
publishEvictEvent(new DeviceProfileEvictEvent(previousDefaultDeviceProfile.getTenantId(), previousDefaultDeviceProfile.getName(), null, previousDefaultDeviceProfile.getId(), false));
|
||||||
cache.evict(Arrays.asList("info", previousDefaultDeviceProfile.getId().getId()));
|
publishEvictEvent(new DeviceProfileEvictEvent(deviceProfile.getTenantId(), deviceProfile.getName(), null, deviceProfile.getId(), true));
|
||||||
cache.evict(Arrays.asList(tenantId.getId(), previousDefaultDeviceProfile.getName()));
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
if (changed) {
|
|
||||||
cache.evict(Collections.singletonList(deviceProfile.getId().getId()));
|
|
||||||
cache.evict(Arrays.asList("info", deviceProfile.getId().getId()));
|
|
||||||
cache.evict(Arrays.asList("default", tenantId.getId()));
|
|
||||||
cache.evict(Arrays.asList("default", "info", tenantId.getId()));
|
|
||||||
cache.evict(Arrays.asList(tenantId.getId(), deviceProfile.getName()));
|
|
||||||
}
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.SUPPORTS)
|
||||||
@Override
|
@Override
|
||||||
public void deleteDeviceProfilesByTenantId(TenantId tenantId) {
|
public void deleteDeviceProfilesByTenantId(TenantId tenantId) {
|
||||||
log.trace("Executing deleteDeviceProfilesByTenantId, tenantId [{}]", tenantId);
|
log.trace("Executing deleteDeviceProfilesByTenantId, tenantId [{}]", tenantId);
|
||||||
@ -305,4 +312,9 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private DeviceProfileInfo toDeviceProfileInfo(DeviceProfile profile) {
|
||||||
|
return profile == null ? null : new DeviceProfileInfo(profile.getId(), profile.getName(), profile.getImage(),
|
||||||
|
profile.getDefaultDashboardId(), profile.getType(), profile.getTransportType());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import lombok.Builder;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.thingsboard.server.cache.CacheKeyUtil;
|
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -35,7 +34,7 @@ public class EdgeCacheKey implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return CacheKeyUtil.toString(tenantId, name);
|
return tenantId + "_" + name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,8 +18,6 @@ package org.thingsboard.server.dao.entityview;
|
|||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.thingsboard.server.cache.CacheKeyUtil;
|
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.EntityViewId;
|
import org.thingsboard.server.common.data.id.EntityViewId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
@ -57,7 +55,13 @@ public class EntityViewCacheKey implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return CacheKeyUtil.toString(tenantId, name, entityId, entityViewId);
|
if (entityViewId != null) {
|
||||||
|
return entityViewId.toString();
|
||||||
|
} else if (entityId != null) {
|
||||||
|
return tenantId + "_" + entityId;
|
||||||
|
} else {
|
||||||
|
return tenantId + "_n_" + name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,11 +18,7 @@ package org.thingsboard.server.dao.entityview;
|
|||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.thingsboard.server.cache.CacheKeyUtil;
|
|
||||||
import org.thingsboard.server.common.data.EntityView;
|
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;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@ -19,9 +19,7 @@ import lombok.Builder;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.thingsboard.server.cache.CacheKeyUtil;
|
|
||||||
import org.thingsboard.server.common.data.id.OtaPackageId;
|
import org.thingsboard.server.common.data.id.OtaPackageId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
@ -35,7 +33,7 @@ public class OtaPackageCacheKey implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return id.getId().toString();
|
return id.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import lombok.Builder;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.thingsboard.server.cache.CacheKeyUtil;
|
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
||||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
||||||
@ -46,7 +45,24 @@ public class RelationCacheKey implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return CacheKeyUtil.toString(from, to, typeGroup, type, direction);
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean first = add(sb, true, from);
|
||||||
|
first = add(sb, first, to);
|
||||||
|
first = add(sb, first, type);
|
||||||
|
first = add(sb, first, typeGroup);
|
||||||
|
add(sb, first, direction);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean add(StringBuilder sb, boolean first, Object param) {
|
||||||
|
if (param != null) {
|
||||||
|
if (!first) {
|
||||||
|
sb.append("_");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
sb.append(param);
|
||||||
|
}
|
||||||
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,7 +47,7 @@ public class TenantProfileCacheKey implements Serializable {
|
|||||||
if (defaultProfile) {
|
if (defaultProfile) {
|
||||||
return "default";
|
return "default";
|
||||||
} else {
|
} else {
|
||||||
return tenantProfileId.getId().toString();
|
return tenantProfileId.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,6 +56,8 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest {
|
|||||||
UUID alarm3Id = UUID.fromString("d4b68f45-3e96-11e7-a884-898080180d6b");
|
UUID alarm3Id = UUID.fromString("d4b68f45-3e96-11e7-a884-898080180d6b");
|
||||||
int alarmCountBeforeSave = alarmDao.find(TenantId.fromUUID(tenantId)).size();
|
int alarmCountBeforeSave = alarmDao.find(TenantId.fromUUID(tenantId)).size();
|
||||||
saveAlarm(alarm1Id, tenantId, originator1Id, "TEST_ALARM");
|
saveAlarm(alarm1Id, tenantId, originator1Id, "TEST_ALARM");
|
||||||
|
//The timestamp of the startTime should be different in order for test to always work
|
||||||
|
Thread.sleep(1);
|
||||||
saveAlarm(alarm2Id, tenantId, originator1Id, "TEST_ALARM");
|
saveAlarm(alarm2Id, tenantId, originator1Id, "TEST_ALARM");
|
||||||
saveAlarm(alarm3Id, tenantId, originator2Id, "TEST_ALARM");
|
saveAlarm(alarm3Id, tenantId, originator2Id, "TEST_ALARM");
|
||||||
int alarmCountAfterSave = alarmDao.find(TenantId.fromUUID(tenantId)).size();
|
int alarmCountAfterSave = alarmDao.find(TenantId.fromUUID(tenantId)).size();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user