diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java index 4f0af49f41..704210e4cc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java @@ -36,5 +36,5 @@ public interface AttributesDao { ListenableFuture save(EntityId entityId, String attributeType, AttributeKvEntry attribute); - ListenableFuture> removeAll(EntityId entityId, String scope, List keys); + ListenableFuture> removeAll(EntityId entityId, String attributeType, List keys); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributesKvCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvCompositeKey.java similarity index 83% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributesKvCompositeKey.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvCompositeKey.java index 219acb5c39..7424e1fb89 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributesKvCompositeKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvCompositeKey.java @@ -15,13 +15,17 @@ */ package org.thingsboard.server.dao.model.sql; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.UUID; @Data -public class AttributesKvCompositeKey implements Serializable { +@AllArgsConstructor +@NoArgsConstructor +public class AttributeKvCompositeKey implements Serializable { private String entityType; private UUID entityId; private String attributeType; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java index 90bf2ab64a..2ea58afcf2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java @@ -16,21 +16,20 @@ package org.thingsboard.server.dao.model.sql; import lombok.Data; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.*; import org.thingsboard.server.dao.model.ToData; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; +import javax.persistence.*; +import java.io.Serializable; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.*; @Data @Entity -@Table(name = "attributes_kv") -public class AttributeKvEntity implements ToData { +@Table(name = "attribute_kv") +@IdClass(AttributeKvCompositeKey.class) +public class AttributeKvEntity implements ToData, Serializable { @Id @Column(name = ENTITY_TYPE_COLUMN) @@ -65,7 +64,16 @@ public class AttributeKvEntity implements ToData { @Override public AttributeKvEntry toData() { - // TODO - add implementation - return null; + KvEntry kvEntry = null; + if (strValue != null) { + kvEntry = new StringDataEntry(attributeKey, strValue); + } else if (booleanValue != null) { + kvEntry = new BooleanDataEntry(attributeKey, booleanValue); + } else if (doubleValue != null) { + kvEntry = new DoubleDataEntry(attributeKey, doubleValue); + } else if (longValue != null) { + kvEntry = new LongDataEntry(attributeKey, longValue); + } + return new BaseAttributeKvEntry(kvEntry, lastUpdateTs); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index aeaac2575d..a911711c98 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -38,7 +38,9 @@ import static org.springframework.transaction.annotation.Propagation.REQUIRES_NE * @author Valerii Sosliuk */ @Slf4j -public abstract class JpaAbstractDao, D> implements Dao { +public abstract class JpaAbstractDao, D> + extends JpaAbstractDaoListeningExecutorService + implements Dao { protected abstract Class getEntityClass(); @@ -48,8 +50,6 @@ public abstract class JpaAbstractDao, D> implements Dao< return false; } - protected ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); - @Override @Transactional(propagation = REQUIRES_NEW) public D save(D domain) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java new file mode 100644 index 0000000000..47df102216 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2017 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; + +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +import java.util.concurrent.Executors; + +public abstract class JpaAbstractDaoListeningExecutorService { + protected ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java index ecb5fbe8b1..a231f8733e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java @@ -17,9 +17,17 @@ package org.thingsboard.server.dao.sql.attributes; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; import org.thingsboard.server.dao.model.sql.AttributeKvEntity; -import org.thingsboard.server.dao.model.sql.AttributesKvCompositeKey; + +import java.util.List; +import java.util.UUID; @ConditionalOnProperty(prefix = "sql", value = "enabled", havingValue = "true") -public interface AttributeKvRepository extends CrudRepository { +public interface AttributeKvRepository extends CrudRepository { + + List findAllByEntityTypeAndEntityIdAndAttributeType(String entityType, + UUID entityId, + String attributeType); } + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index eb02acafcd..32603359d6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.sql.attributes; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -22,42 +23,97 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.attributes.AttributesDao; +import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; +import org.thingsboard.server.dao.model.sql.AttributeKvEntity; +import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Component @Slf4j @ConditionalOnProperty(prefix = "sql", value = "enabled", havingValue = "true") -public class JpaAttributeDao implements AttributesDao { +public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService implements AttributesDao { @Autowired private AttributeKvRepository attributeKvRepository; @Override public ListenableFuture> find(EntityId entityId, String attributeType, String attributeKey) { - return null; + AttributeKvCompositeKey compositeKey = + new AttributeKvCompositeKey( + entityId.getEntityType().name(), + entityId.getId(), + attributeType, + attributeKey); + return service.submit(() -> + Optional.of(DaoUtil.getData(attributeKvRepository.findOne(compositeKey)))); } @Override - public ListenableFuture> find(EntityId entityId, String attributeType, Collection attributeKey) { - return null; + public ListenableFuture> find(EntityId entityId, String attributeType, Collection attributeKeys) { + List compositeKeys = + attributeKeys + .stream() + .map(attributeKey -> + new AttributeKvCompositeKey( + entityId.getEntityType().name(), + entityId.getId(), + attributeType, + attributeKey)) + .collect(Collectors.toList()); + return service.submit(() -> + DaoUtil.convertDataList(Lists.newArrayList(attributeKvRepository.findAll(compositeKeys)))); } @Override public ListenableFuture> findAll(EntityId entityId, String attributeType) { - return null; + return service.submit(() -> + DaoUtil.convertDataList(Lists.newArrayList( + attributeKvRepository.findAllByEntityTypeAndEntityIdAndAttributeType( + entityId.getEntityType().name(), + entityId.getId(), + attributeType)))); } @Override public ListenableFuture save(EntityId entityId, String attributeType, AttributeKvEntry attribute) { - return null; + AttributeKvEntity entity = new AttributeKvEntity(); + entity.setEntityType(entityId.getEntityType().name()); + entity.setEntityId(entityId.getId()); + entity.setAttributeType(attributeType); + entity.setAttributeKey(attribute.getKey()); + entity.setLastUpdateTs(attribute.getLastUpdateTs()); + entity.setStrValue(attribute.getStrValue().orElse(null)); + entity.setDoubleValue(attribute.getDoubleValue().orElse(null)); + entity.setLongValue(attribute.getLongValue().orElse(null)); + entity.setBooleanValue(attribute.getBooleanValue().orElse(null)); + return service.submit(() -> { + attributeKvRepository.save(entity); + return null; + }); } @Override - public ListenableFuture> removeAll(EntityId entityId, String scope, List keys) { - return null; + public ListenableFuture> removeAll(EntityId entityId, String attributeType, List keys) { + List entitiesToDelete = keys + .stream() + .map(key -> { + AttributeKvEntity entityToDelete = new AttributeKvEntity(); + entityToDelete.setEntityType(entityId.getEntityType().name()); + entityToDelete.setEntityId(entityId.getId()); + entityToDelete.setAttributeType(attributeType); + entityToDelete.setAttributeKey(key); + return entityToDelete; + }).collect(Collectors.toList()); + + return service.submit(() -> { + attributeKvRepository.delete(entitiesToDelete); + return null; + }); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java index 3dadbf00cc..db33808eb8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java @@ -16,8 +16,6 @@ package org.thingsboard.server.dao.sql.relation; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -36,6 +34,7 @@ import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.RelationCompositeKey; import org.thingsboard.server.dao.model.sql.RelationEntity; import org.thingsboard.server.dao.relation.RelationDao; +import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; import org.thingsboard.server.dao.sql.JpaAbstractSearchTimeDao; import javax.persistence.criteria.CriteriaBuilder; @@ -44,7 +43,6 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Executors; import static org.springframework.data.domain.Sort.Direction.ASC; import static org.springframework.data.jpa.domain.Specifications.where; @@ -56,16 +54,14 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; @Slf4j @Component @ConditionalOnProperty(prefix = "sql", value = "enabled", havingValue = "true", matchIfMissing = false) -public class JpaRelationDao implements RelationDao { +public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService implements RelationDao { @Autowired private RelationRepository relationRepository; - private ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); - @Override public ListenableFuture> findAllByFrom(EntityId from, RelationTypeGroup typeGroup) { - return executorService.submit(() -> DaoUtil.convertDataList( + return service.submit(() -> DaoUtil.convertDataList( relationRepository.findAllByFromIdAndFromTypeAndRelationTypeGroup( from.getId(), from.getEntityType().name(), @@ -74,7 +70,7 @@ public class JpaRelationDao implements RelationDao { @Override public ListenableFuture> findAllByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { - return executorService.submit(() -> DaoUtil.convertDataList( + return service.submit(() -> DaoUtil.convertDataList( relationRepository.findAllByFromIdAndFromTypeAndRelationTypeAndRelationTypeGroup( from.getId(), from.getEntityType().name(), @@ -84,7 +80,7 @@ public class JpaRelationDao implements RelationDao { @Override public ListenableFuture> findAllByTo(EntityId to, RelationTypeGroup typeGroup) { - return executorService.submit(() -> DaoUtil.convertDataList( + return service.submit(() -> DaoUtil.convertDataList( relationRepository.findAllByToIdAndToTypeAndRelationTypeGroup( to.getId(), to.getEntityType().name(), @@ -93,7 +89,7 @@ public class JpaRelationDao implements RelationDao { @Override public ListenableFuture> findAllByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { - return executorService.submit(() -> DaoUtil.convertDataList( + return service.submit(() -> DaoUtil.convertDataList( relationRepository.findAllByToIdAndToTypeAndRelationTypeAndRelationTypeGroup( to.getId(), to.getEntityType().name(), @@ -110,18 +106,18 @@ public class JpaRelationDao implements RelationDao { to.getEntityType().name(), relationType, typeGroup.name()); - return executorService.submit(() -> relationRepository.findOne(key) != null); + return service.submit(() -> relationRepository.findOne(key) != null); } @Override public ListenableFuture saveRelation(EntityRelation relation) { - return executorService.submit(() -> relationRepository.save(new RelationEntity(relation)) != null); + return service.submit(() -> relationRepository.save(new RelationEntity(relation)) != null); } @Override public ListenableFuture deleteRelation(EntityRelation relation) { RelationCompositeKey key = new RelationCompositeKey(relation); - return executorService.submit( + return service.submit( () -> { boolean relationExistsBeforeDelete = relationRepository.exists(key); relationRepository.delete(key); @@ -138,7 +134,7 @@ public class JpaRelationDao implements RelationDao { to.getEntityType().name(), relationType, typeGroup.name()); - return executorService.submit( + return service.submit( () -> { boolean relationExistsBeforeDelete = relationRepository.exists(key); relationRepository.delete(key); @@ -152,7 +148,7 @@ public class JpaRelationDao implements RelationDao { relationEntity.setFromId(entity.getId()); relationEntity.setFromType(entity.getEntityType().name()); - return executorService.submit( + return service.submit( () -> { boolean relationExistsBeforeDelete = relationRepository .findAllByFromIdAndFromType(relationEntity.getFromId(), relationEntity.getFromType()) @@ -172,7 +168,7 @@ public class JpaRelationDao implements RelationDao { new Order(ASC, RELATION_TYPE_PROPERTY), new Order(ASC, RELATION_TO_TYPE_PROPERTY)) ); - return executorService.submit(() -> + return service.submit(() -> DaoUtil.convertDataList(relationRepository.findAll(where(timeSearchSpec).and(fieldsSpec), pageable).getContent())); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/JsonBinaryType.java b/dao/src/main/java/org/thingsboard/server/dao/util/JsonBinaryType.java index 96a49c9a39..81d776b3de 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/JsonBinaryType.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/JsonBinaryType.java @@ -23,7 +23,7 @@ import java.util.Properties; /** * Created by Valerii Sosliuk on 5/12/2017. */ -public class JsonBinaryType extends AbstractSingleColumnStandardBasicType +public class JsonBinaryType extends AbstractSingleColumnStandardBasicType implements DynamicParameterizedType { public JsonBinaryType() { diff --git a/dao/src/main/resources/postgres/schema.sql b/dao/src/main/resources/postgres/schema.sql index 684ec00956..e78199fdf0 100644 --- a/dao/src/main/resources/postgres/schema.sql +++ b/dao/src/main/resources/postgres/schema.sql @@ -74,7 +74,7 @@ CREATE TABLE IF NOT EXISTS asset ( ); ALTER TABLE asset OWNER TO postgres; -CREATE TABLE IF NOT EXISTS attributes_kv ( +CREATE TABLE IF NOT EXISTS attribute_kv ( entity_type character varying(255), entity_id uuid, attribute_type character varying(255), @@ -84,7 +84,7 @@ CREATE TABLE IF NOT EXISTS attributes_kv ( long_v bigint, dbl_v double precision, last_update_ts bigint, - CONSTRAINT attributes_kv_unq_key UNIQUE (entity_type, entity_id, attribute_type, attribute_key) + CONSTRAINT attribute_kv_unq_key UNIQUE (entity_type, entity_id, attribute_type, attribute_key) ); ALTER TABLE relation OWNER TO postgres; diff --git a/dao/src/test/java/org/thingsboard/server/dao/AbstractJpaDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/AbstractJpaDaoTest.java index c8e715fdaa..daf59f319f 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/AbstractJpaDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/AbstractJpaDaoTest.java @@ -37,6 +37,6 @@ import org.springframework.test.context.support.DirtiesContextTestExecutionListe DirtiesContextTestExecutionListener.class, DbUnitTestExecutionListener.class }) @DbUnitConfiguration(databaseConnection = "dbUnitDatabaseConnection") -public class AbstractJpaDaoTest extends AbstractTransactionalJUnit4SpringContextTests { +public abstract class AbstractJpaDaoTest extends AbstractTransactionalJUnit4SpringContextTests { } diff --git a/dao/src/test/java/org/thingsboard/server/dao/DaoTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/DaoTestSuite.java index 26d92237c1..845c4c67c8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/DaoTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/DaoTestSuite.java @@ -38,8 +38,7 @@ public class DaoTestSuite { public static CustomCassandraCQLUnit cassandraUnit = new CustomCassandraCQLUnit( Arrays.asList(new ClassPathCQLDataSet("cassandra/schema.cql", false, false), - new ClassPathCQLDataSet("cassandra/system-data.cql", false, false), - new ClassPathCQLDataSet("system-test.cql", false, false)), + new ClassPathCQLDataSet("cassandra/system-data.cql", false, false), + new ClassPathCQLDataSet("system-test.cql", false, false)), "cassandra-test.yaml", 30000l); - } diff --git a/dao/src/test/java/org/thingsboard/server/dao/attributes/BaseAttributesServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/attributes/BaseAttributesServiceTest.java index 559a3130e8..61b5329ffc 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/attributes/BaseAttributesServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/attributes/BaseAttributesServiceTest.java @@ -16,6 +16,10 @@ package org.thingsboard.server.dao.attributes; import com.datastax.driver.core.utils.UUIDs; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; @@ -23,20 +27,11 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.dao.service.AbstractServiceTest; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import java.util.Collections; import java.util.List; import java.util.Optional; -import static org.thingsboard.server.common.data.DataConstants.CLIENT_SCOPE; -import static org.thingsboard.server.common.data.DataConstants.DEVICE; - /** * @author Andrew Shvayka */