diff --git a/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java b/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java index b12e114d93..3886a9de65 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java @@ -54,11 +54,7 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic public ComponentDescriptor saveComponent(TenantId tenantId, ComponentDescriptor component) { componentValidator.validate(component, data -> new TenantId(EntityId.NULL_UUID)); Optional result = componentDescriptorDao.saveIfNotExist(tenantId, component); - if (result.isPresent()) { - return result.get(); - } else { - return componentDescriptorDao.findByClazz(tenantId, component.getClazz()); - } + return result.orElseGet(() -> componentDescriptorDao.findByClazz(tenantId, component.getClazz())); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java new file mode 100644 index 0000000000..ff464139b2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java @@ -0,0 +1,97 @@ +/** + * Copyright © 2016-2019 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.sql.component; + +import lombok.extern.slf4j.Slf4j; +import org.hibernate.exception.ConstraintViolationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; +import org.thingsboard.server.dao.util.SqlDao; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; + +@Slf4j +@SqlDao +@Repository +public abstract class AbstractComponentDescriptorInsertRepository { + + @PersistenceContext + protected EntityManager entityManager; + + @Autowired + protected PlatformTransactionManager transactionManager; + + public abstract ComponentDescriptorEntity saveOrUpdate(ComponentDescriptorEntity entity); + + protected ComponentDescriptorEntity saveAndGet(ComponentDescriptorEntity entity, String insertOrUpdateOnPrimaryKeyConflict, String insertOrUpdateOnUniqueKeyConflict) { + ComponentDescriptorEntity componentDescriptorEntity = null; + TransactionStatus insertTransaction = getTransactionStatus(TransactionDefinition.PROPAGATION_REQUIRED); + try { + componentDescriptorEntity = processSaveOrUpdate(entity, insertOrUpdateOnPrimaryKeyConflict); + transactionManager.commit(insertTransaction); + } catch (Throwable throwable) { + transactionManager.rollback(insertTransaction); + if (throwable.getCause() instanceof ConstraintViolationException) { + log.trace("Insert request leaded in a violation of a defined integrity constraint {} for Component Descriptor with id {}, name {} and entityType {}", throwable.getMessage(), entity.getId(), entity.getName(), entity.getType()); + TransactionStatus transaction = getTransactionStatus(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + try { + componentDescriptorEntity = processSaveOrUpdate(entity, insertOrUpdateOnUniqueKeyConflict); + } catch (Throwable th) { + log.trace("Could not execute the update statement for Component Descriptor with id {}, name {} and entityType {}", entity.getId(), entity.getName(), entity.getType()); + transactionManager.rollback(transaction); + } + transactionManager.commit(transaction); + } else { + log.trace("Could not execute the insert statement for Component Descriptor with id {}, name {} and entityType {}", entity.getId(), entity.getName(), entity.getType()); + } + } + return componentDescriptorEntity; + } + + @Modifying + protected abstract ComponentDescriptorEntity doProcessSaveOrUpdate(ComponentDescriptorEntity entity, String query); + + protected Query getQuery(ComponentDescriptorEntity entity, String query) { + return entityManager.createNativeQuery(query, ComponentDescriptorEntity.class) + .setParameter("id", UUIDConverter.fromTimeUUID(entity.getId())) + .setParameter("actions", entity.getActions()) + .setParameter("clazz", entity.getClazz()) + .setParameter("configuration_descriptor", entity.getConfigurationDescriptor().toString()) + .setParameter("name", entity.getName()) + .setParameter("scope", entity.getScope().name()) + .setParameter("search_text", entity.getSearchText()) + .setParameter("type", entity.getType().name()); + } + + private ComponentDescriptorEntity processSaveOrUpdate(ComponentDescriptorEntity entity, String query) { + return doProcessSaveOrUpdate(entity, query); + } + + private TransactionStatus getTransactionStatus(int propagationRequired) { + DefaultTransactionDefinition insertDefinition = new DefaultTransactionDefinition(); + insertDefinition.setPropagationBehavior(propagationRequired); + return transactionManager.getTransaction(insertDefinition); + } +} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/HsqlComponentDescriptorInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/HsqlComponentDescriptorInsertRepository.java new file mode 100644 index 0000000000..ae7d10656c --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/HsqlComponentDescriptorInsertRepository.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2019 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.sql.component; + +import org.springframework.stereotype.Repository; +import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; +import org.thingsboard.server.dao.util.HsqlDao; +import org.thingsboard.server.dao.util.SqlTsDao; + +@SqlTsDao +@HsqlDao +@Repository +public class HsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository { + + private static final String P_KEY_CONFLICT_STATEMENT = "(component_descriptor.id=I.id)"; + private static final String UNQ_KEY_CONFLICT_STATEMENT = "(component_descriptor.clazz=I.clazz)"; + + private static final String INSERT_OR_UPDATE_ON_P_KEY_CONFLICT = getInsertString(P_KEY_CONFLICT_STATEMENT); + private static final String INSERT_OR_UPDATE_ON_UNQ_KEY_CONFLICT = getInsertString(UNQ_KEY_CONFLICT_STATEMENT); + + @Override + public ComponentDescriptorEntity saveOrUpdate(ComponentDescriptorEntity entity) { + return saveAndGet(entity, INSERT_OR_UPDATE_ON_P_KEY_CONFLICT, INSERT_OR_UPDATE_ON_UNQ_KEY_CONFLICT); + } + + @Override + protected ComponentDescriptorEntity doProcessSaveOrUpdate(ComponentDescriptorEntity entity, String query) { + getQuery(entity, query).executeUpdate(); + return entityManager.find(ComponentDescriptorEntity.class, UUIDConverter.fromTimeUUID(entity.getId())); + } + + private static String getInsertString(String conflictStatement) { + return "MERGE INTO component_descriptor USING (VALUES :id, :actions, :clazz, :configuration_descriptor, :name, :scope, :search_text, :type) I (id, actions, clazz, configuration_descriptor, name, scope, search_text, type) ON " + conflictStatement + " WHEN MATCHED THEN UPDATE SET component_descriptor.id = I.id, component_descriptor.actions = I.actions, component_descriptor.clazz = I.clazz, component_descriptor.configuration_descriptor = I.configuration_descriptor, component_descriptor.name = I.name, component_descriptor.scope = I.scope, component_descriptor.search_text = I.search_text, component_descriptor.type = I.type" + + " WHEN NOT MATCHED THEN INSERT (id, actions, clazz, configuration_descriptor, name, scope, search_text, type) VALUES (I.id, I.actions, I.clazz, I.configuration_descriptor, I.name, I.scope, I.search_text, I.type)"; + } +} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java index c66ea9c5cc..657c02d4a6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java @@ -51,6 +51,9 @@ public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao getEntityClass() { return ComponentDescriptorEntity.class; @@ -67,7 +70,9 @@ public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao