diff --git a/application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql b/application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql index 18225f2134..0dd66256ea 100644 --- a/application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql +++ b/application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql @@ -75,7 +75,8 @@ CREATE OR REPLACE PROCEDURE drop_all_idx() $$ BEGIN DROP INDEX IF EXISTS idx_alarm_originator_alarm_type; - DROP INDEX IF EXISTS idx_alarm_originator_alarm_time; + DROP INDEX IF EXISTS idx_alarm_originator_created_time; + DROP INDEX IF EXISTS idx_alarm_tenant_created_time; DROP INDEX IF EXISTS idx_event_type_entity_id; DROP INDEX IF EXISTS idx_relation_to_id; DROP INDEX IF EXISTS idx_relation_from_id; @@ -93,7 +94,8 @@ CREATE OR REPLACE PROCEDURE create_all_idx() $$ BEGIN CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC); - CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_time ON alarm(originator_id, created_time DESC); + CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator_id, created_time DESC); + CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC); CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id); CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id); CREATE INDEX IF NOT EXISTS idx_relation_from_id ON relation(relation_type_group, from_type, from_id); diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java index f1f9c8f0c8..39c9c66a98 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java @@ -286,7 +286,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc int dynamicQueryInvocationCntValue = stats.getDynamicQueryInvocationCnt().getAndSet(0); long dynamicQueryInvocationTimeValue = stats.getDynamicQueryTimeSpent().getAndSet(0); long dynamicQueryCnt = subscriptionsBySessionId.values().stream().map(Map::values).count(); - if (regularQueryInvocationCntValue > 0 || dynamicQueryInvocationCntValue > 0 || dynamicQueryCnt > 0) { + if (regularQueryInvocationCntValue > 0 || dynamicQueryInvocationCntValue > 0 || dynamicQueryCnt > 0 || alarmQueryInvocationCntValue > 0) { log.info("Stats: regularQueryInvocationCnt = [{}], regularQueryInvocationTime = [{}], " + "dynamicQueryCnt = [{}] dynamicQueryInvocationCnt = [{}], dynamicQueryInvocationTime = [{}], " + "alarmQueryInvocationCnt = [{}], alarmQueryInvocationTime = [{}]", diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index b08ff7c20f..74262b034d 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -58,6 +58,9 @@ import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.state.DeviceStateService; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** @@ -92,7 +95,7 @@ public class DefaultTransportApiService implements TransportApiService { @Autowired protected TbClusterService tbClusterService; - private ReentrantLock deviceCreationLock = new ReentrantLock(); + private final ConcurrentMap deviceCreationLocks = new ConcurrentHashMap<>(); @Override public ListenableFuture> handle(TbProtoQueueMsg tbProtoQueueMsg) { @@ -125,6 +128,7 @@ public class DefaultTransportApiService implements TransportApiService { DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); ListenableFuture gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId); return Futures.transform(gatewayFuture, gateway -> { + Lock deviceCreationLock = deviceCreationLocks.computeIfAbsent(requestMsg.getDeviceName(), id -> new ReentrantLock()); deviceCreationLock.lock(); try { Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java index 6ceaa8dc04..97d43ee892 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java @@ -20,6 +20,9 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; @@ -53,7 +56,6 @@ import java.util.stream.Collectors; public class DefaultAlarmQueryRepository implements AlarmQueryRepository { private static final Map alarmFieldColumnMap = new HashMap<>(); - private static final List uniqueAlarmFields = new ArrayList<>(); static { alarmFieldColumnMap.put("createdTime", ModelConstants.CREATED_TIME_PROPERTY); @@ -72,11 +74,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { alarmFieldColumnMap.put("originator_id", ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY); alarmFieldColumnMap.put("originator_type", ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY); alarmFieldColumnMap.put("originator", "originator_name"); - - uniqueAlarmFields.addAll(new HashSet<>(alarmFieldColumnMap.values())); } - public static final String SELECT_ORIGINATOR_NAME = " CASE" + + private static final String SELECT_ORIGINATOR_NAME = " CASE" + " WHEN a.originator_type = " + EntityType.TENANT.ordinal() + " THEN (select title from tenant where id = a.originator_id)" + " WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() + @@ -93,7 +93,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { " THEN (select name from entity_view where id = a.originator_id)" + " END as originator_name"; - public static final String FIELDS_SELECTION = "select a.id as id," + + private static final String FIELDS_SELECTION = "select a.id as id," + " a.created_time as created_time," + " a.ack_ts as ack_ts," + " a.clear_ts as clear_ts," + @@ -109,130 +109,136 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { " a.propagate_relation_types as propagate_relation_types, " + " a.type as type," + SELECT_ORIGINATOR_NAME + ", "; - public static final String JOIN_RELATIONS = "left join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id and r.from_id in (:entity_ids)"; + private static final String JOIN_RELATIONS = "left join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id and r.from_id in (:entity_ids)"; - @Autowired - protected NamedParameterJdbcTemplate jdbcTemplate; + protected final NamedParameterJdbcTemplate jdbcTemplate; + private final TransactionTemplate transactionTemplate; + public DefaultAlarmQueryRepository(NamedParameterJdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) { + this.jdbcTemplate = jdbcTemplate; + this.transactionTemplate = transactionTemplate; + } @Override public PageData findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, AlarmDataQuery query, Collection orderedEntityIds) { - AlarmDataPageLink pageLink = query.getPageLink(); - QueryContext ctx = new QueryContext(); - ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList())); + return transactionTemplate.execute(status -> { + AlarmDataPageLink pageLink = query.getPageLink(); + QueryContext ctx = new QueryContext(); + ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList())); - StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION); - StringBuilder fromPart = new StringBuilder(" from alarm a "); - StringBuilder wherePart = new StringBuilder(" where "); - StringBuilder sortPart = new StringBuilder(" order by "); - boolean addAnd = false; - if (pageLink.isSearchPropagatedAlarms()) { - selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id "); - fromPart.append(JOIN_RELATIONS); - wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx)); - addAnd = true; - } else { - selectPart.append(" a.originator_id as entity_id "); - } - EntityDataSortOrder sortOrder = pageLink.getSortOrder(); - if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) { - String sortOrderKey = sortOrder.getKey().getKey(); - sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey)) - .append(" ").append(sortOrder.getDirection().name()); + StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION); + StringBuilder fromPart = new StringBuilder(" from alarm a "); + StringBuilder wherePart = new StringBuilder(" where "); + StringBuilder sortPart = new StringBuilder(" order by "); + boolean addAnd = false; if (pageLink.isSearchPropagatedAlarms()) { - wherePart.append(" and (a.originator_id in (:entity_ids) or r.from_id IS NOT NULL)"); - } else { - addAndIfNeeded(wherePart, addAnd); + selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id "); + fromPart.append(JOIN_RELATIONS); + wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx)); addAnd = true; - wherePart.append(" a.originator_id in (:entity_ids)"); + } else { + selectPart.append(" a.originator_id as entity_id "); } - } else { - fromPart.append(" left join (select * from (VALUES"); - int entityIdIdx = 0; - int lastEntityIdIdx = orderedEntityIds.size() - 1; - for (EntityId entityId : orderedEntityIds) { - fromPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")"); - if (entityIdIdx != lastEntityIdIdx) { - fromPart.append(","); + EntityDataSortOrder sortOrder = pageLink.getSortOrder(); + if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) { + String sortOrderKey = sortOrder.getKey().getKey(); + sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey)) + .append(" ").append(sortOrder.getDirection().name()); + if (pageLink.isSearchPropagatedAlarms()) { + wherePart.append(" and (a.originator_id in (:entity_ids) or r.from_id IS NOT NULL)"); } else { - fromPart.append(")"); + addAndIfNeeded(wherePart, addAnd); + addAnd = true; + wherePart.append(" a.originator_id in (:entity_ids)"); } - entityIdIdx++; - } - fromPart.append(" as e(id, priority)) e "); - if (pageLink.isSearchPropagatedAlarms()) { - fromPart.append("on (r.from_id IS NULL and a.originator_id = e.id) or (r.from_id IS NOT NULL and r.from_id = e.id)"); } else { - fromPart.append("on a.originator_id = e.id"); + fromPart.append(" left join (select * from (VALUES"); + int entityIdIdx = 0; + int lastEntityIdIdx = orderedEntityIds.size() - 1; + for (EntityId entityId : orderedEntityIds) { + fromPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")"); + if (entityIdIdx != lastEntityIdIdx) { + fromPart.append(","); + } else { + fromPart.append(")"); + } + entityIdIdx++; + } + fromPart.append(" as e(id, priority)) e "); + if (pageLink.isSearchPropagatedAlarms()) { + fromPart.append("on (r.from_id IS NULL and a.originator_id = e.id) or (r.from_id IS NOT NULL and r.from_id = e.id)"); + } else { + fromPart.append("on a.originator_id = e.id"); + } + sortPart.append("e.priority"); } - sortPart.append("e.priority"); - } - - long startTs; - long endTs; - if (pageLink.getTimeWindow() > 0) { - endTs = System.currentTimeMillis(); - startTs = endTs - pageLink.getTimeWindow(); - } else { - startTs = pageLink.getStartTs(); - endTs = pageLink.getEndTs(); - } - if (startTs > 0) { - addAndIfNeeded(wherePart, addAnd); - addAnd = true; - ctx.addLongParameter("startTime", startTs); - wherePart.append("a.created_time >= :startTime"); - } + long startTs; + long endTs; + if (pageLink.getTimeWindow() > 0) { + endTs = System.currentTimeMillis(); + startTs = endTs - pageLink.getTimeWindow(); + } else { + startTs = pageLink.getStartTs(); + endTs = pageLink.getEndTs(); + } - if (endTs > 0) { - addAndIfNeeded(wherePart, addAnd); - addAnd = true; - ctx.addLongParameter("endTime", endTs); - wherePart.append("a.created_time <= :endTime"); - } + if (startTs > 0) { + addAndIfNeeded(wherePart, addAnd); + addAnd = true; + ctx.addLongParameter("startTime", startTs); + wherePart.append("a.created_time >= :startTime"); + } - if (pageLink.getTypeList() != null && !pageLink.getTypeList().isEmpty()) { - addAndIfNeeded(wherePart, addAnd); - addAnd = true; - ctx.addStringListParameter("alarmTypes", pageLink.getTypeList()); - wherePart.append("a.type in (:alarmTypes)"); - } + if (endTs > 0) { + addAndIfNeeded(wherePart, addAnd); + addAnd = true; + ctx.addLongParameter("endTime", endTs); + wherePart.append("a.created_time <= :endTime"); + } - if (pageLink.getSeverityList() != null && !pageLink.getSeverityList().isEmpty()) { - addAndIfNeeded(wherePart, addAnd); - addAnd = true; - ctx.addStringListParameter("alarmSeverities", pageLink.getSeverityList().stream().map(AlarmSeverity::name).collect(Collectors.toList())); - wherePart.append("a.severity in (:alarmSeverities)"); - } + if (pageLink.getTypeList() != null && !pageLink.getTypeList().isEmpty()) { + addAndIfNeeded(wherePart, addAnd); + addAnd = true; + ctx.addStringListParameter("alarmTypes", pageLink.getTypeList()); + wherePart.append("a.type in (:alarmTypes)"); + } - if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) { - Set statusSet = toStatusSet(pageLink.getStatusList()); - if (!statusSet.isEmpty()) { + if (pageLink.getSeverityList() != null && !pageLink.getSeverityList().isEmpty()) { addAndIfNeeded(wherePart, addAnd); addAnd = true; - ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList())); - wherePart.append(" a.status in (:alarmStatuses)"); + ctx.addStringListParameter("alarmSeverities", pageLink.getSeverityList().stream().map(AlarmSeverity::name).collect(Collectors.toList())); + wherePart.append("a.severity in (:alarmSeverities)"); } - } - String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch()); - String mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString(); - if (!textSearchQuery.isEmpty()) { - mainQuery = String.format("select * from (%s) a WHERE %s", mainQuery, textSearchQuery); - } - String countQuery = mainQuery; - int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) from (%s) result", countQuery), ctx, Integer.class); + if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) { + Set statusSet = toStatusSet(pageLink.getStatusList()); + if (!statusSet.isEmpty()) { + addAndIfNeeded(wherePart, addAnd); + addAnd = true; + ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList())); + wherePart.append(" a.status in (:alarmStatuses)"); + } + } - String dataQuery = mainQuery + sortPart; + String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch()); + String mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString(); + if (!textSearchQuery.isEmpty()) { + mainQuery = String.format("select * from (%s) a WHERE %s", mainQuery, textSearchQuery); + } + String countQuery = mainQuery; + int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) from (%s) result", countQuery), ctx, Integer.class); - int startIndex = pageLink.getPageSize() * pageLink.getPage(); - if (pageLink.getPageSize() > 0) { - dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); - } - List> rows = jdbcTemplate.queryForList(dataQuery, ctx); - return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements, orderedEntityIds); + String dataQuery = mainQuery + sortPart; + + int startIndex = pageLink.getPageSize() * pageLink.getPage(); + if (pageLink.getPageSize() > 0) { + dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); + } + List> rows = jdbcTemplate.queryForList(dataQuery, ctx); + return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements, orderedEntityIds); + }); } private String buildTextSearchQuery(QueryContext ctx, List selectionMapping, String searchText) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index 53463325e5..a6a03531af 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -20,6 +20,9 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -58,37 +61,36 @@ import java.util.stream.Collectors; @Repository @Slf4j public class DefaultEntityQueryRepository implements EntityQueryRepository { - //TODO: rafactoring to protect from SQL injections; private static final Map entityTableMap = new HashMap<>(); - public static final String SELECT_PHONE = " CASE WHEN entity.entity_type = 'TENANT' THEN (select phone from tenant where id = entity_id)" + + private static final String SELECT_PHONE = " CASE WHEN entity.entity_type = 'TENANT' THEN (select phone from tenant where id = entity_id)" + " WHEN entity.entity_type = 'CUSTOMER' THEN (select phone from customer where id = entity_id) END as phone"; - public static final String SELECT_ZIP = " CASE WHEN entity.entity_type = 'TENANT' THEN (select zip from tenant where id = entity_id)" + + private static final String SELECT_ZIP = " CASE WHEN entity.entity_type = 'TENANT' THEN (select zip from tenant where id = entity_id)" + " WHEN entity.entity_type = 'CUSTOMER' THEN (select zip from customer where id = entity_id) END as zip"; - public static final String SELECT_ADDRESS_2 = " CASE WHEN entity.entity_type = 'TENANT'" + + private static final String SELECT_ADDRESS_2 = " CASE WHEN entity.entity_type = 'TENANT'" + " THEN (select address2 from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + " THEN (select address2 from customer where id = entity_id) END as address2"; - public static final String SELECT_ADDRESS = " CASE WHEN entity.entity_type = 'TENANT'" + + private static final String SELECT_ADDRESS = " CASE WHEN entity.entity_type = 'TENANT'" + " THEN (select address from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + " THEN (select address from customer where id = entity_id) END as address"; - public static final String SELECT_CITY = " CASE WHEN entity.entity_type = 'TENANT'" + + private static final String SELECT_CITY = " CASE WHEN entity.entity_type = 'TENANT'" + " THEN (select city from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + " THEN (select city from customer where id = entity_id) END as city"; - public static final String SELECT_STATE = " CASE WHEN entity.entity_type = 'TENANT'" + + private static final String SELECT_STATE = " CASE WHEN entity.entity_type = 'TENANT'" + " THEN (select state from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + " THEN (select state from customer where id = entity_id) END as state"; - public static final String SELECT_COUNTRY = " CASE WHEN entity.entity_type = 'TENANT'" + + private static final String SELECT_COUNTRY = " CASE WHEN entity.entity_type = 'TENANT'" + " THEN (select country from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + " THEN (select country from customer where id = entity_id) END as country"; - public static final String SELECT_TITLE = " CASE WHEN entity.entity_type = 'TENANT'" + + private static final String SELECT_TITLE = " CASE WHEN entity.entity_type = 'TENANT'" + " THEN (select title from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + " THEN (select title from customer where id = entity_id) END as title"; - public static final String SELECT_LAST_NAME = " CASE WHEN entity.entity_type = 'USER'" + + private static final String SELECT_LAST_NAME = " CASE WHEN entity.entity_type = 'USER'" + " THEN (select last_name from tb_user where id = entity_id) END as last_name"; - public static final String SELECT_FIRST_NAME = " CASE WHEN entity.entity_type = 'USER'" + + private static final String SELECT_FIRST_NAME = " CASE WHEN entity.entity_type = 'USER'" + " THEN (select first_name from tb_user where id = entity_id) END as first_name"; - public static final String SELECT_REGION = " CASE WHEN entity.entity_type = 'TENANT'" + + private static final String SELECT_REGION = " CASE WHEN entity.entity_type = 'TENANT'" + " THEN (select region from tenant where id = entity_id) END as region"; - public static final String SELECT_EMAIL = " CASE" + + private static final String SELECT_EMAIL = " CASE" + " WHEN entity.entity_type = 'TENANT'" + " THEN (select email from tenant where id = entity_id)" + " WHEN entity.entity_type = 'CUSTOMER' " + @@ -96,7 +98,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { " WHEN entity.entity_type = 'USER'" + " THEN (select email from tb_user where id = entity_id)" + " END as email"; - public static final String SELECT_CUSTOMER_ID = "CASE" + + private static final String SELECT_CUSTOMER_ID = "CASE" + " WHEN entity.entity_type = 'TENANT'" + " THEN UUID('" + TenantId.NULL_UUID + "')" + " WHEN entity.entity_type = 'CUSTOMER' THEN entity_id" + @@ -112,7 +114,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { " WHEN entity.entity_type = 'ENTITY_VIEW'" + " THEN (select customer_id from entity_view where id = entity_id)" + " END as customer_id"; - public static final String SELECT_TENANT_ID = "SELECT CASE" + + private static final String SELECT_TENANT_ID = "SELECT CASE" + " WHEN entity.entity_type = 'TENANT' THEN entity_id" + " WHEN entity.entity_type = 'CUSTOMER'" + " THEN (select tenant_id from customer where id = entity_id)" + @@ -127,7 +129,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { " WHEN entity.entity_type = 'ENTITY_VIEW'" + " THEN (select tenant_id from entity_view where id = entity_id)" + " END as tenant_id"; - public static final String SELECT_CREATED_TIME = " CASE" + + private static final String SELECT_CREATED_TIME = " CASE" + " WHEN entity.entity_type = 'TENANT'" + " THEN (select created_time from tenant where id = entity_id)" + " WHEN entity.entity_type = 'CUSTOMER' " + @@ -143,7 +145,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { " WHEN entity.entity_type = 'ENTITY_VIEW'" + " THEN (select created_time from entity_view where id = entity_id)" + " END as created_time"; - public static final String SELECT_NAME = " CASE" + + private static final String SELECT_NAME = " CASE" + " WHEN entity.entity_type = 'TENANT'" + " THEN (select title from tenant where id = entity_id)" + " WHEN entity.entity_type = 'CUSTOMER' " + @@ -159,7 +161,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { " WHEN entity.entity_type = 'ENTITY_VIEW'" + " THEN (select name from entity_view where id = entity_id)" + " END as name"; - public static final String SELECT_TYPE = " CASE" + + private static final String SELECT_TYPE = " CASE" + " WHEN entity.entity_type = 'USER'" + " THEN (select authority from tb_user where id = entity_id)" + " WHEN entity.entity_type = 'ASSET'" + @@ -169,7 +171,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { " WHEN entity.entity_type = 'ENTITY_VIEW'" + " THEN (select type from entity_view where id = entity_id)" + " ELSE entity.entity_type END as type"; - public static final String SELECT_LABEL = " CASE" + + private static final String SELECT_LABEL = " CASE" + " WHEN entity.entity_type = 'TENANT'" + " THEN (select title from tenant where id = entity_id)" + " WHEN entity.entity_type = 'CUSTOMER' " + @@ -196,7 +198,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { entityTableMap.put(EntityType.TENANT, "tenant"); } - public static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" + + private static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" + " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" + " FROM relation" + " WHERE $in_id = :relation_root_id and $in_type = :relation_root_type and relation_type_group = 'COMMON'" + @@ -209,11 +211,16 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { " SELECT re.$out_id entity_id, re.$out_type entity_type, re.lvl lvl" + " from related_entities re" + " %s ) entity"; - public static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from"); - public static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to"); + private static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from"); + private static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to"); - @Autowired - protected NamedParameterJdbcTemplate jdbcTemplate; + private final NamedParameterJdbcTemplate jdbcTemplate; + private final TransactionTemplate transactionTemplate; + + public DefaultEntityQueryRepository(NamedParameterJdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) { + this.jdbcTemplate = jdbcTemplate; + this.transactionTemplate = transactionTemplate; + } @Override public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { @@ -223,89 +230,91 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { ctx.append(addEntityTableQuery(ctx, query.getEntityFilter(), entityType)); ctx.append(" e where "); ctx.append(buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), Collections.emptyList(), entityType)); - return jdbcTemplate.queryForObject(ctx.getQuery(), ctx, Long.class); + return transactionTemplate.execute(status -> jdbcTemplate.queryForObject(ctx.getQuery(), ctx, Long.class)); } @Override public PageData findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { - QueryContext ctx = new QueryContext(); - EntityType entityType = resolveEntityType(query.getEntityFilter()); - EntityDataPageLink pageLink = query.getPageLink(); - - List mappings = EntityKeyMapping.prepareKeyMapping(query); - - List selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection) - .collect(Collectors.toList()); - List entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest()) - .collect(Collectors.toList()); - List latestSelectionMapping = selectionMapping.stream().filter(EntityKeyMapping::isLatest) - .collect(Collectors.toList()); - - List filterMapping = mappings.stream().filter(EntityKeyMapping::hasFilter) - .collect(Collectors.toList()); - List entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest()) - .collect(Collectors.toList()); - List latestFiltersMapping = filterMapping.stream().filter(EntityKeyMapping::isLatest) - .collect(Collectors.toList()); - - List allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest) - .collect(Collectors.toList()); - - - String entityWhereClause = this.buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType); - String latestJoins = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings); - String whereClause = this.buildWhere(ctx, latestFiltersMapping, query.getEntityFilter().getType(), entityType); - String textSearchQuery = this.buildTextSearchQuery(ctx, selectionMapping, pageLink.getTextSearch()); - String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType); - String entityTypeStr; - if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) { - entityTypeStr = "e.entity_type"; - } else { - entityTypeStr = "'" + entityType.name() + "'"; - } - if (!StringUtils.isEmpty(entityFieldsSelection)) { - entityFieldsSelection = String.format("e.id id, %s entity_type, %s", entityTypeStr, entityFieldsSelection); - } else { - entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr); - } - String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping, query.getEntityFilter().getType(), entityType); - String topSelection = "entities.*"; - if (!StringUtils.isEmpty(latestSelection)) { - topSelection = topSelection + ", " + latestSelection; - } + return transactionTemplate.execute(status -> { + QueryContext ctx = new QueryContext(); + EntityType entityType = resolveEntityType(query.getEntityFilter()); + EntityDataPageLink pageLink = query.getPageLink(); + + List mappings = EntityKeyMapping.prepareKeyMapping(query); + + List selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection) + .collect(Collectors.toList()); + List entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest()) + .collect(Collectors.toList()); + List latestSelectionMapping = selectionMapping.stream().filter(EntityKeyMapping::isLatest) + .collect(Collectors.toList()); + + List filterMapping = mappings.stream().filter(EntityKeyMapping::hasFilter) + .collect(Collectors.toList()); + List entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest()) + .collect(Collectors.toList()); + List latestFiltersMapping = filterMapping.stream().filter(EntityKeyMapping::isLatest) + .collect(Collectors.toList()); + + List allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest) + .collect(Collectors.toList()); + + + String entityWhereClause = DefaultEntityQueryRepository.this.buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType); + String latestJoins = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings); + String whereClause = DefaultEntityQueryRepository.this.buildWhere(ctx, latestFiltersMapping, query.getEntityFilter().getType(), entityType); + String textSearchQuery = DefaultEntityQueryRepository.this.buildTextSearchQuery(ctx, selectionMapping, pageLink.getTextSearch()); + String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType); + String entityTypeStr; + if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) { + entityTypeStr = "e.entity_type"; + } else { + entityTypeStr = "'" + entityType.name() + "'"; + } + if (!StringUtils.isEmpty(entityFieldsSelection)) { + entityFieldsSelection = String.format("e.id id, %s entity_type, %s", entityTypeStr, entityFieldsSelection); + } else { + entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr); + } + String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping, query.getEntityFilter().getType(), entityType); + String topSelection = "entities.*"; + if (!StringUtils.isEmpty(latestSelection)) { + topSelection = topSelection + ", " + latestSelection; + } - String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result %s", - topSelection, - entityFieldsSelection, - addEntityTableQuery(ctx, query.getEntityFilter(), entityType), - entityWhereClause, - latestJoins, - whereClause, - textSearchQuery); - - int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) %s", fromClause), ctx, Integer.class); - - String dataQuery = String.format("select * %s", fromClause); - - EntityDataSortOrder sortOrder = pageLink.getSortOrder(); - if (sortOrder != null) { - Optional sortOrderMappingOpt = mappings.stream().filter(EntityKeyMapping::isSortOrder).findFirst(); - if (sortOrderMappingOpt.isPresent()) { - EntityKeyMapping sortOrderMapping = sortOrderMappingOpt.get(); - dataQuery = String.format("%s order by %s", dataQuery, sortOrderMapping.getValueAlias()); - if (sortOrder.getDirection() == EntityDataSortOrder.Direction.ASC) { - dataQuery += " asc"; - } else { - dataQuery += " desc"; + String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result %s", + topSelection, + entityFieldsSelection, + addEntityTableQuery(ctx, query.getEntityFilter(), entityType), + entityWhereClause, + latestJoins, + whereClause, + textSearchQuery); + + int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) %s", fromClause), ctx, Integer.class); + + String dataQuery = String.format("select * %s", fromClause); + + EntityDataSortOrder sortOrder = pageLink.getSortOrder(); + if (sortOrder != null) { + Optional sortOrderMappingOpt = mappings.stream().filter(EntityKeyMapping::isSortOrder).findFirst(); + if (sortOrderMappingOpt.isPresent()) { + EntityKeyMapping sortOrderMapping = sortOrderMappingOpt.get(); + dataQuery = String.format("%s order by %s", dataQuery, sortOrderMapping.getValueAlias()); + if (sortOrder.getDirection() == EntityDataSortOrder.Direction.ASC) { + dataQuery += " asc"; + } else { + dataQuery += " desc"; + } } } - } - int startIndex = pageLink.getPageSize() * pageLink.getPage(); - if (pageLink.getPageSize() > 0) { - dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); - } - List> rows = jdbcTemplate.queryForList(dataQuery, ctx); - return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements); + int startIndex = pageLink.getPageSize() * pageLink.getPage(); + if (pageLink.getPageSize() > 0) { + dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); + } + List> rows = jdbcTemplate.queryForList(dataQuery, ctx); + return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements); + }); } private String buildEntityWhere(QueryContext ctx, diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 4d16d838d7..c1490415ac 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -16,7 +16,9 @@ CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC); -CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_time ON alarm(originator_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator_id, created_time DESC); + +CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC); CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html index ed0950979b..20659922d1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html @@ -119,7 +119,7 @@ - { loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { this.dataLoading = true; - this.clear(); + // this.clear(); this.appliedPageLink = pageLink; this.appliedSortOrderLabel = sortOrderLabel; this.subscription.subscribeForAlarms(pageLink, keyFilters); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html index c169673ae1..8574076acd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html @@ -82,7 +82,7 @@ - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts index 142edcfe64..5bf040ed0d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts @@ -87,6 +87,7 @@ import { import { sortItems } from '@shared/models/page/page-link'; import { entityFields } from '@shared/models/entity.models'; import { DatePipe } from '@angular/common'; +import { alarmFields } from '@shared/models/alarm.models'; interface EntitiesTableWidgetSettings extends TableWidgetSettings { entitiesTitle: string; @@ -348,6 +349,13 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); dataKey.def = 'def' + this.columns.length; const keySettings: TableWidgetDataKeySettings = dataKey.settings; + if (dataKey.type === DataKeyType.entityField && + !isDefined(keySettings.columnWidth) || keySettings.columnWidth === '0px') { + const entityField = entityFields[dataKey.name]; + if (entityField && entityField.time) { + keySettings.columnWidth = '120px'; + } + } this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings); this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx'); @@ -595,7 +603,7 @@ class EntityDatasource implements DataSource { loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { this.dataLoading = true; - this.clear(); + // this.clear(); this.appliedPageLink = pageLink; this.appliedSortOrderLabel = sortOrderLabel; this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters);