Showing
10 changed files
with
250 additions
and
214 deletions
@@ -75,7 +75,8 @@ CREATE OR REPLACE PROCEDURE drop_all_idx() | @@ -75,7 +75,8 @@ CREATE OR REPLACE PROCEDURE drop_all_idx() | ||
75 | $$ | 75 | $$ |
76 | BEGIN | 76 | BEGIN |
77 | DROP INDEX IF EXISTS idx_alarm_originator_alarm_type; | 77 | DROP INDEX IF EXISTS idx_alarm_originator_alarm_type; |
78 | - DROP INDEX IF EXISTS idx_alarm_originator_alarm_time; | 78 | + DROP INDEX IF EXISTS idx_alarm_originator_created_time; |
79 | + DROP INDEX IF EXISTS idx_alarm_tenant_created_time; | ||
79 | DROP INDEX IF EXISTS idx_event_type_entity_id; | 80 | DROP INDEX IF EXISTS idx_event_type_entity_id; |
80 | DROP INDEX IF EXISTS idx_relation_to_id; | 81 | DROP INDEX IF EXISTS idx_relation_to_id; |
81 | DROP INDEX IF EXISTS idx_relation_from_id; | 82 | DROP INDEX IF EXISTS idx_relation_from_id; |
@@ -93,7 +94,8 @@ CREATE OR REPLACE PROCEDURE create_all_idx() | @@ -93,7 +94,8 @@ CREATE OR REPLACE PROCEDURE create_all_idx() | ||
93 | $$ | 94 | $$ |
94 | BEGIN | 95 | BEGIN |
95 | CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC); | 96 | CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC); |
96 | - CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_time ON alarm(originator_id, created_time DESC); | 97 | + CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator_id, created_time DESC); |
98 | + CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC); | ||
97 | CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id); | 99 | CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id); |
98 | CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id); | 100 | CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id); |
99 | CREATE INDEX IF NOT EXISTS idx_relation_from_id ON relation(relation_type_group, from_type, from_id); | 101 | CREATE INDEX IF NOT EXISTS idx_relation_from_id ON relation(relation_type_group, from_type, from_id); |
@@ -286,7 +286,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | @@ -286,7 +286,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | ||
286 | int dynamicQueryInvocationCntValue = stats.getDynamicQueryInvocationCnt().getAndSet(0); | 286 | int dynamicQueryInvocationCntValue = stats.getDynamicQueryInvocationCnt().getAndSet(0); |
287 | long dynamicQueryInvocationTimeValue = stats.getDynamicQueryTimeSpent().getAndSet(0); | 287 | long dynamicQueryInvocationTimeValue = stats.getDynamicQueryTimeSpent().getAndSet(0); |
288 | long dynamicQueryCnt = subscriptionsBySessionId.values().stream().map(Map::values).count(); | 288 | long dynamicQueryCnt = subscriptionsBySessionId.values().stream().map(Map::values).count(); |
289 | - if (regularQueryInvocationCntValue > 0 || dynamicQueryInvocationCntValue > 0 || dynamicQueryCnt > 0) { | 289 | + if (regularQueryInvocationCntValue > 0 || dynamicQueryInvocationCntValue > 0 || dynamicQueryCnt > 0 || alarmQueryInvocationCntValue > 0) { |
290 | log.info("Stats: regularQueryInvocationCnt = [{}], regularQueryInvocationTime = [{}], " + | 290 | log.info("Stats: regularQueryInvocationCnt = [{}], regularQueryInvocationTime = [{}], " + |
291 | "dynamicQueryCnt = [{}] dynamicQueryInvocationCnt = [{}], dynamicQueryInvocationTime = [{}], " + | 291 | "dynamicQueryCnt = [{}] dynamicQueryInvocationCnt = [{}], dynamicQueryInvocationTime = [{}], " + |
292 | "alarmQueryInvocationCnt = [{}], alarmQueryInvocationTime = [{}]", | 292 | "alarmQueryInvocationCnt = [{}], alarmQueryInvocationTime = [{}]", |
@@ -58,6 +58,9 @@ import org.thingsboard.server.service.queue.TbClusterService; | @@ -58,6 +58,9 @@ import org.thingsboard.server.service.queue.TbClusterService; | ||
58 | import org.thingsboard.server.service.state.DeviceStateService; | 58 | import org.thingsboard.server.service.state.DeviceStateService; |
59 | 59 | ||
60 | import java.util.UUID; | 60 | import java.util.UUID; |
61 | +import java.util.concurrent.ConcurrentHashMap; | ||
62 | +import java.util.concurrent.ConcurrentMap; | ||
63 | +import java.util.concurrent.locks.Lock; | ||
61 | import java.util.concurrent.locks.ReentrantLock; | 64 | import java.util.concurrent.locks.ReentrantLock; |
62 | 65 | ||
63 | /** | 66 | /** |
@@ -92,7 +95,7 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -92,7 +95,7 @@ public class DefaultTransportApiService implements TransportApiService { | ||
92 | @Autowired | 95 | @Autowired |
93 | protected TbClusterService tbClusterService; | 96 | protected TbClusterService tbClusterService; |
94 | 97 | ||
95 | - private ReentrantLock deviceCreationLock = new ReentrantLock(); | 98 | + private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); |
96 | 99 | ||
97 | @Override | 100 | @Override |
98 | public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) { | 101 | public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) { |
@@ -125,6 +128,7 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -125,6 +128,7 @@ public class DefaultTransportApiService implements TransportApiService { | ||
125 | DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); | 128 | DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); |
126 | ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId); | 129 | ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId); |
127 | return Futures.transform(gatewayFuture, gateway -> { | 130 | return Futures.transform(gatewayFuture, gateway -> { |
131 | + Lock deviceCreationLock = deviceCreationLocks.computeIfAbsent(requestMsg.getDeviceName(), id -> new ReentrantLock()); | ||
128 | deviceCreationLock.lock(); | 132 | deviceCreationLock.lock(); |
129 | try { | 133 | try { |
130 | Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName()); | 134 | Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName()); |
@@ -20,6 +20,9 @@ import org.apache.commons.lang3.StringUtils; | @@ -20,6 +20,9 @@ import org.apache.commons.lang3.StringUtils; | ||
20 | import org.springframework.beans.factory.annotation.Autowired; | 20 | import org.springframework.beans.factory.annotation.Autowired; |
21 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; | 21 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; |
22 | import org.springframework.stereotype.Repository; | 22 | import org.springframework.stereotype.Repository; |
23 | +import org.springframework.transaction.TransactionStatus; | ||
24 | +import org.springframework.transaction.support.TransactionCallback; | ||
25 | +import org.springframework.transaction.support.TransactionTemplate; | ||
23 | import org.thingsboard.server.common.data.EntityType; | 26 | import org.thingsboard.server.common.data.EntityType; |
24 | import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; | 27 | import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; |
25 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; | 28 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
@@ -53,7 +56,6 @@ import java.util.stream.Collectors; | @@ -53,7 +56,6 @@ import java.util.stream.Collectors; | ||
53 | public class DefaultAlarmQueryRepository implements AlarmQueryRepository { | 56 | public class DefaultAlarmQueryRepository implements AlarmQueryRepository { |
54 | 57 | ||
55 | private static final Map<String, String> alarmFieldColumnMap = new HashMap<>(); | 58 | private static final Map<String, String> alarmFieldColumnMap = new HashMap<>(); |
56 | - private static final List<String> uniqueAlarmFields = new ArrayList<>(); | ||
57 | 59 | ||
58 | static { | 60 | static { |
59 | alarmFieldColumnMap.put("createdTime", ModelConstants.CREATED_TIME_PROPERTY); | 61 | alarmFieldColumnMap.put("createdTime", ModelConstants.CREATED_TIME_PROPERTY); |
@@ -72,11 +74,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { | @@ -72,11 +74,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { | ||
72 | alarmFieldColumnMap.put("originator_id", ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY); | 74 | alarmFieldColumnMap.put("originator_id", ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY); |
73 | alarmFieldColumnMap.put("originator_type", ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY); | 75 | alarmFieldColumnMap.put("originator_type", ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY); |
74 | alarmFieldColumnMap.put("originator", "originator_name"); | 76 | alarmFieldColumnMap.put("originator", "originator_name"); |
75 | - | ||
76 | - uniqueAlarmFields.addAll(new HashSet<>(alarmFieldColumnMap.values())); | ||
77 | } | 77 | } |
78 | 78 | ||
79 | - public static final String SELECT_ORIGINATOR_NAME = " CASE" + | 79 | + private static final String SELECT_ORIGINATOR_NAME = " CASE" + |
80 | " WHEN a.originator_type = " + EntityType.TENANT.ordinal() + | 80 | " WHEN a.originator_type = " + EntityType.TENANT.ordinal() + |
81 | " THEN (select title from tenant where id = a.originator_id)" + | 81 | " THEN (select title from tenant where id = a.originator_id)" + |
82 | " WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() + | 82 | " WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() + |
@@ -93,7 +93,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { | @@ -93,7 +93,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { | ||
93 | " THEN (select name from entity_view where id = a.originator_id)" + | 93 | " THEN (select name from entity_view where id = a.originator_id)" + |
94 | " END as originator_name"; | 94 | " END as originator_name"; |
95 | 95 | ||
96 | - public static final String FIELDS_SELECTION = "select a.id as id," + | 96 | + private static final String FIELDS_SELECTION = "select a.id as id," + |
97 | " a.created_time as created_time," + | 97 | " a.created_time as created_time," + |
98 | " a.ack_ts as ack_ts," + | 98 | " a.ack_ts as ack_ts," + |
99 | " a.clear_ts as clear_ts," + | 99 | " a.clear_ts as clear_ts," + |
@@ -109,130 +109,136 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { | @@ -109,130 +109,136 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { | ||
109 | " a.propagate_relation_types as propagate_relation_types, " + | 109 | " a.propagate_relation_types as propagate_relation_types, " + |
110 | " a.type as type," + SELECT_ORIGINATOR_NAME + ", "; | 110 | " a.type as type," + SELECT_ORIGINATOR_NAME + ", "; |
111 | 111 | ||
112 | - 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)"; | 112 | + 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)"; |
113 | 113 | ||
114 | - @Autowired | ||
115 | - protected NamedParameterJdbcTemplate jdbcTemplate; | 114 | + protected final NamedParameterJdbcTemplate jdbcTemplate; |
115 | + private final TransactionTemplate transactionTemplate; | ||
116 | 116 | ||
117 | + public DefaultAlarmQueryRepository(NamedParameterJdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) { | ||
118 | + this.jdbcTemplate = jdbcTemplate; | ||
119 | + this.transactionTemplate = transactionTemplate; | ||
120 | + } | ||
117 | 121 | ||
118 | @Override | 122 | @Override |
119 | public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, | 123 | public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, |
120 | AlarmDataQuery query, Collection<EntityId> orderedEntityIds) { | 124 | AlarmDataQuery query, Collection<EntityId> orderedEntityIds) { |
121 | - AlarmDataPageLink pageLink = query.getPageLink(); | ||
122 | - QueryContext ctx = new QueryContext(); | ||
123 | - ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList())); | 125 | + return transactionTemplate.execute(status -> { |
126 | + AlarmDataPageLink pageLink = query.getPageLink(); | ||
127 | + QueryContext ctx = new QueryContext(); | ||
128 | + ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList())); | ||
124 | 129 | ||
125 | - StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION); | ||
126 | - StringBuilder fromPart = new StringBuilder(" from alarm a "); | ||
127 | - StringBuilder wherePart = new StringBuilder(" where "); | ||
128 | - StringBuilder sortPart = new StringBuilder(" order by "); | ||
129 | - boolean addAnd = false; | ||
130 | - if (pageLink.isSearchPropagatedAlarms()) { | ||
131 | - selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id "); | ||
132 | - fromPart.append(JOIN_RELATIONS); | ||
133 | - wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx)); | ||
134 | - addAnd = true; | ||
135 | - } else { | ||
136 | - selectPart.append(" a.originator_id as entity_id "); | ||
137 | - } | ||
138 | - EntityDataSortOrder sortOrder = pageLink.getSortOrder(); | ||
139 | - if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) { | ||
140 | - String sortOrderKey = sortOrder.getKey().getKey(); | ||
141 | - sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey)) | ||
142 | - .append(" ").append(sortOrder.getDirection().name()); | 130 | + StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION); |
131 | + StringBuilder fromPart = new StringBuilder(" from alarm a "); | ||
132 | + StringBuilder wherePart = new StringBuilder(" where "); | ||
133 | + StringBuilder sortPart = new StringBuilder(" order by "); | ||
134 | + boolean addAnd = false; | ||
143 | if (pageLink.isSearchPropagatedAlarms()) { | 135 | if (pageLink.isSearchPropagatedAlarms()) { |
144 | - wherePart.append(" and (a.originator_id in (:entity_ids) or r.from_id IS NOT NULL)"); | ||
145 | - } else { | ||
146 | - addAndIfNeeded(wherePart, addAnd); | 136 | + selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id "); |
137 | + fromPart.append(JOIN_RELATIONS); | ||
138 | + wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx)); | ||
147 | addAnd = true; | 139 | addAnd = true; |
148 | - wherePart.append(" a.originator_id in (:entity_ids)"); | 140 | + } else { |
141 | + selectPart.append(" a.originator_id as entity_id "); | ||
149 | } | 142 | } |
150 | - } else { | ||
151 | - fromPart.append(" left join (select * from (VALUES"); | ||
152 | - int entityIdIdx = 0; | ||
153 | - int lastEntityIdIdx = orderedEntityIds.size() - 1; | ||
154 | - for (EntityId entityId : orderedEntityIds) { | ||
155 | - fromPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")"); | ||
156 | - if (entityIdIdx != lastEntityIdIdx) { | ||
157 | - fromPart.append(","); | 143 | + EntityDataSortOrder sortOrder = pageLink.getSortOrder(); |
144 | + if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) { | ||
145 | + String sortOrderKey = sortOrder.getKey().getKey(); | ||
146 | + sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey)) | ||
147 | + .append(" ").append(sortOrder.getDirection().name()); | ||
148 | + if (pageLink.isSearchPropagatedAlarms()) { | ||
149 | + wherePart.append(" and (a.originator_id in (:entity_ids) or r.from_id IS NOT NULL)"); | ||
158 | } else { | 150 | } else { |
159 | - fromPart.append(")"); | 151 | + addAndIfNeeded(wherePart, addAnd); |
152 | + addAnd = true; | ||
153 | + wherePart.append(" a.originator_id in (:entity_ids)"); | ||
160 | } | 154 | } |
161 | - entityIdIdx++; | ||
162 | - } | ||
163 | - fromPart.append(" as e(id, priority)) e "); | ||
164 | - if (pageLink.isSearchPropagatedAlarms()) { | ||
165 | - 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)"); | ||
166 | } else { | 155 | } else { |
167 | - fromPart.append("on a.originator_id = e.id"); | 156 | + fromPart.append(" left join (select * from (VALUES"); |
157 | + int entityIdIdx = 0; | ||
158 | + int lastEntityIdIdx = orderedEntityIds.size() - 1; | ||
159 | + for (EntityId entityId : orderedEntityIds) { | ||
160 | + fromPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")"); | ||
161 | + if (entityIdIdx != lastEntityIdIdx) { | ||
162 | + fromPart.append(","); | ||
163 | + } else { | ||
164 | + fromPart.append(")"); | ||
165 | + } | ||
166 | + entityIdIdx++; | ||
167 | + } | ||
168 | + fromPart.append(" as e(id, priority)) e "); | ||
169 | + if (pageLink.isSearchPropagatedAlarms()) { | ||
170 | + 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)"); | ||
171 | + } else { | ||
172 | + fromPart.append("on a.originator_id = e.id"); | ||
173 | + } | ||
174 | + sortPart.append("e.priority"); | ||
168 | } | 175 | } |
169 | - sortPart.append("e.priority"); | ||
170 | - } | ||
171 | - | ||
172 | - long startTs; | ||
173 | - long endTs; | ||
174 | - if (pageLink.getTimeWindow() > 0) { | ||
175 | - endTs = System.currentTimeMillis(); | ||
176 | - startTs = endTs - pageLink.getTimeWindow(); | ||
177 | - } else { | ||
178 | - startTs = pageLink.getStartTs(); | ||
179 | - endTs = pageLink.getEndTs(); | ||
180 | - } | ||
181 | 176 | ||
182 | - if (startTs > 0) { | ||
183 | - addAndIfNeeded(wherePart, addAnd); | ||
184 | - addAnd = true; | ||
185 | - ctx.addLongParameter("startTime", startTs); | ||
186 | - wherePart.append("a.created_time >= :startTime"); | ||
187 | - } | 177 | + long startTs; |
178 | + long endTs; | ||
179 | + if (pageLink.getTimeWindow() > 0) { | ||
180 | + endTs = System.currentTimeMillis(); | ||
181 | + startTs = endTs - pageLink.getTimeWindow(); | ||
182 | + } else { | ||
183 | + startTs = pageLink.getStartTs(); | ||
184 | + endTs = pageLink.getEndTs(); | ||
185 | + } | ||
188 | 186 | ||
189 | - if (endTs > 0) { | ||
190 | - addAndIfNeeded(wherePart, addAnd); | ||
191 | - addAnd = true; | ||
192 | - ctx.addLongParameter("endTime", endTs); | ||
193 | - wherePart.append("a.created_time <= :endTime"); | ||
194 | - } | 187 | + if (startTs > 0) { |
188 | + addAndIfNeeded(wherePart, addAnd); | ||
189 | + addAnd = true; | ||
190 | + ctx.addLongParameter("startTime", startTs); | ||
191 | + wherePart.append("a.created_time >= :startTime"); | ||
192 | + } | ||
195 | 193 | ||
196 | - if (pageLink.getTypeList() != null && !pageLink.getTypeList().isEmpty()) { | ||
197 | - addAndIfNeeded(wherePart, addAnd); | ||
198 | - addAnd = true; | ||
199 | - ctx.addStringListParameter("alarmTypes", pageLink.getTypeList()); | ||
200 | - wherePart.append("a.type in (:alarmTypes)"); | ||
201 | - } | 194 | + if (endTs > 0) { |
195 | + addAndIfNeeded(wherePart, addAnd); | ||
196 | + addAnd = true; | ||
197 | + ctx.addLongParameter("endTime", endTs); | ||
198 | + wherePart.append("a.created_time <= :endTime"); | ||
199 | + } | ||
202 | 200 | ||
203 | - if (pageLink.getSeverityList() != null && !pageLink.getSeverityList().isEmpty()) { | ||
204 | - addAndIfNeeded(wherePart, addAnd); | ||
205 | - addAnd = true; | ||
206 | - ctx.addStringListParameter("alarmSeverities", pageLink.getSeverityList().stream().map(AlarmSeverity::name).collect(Collectors.toList())); | ||
207 | - wherePart.append("a.severity in (:alarmSeverities)"); | ||
208 | - } | 201 | + if (pageLink.getTypeList() != null && !pageLink.getTypeList().isEmpty()) { |
202 | + addAndIfNeeded(wherePart, addAnd); | ||
203 | + addAnd = true; | ||
204 | + ctx.addStringListParameter("alarmTypes", pageLink.getTypeList()); | ||
205 | + wherePart.append("a.type in (:alarmTypes)"); | ||
206 | + } | ||
209 | 207 | ||
210 | - if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) { | ||
211 | - Set<AlarmStatus> statusSet = toStatusSet(pageLink.getStatusList()); | ||
212 | - if (!statusSet.isEmpty()) { | 208 | + if (pageLink.getSeverityList() != null && !pageLink.getSeverityList().isEmpty()) { |
213 | addAndIfNeeded(wherePart, addAnd); | 209 | addAndIfNeeded(wherePart, addAnd); |
214 | addAnd = true; | 210 | addAnd = true; |
215 | - ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList())); | ||
216 | - wherePart.append(" a.status in (:alarmStatuses)"); | 211 | + ctx.addStringListParameter("alarmSeverities", pageLink.getSeverityList().stream().map(AlarmSeverity::name).collect(Collectors.toList())); |
212 | + wherePart.append("a.severity in (:alarmSeverities)"); | ||
217 | } | 213 | } |
218 | - } | ||
219 | 214 | ||
220 | - String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch()); | ||
221 | - String mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString(); | ||
222 | - if (!textSearchQuery.isEmpty()) { | ||
223 | - mainQuery = String.format("select * from (%s) a WHERE %s", mainQuery, textSearchQuery); | ||
224 | - } | ||
225 | - String countQuery = mainQuery; | ||
226 | - int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) from (%s) result", countQuery), ctx, Integer.class); | 215 | + if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) { |
216 | + Set<AlarmStatus> statusSet = toStatusSet(pageLink.getStatusList()); | ||
217 | + if (!statusSet.isEmpty()) { | ||
218 | + addAndIfNeeded(wherePart, addAnd); | ||
219 | + addAnd = true; | ||
220 | + ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList())); | ||
221 | + wherePart.append(" a.status in (:alarmStatuses)"); | ||
222 | + } | ||
223 | + } | ||
227 | 224 | ||
228 | - String dataQuery = mainQuery + sortPart; | 225 | + String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch()); |
226 | + String mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString(); | ||
227 | + if (!textSearchQuery.isEmpty()) { | ||
228 | + mainQuery = String.format("select * from (%s) a WHERE %s", mainQuery, textSearchQuery); | ||
229 | + } | ||
230 | + String countQuery = mainQuery; | ||
231 | + int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) from (%s) result", countQuery), ctx, Integer.class); | ||
229 | 232 | ||
230 | - int startIndex = pageLink.getPageSize() * pageLink.getPage(); | ||
231 | - if (pageLink.getPageSize() > 0) { | ||
232 | - dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); | ||
233 | - } | ||
234 | - List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx); | ||
235 | - return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements, orderedEntityIds); | 233 | + String dataQuery = mainQuery + sortPart; |
234 | + | ||
235 | + int startIndex = pageLink.getPageSize() * pageLink.getPage(); | ||
236 | + if (pageLink.getPageSize() > 0) { | ||
237 | + dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); | ||
238 | + } | ||
239 | + List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx); | ||
240 | + return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements, orderedEntityIds); | ||
241 | + }); | ||
236 | } | 242 | } |
237 | 243 | ||
238 | private String buildTextSearchQuery(QueryContext ctx, List<EntityKey> selectionMapping, String searchText) { | 244 | private String buildTextSearchQuery(QueryContext ctx, List<EntityKey> selectionMapping, String searchText) { |
@@ -20,6 +20,9 @@ import org.apache.commons.lang3.StringUtils; | @@ -20,6 +20,9 @@ import org.apache.commons.lang3.StringUtils; | ||
20 | import org.springframework.beans.factory.annotation.Autowired; | 20 | import org.springframework.beans.factory.annotation.Autowired; |
21 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; | 21 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; |
22 | import org.springframework.stereotype.Repository; | 22 | import org.springframework.stereotype.Repository; |
23 | +import org.springframework.transaction.TransactionStatus; | ||
24 | +import org.springframework.transaction.support.TransactionCallback; | ||
25 | +import org.springframework.transaction.support.TransactionTemplate; | ||
23 | import org.thingsboard.server.common.data.EntityType; | 26 | import org.thingsboard.server.common.data.EntityType; |
24 | import org.thingsboard.server.common.data.id.CustomerId; | 27 | import org.thingsboard.server.common.data.id.CustomerId; |
25 | import org.thingsboard.server.common.data.id.EntityId; | 28 | import org.thingsboard.server.common.data.id.EntityId; |
@@ -58,37 +61,36 @@ import java.util.stream.Collectors; | @@ -58,37 +61,36 @@ import java.util.stream.Collectors; | ||
58 | @Repository | 61 | @Repository |
59 | @Slf4j | 62 | @Slf4j |
60 | public class DefaultEntityQueryRepository implements EntityQueryRepository { | 63 | public class DefaultEntityQueryRepository implements EntityQueryRepository { |
61 | - //TODO: rafactoring to protect from SQL injections; | ||
62 | private static final Map<EntityType, String> entityTableMap = new HashMap<>(); | 64 | private static final Map<EntityType, String> entityTableMap = new HashMap<>(); |
63 | - public static final String SELECT_PHONE = " CASE WHEN entity.entity_type = 'TENANT' THEN (select phone from tenant where id = entity_id)" + | 65 | + private static final String SELECT_PHONE = " CASE WHEN entity.entity_type = 'TENANT' THEN (select phone from tenant where id = entity_id)" + |
64 | " WHEN entity.entity_type = 'CUSTOMER' THEN (select phone from customer where id = entity_id) END as phone"; | 66 | " WHEN entity.entity_type = 'CUSTOMER' THEN (select phone from customer where id = entity_id) END as phone"; |
65 | - public static final String SELECT_ZIP = " CASE WHEN entity.entity_type = 'TENANT' THEN (select zip from tenant where id = entity_id)" + | 67 | + private static final String SELECT_ZIP = " CASE WHEN entity.entity_type = 'TENANT' THEN (select zip from tenant where id = entity_id)" + |
66 | " WHEN entity.entity_type = 'CUSTOMER' THEN (select zip from customer where id = entity_id) END as zip"; | 68 | " WHEN entity.entity_type = 'CUSTOMER' THEN (select zip from customer where id = entity_id) END as zip"; |
67 | - public static final String SELECT_ADDRESS_2 = " CASE WHEN entity.entity_type = 'TENANT'" + | 69 | + private static final String SELECT_ADDRESS_2 = " CASE WHEN entity.entity_type = 'TENANT'" + |
68 | " THEN (select address2 from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + | 70 | " THEN (select address2 from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
69 | " THEN (select address2 from customer where id = entity_id) END as address2"; | 71 | " THEN (select address2 from customer where id = entity_id) END as address2"; |
70 | - public static final String SELECT_ADDRESS = " CASE WHEN entity.entity_type = 'TENANT'" + | 72 | + private static final String SELECT_ADDRESS = " CASE WHEN entity.entity_type = 'TENANT'" + |
71 | " THEN (select address from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + | 73 | " THEN (select address from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
72 | " THEN (select address from customer where id = entity_id) END as address"; | 74 | " THEN (select address from customer where id = entity_id) END as address"; |
73 | - public static final String SELECT_CITY = " CASE WHEN entity.entity_type = 'TENANT'" + | 75 | + private static final String SELECT_CITY = " CASE WHEN entity.entity_type = 'TENANT'" + |
74 | " THEN (select city from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + | 76 | " THEN (select city from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
75 | " THEN (select city from customer where id = entity_id) END as city"; | 77 | " THEN (select city from customer where id = entity_id) END as city"; |
76 | - public static final String SELECT_STATE = " CASE WHEN entity.entity_type = 'TENANT'" + | 78 | + private static final String SELECT_STATE = " CASE WHEN entity.entity_type = 'TENANT'" + |
77 | " THEN (select state from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + | 79 | " THEN (select state from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
78 | " THEN (select state from customer where id = entity_id) END as state"; | 80 | " THEN (select state from customer where id = entity_id) END as state"; |
79 | - public static final String SELECT_COUNTRY = " CASE WHEN entity.entity_type = 'TENANT'" + | 81 | + private static final String SELECT_COUNTRY = " CASE WHEN entity.entity_type = 'TENANT'" + |
80 | " THEN (select country from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + | 82 | " THEN (select country from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
81 | " THEN (select country from customer where id = entity_id) END as country"; | 83 | " THEN (select country from customer where id = entity_id) END as country"; |
82 | - public static final String SELECT_TITLE = " CASE WHEN entity.entity_type = 'TENANT'" + | 84 | + private static final String SELECT_TITLE = " CASE WHEN entity.entity_type = 'TENANT'" + |
83 | " THEN (select title from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + | 85 | " THEN (select title from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
84 | " THEN (select title from customer where id = entity_id) END as title"; | 86 | " THEN (select title from customer where id = entity_id) END as title"; |
85 | - public static final String SELECT_LAST_NAME = " CASE WHEN entity.entity_type = 'USER'" + | 87 | + private static final String SELECT_LAST_NAME = " CASE WHEN entity.entity_type = 'USER'" + |
86 | " THEN (select last_name from tb_user where id = entity_id) END as last_name"; | 88 | " THEN (select last_name from tb_user where id = entity_id) END as last_name"; |
87 | - public static final String SELECT_FIRST_NAME = " CASE WHEN entity.entity_type = 'USER'" + | 89 | + private static final String SELECT_FIRST_NAME = " CASE WHEN entity.entity_type = 'USER'" + |
88 | " THEN (select first_name from tb_user where id = entity_id) END as first_name"; | 90 | " THEN (select first_name from tb_user where id = entity_id) END as first_name"; |
89 | - public static final String SELECT_REGION = " CASE WHEN entity.entity_type = 'TENANT'" + | 91 | + private static final String SELECT_REGION = " CASE WHEN entity.entity_type = 'TENANT'" + |
90 | " THEN (select region from tenant where id = entity_id) END as region"; | 92 | " THEN (select region from tenant where id = entity_id) END as region"; |
91 | - public static final String SELECT_EMAIL = " CASE" + | 93 | + private static final String SELECT_EMAIL = " CASE" + |
92 | " WHEN entity.entity_type = 'TENANT'" + | 94 | " WHEN entity.entity_type = 'TENANT'" + |
93 | " THEN (select email from tenant where id = entity_id)" + | 95 | " THEN (select email from tenant where id = entity_id)" + |
94 | " WHEN entity.entity_type = 'CUSTOMER' " + | 96 | " WHEN entity.entity_type = 'CUSTOMER' " + |
@@ -96,7 +98,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -96,7 +98,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
96 | " WHEN entity.entity_type = 'USER'" + | 98 | " WHEN entity.entity_type = 'USER'" + |
97 | " THEN (select email from tb_user where id = entity_id)" + | 99 | " THEN (select email from tb_user where id = entity_id)" + |
98 | " END as email"; | 100 | " END as email"; |
99 | - public static final String SELECT_CUSTOMER_ID = "CASE" + | 101 | + private static final String SELECT_CUSTOMER_ID = "CASE" + |
100 | " WHEN entity.entity_type = 'TENANT'" + | 102 | " WHEN entity.entity_type = 'TENANT'" + |
101 | " THEN UUID('" + TenantId.NULL_UUID + "')" + | 103 | " THEN UUID('" + TenantId.NULL_UUID + "')" + |
102 | " WHEN entity.entity_type = 'CUSTOMER' THEN entity_id" + | 104 | " WHEN entity.entity_type = 'CUSTOMER' THEN entity_id" + |
@@ -112,7 +114,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -112,7 +114,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
112 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + | 114 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + |
113 | " THEN (select customer_id from entity_view where id = entity_id)" + | 115 | " THEN (select customer_id from entity_view where id = entity_id)" + |
114 | " END as customer_id"; | 116 | " END as customer_id"; |
115 | - public static final String SELECT_TENANT_ID = "SELECT CASE" + | 117 | + private static final String SELECT_TENANT_ID = "SELECT CASE" + |
116 | " WHEN entity.entity_type = 'TENANT' THEN entity_id" + | 118 | " WHEN entity.entity_type = 'TENANT' THEN entity_id" + |
117 | " WHEN entity.entity_type = 'CUSTOMER'" + | 119 | " WHEN entity.entity_type = 'CUSTOMER'" + |
118 | " THEN (select tenant_id from customer where id = entity_id)" + | 120 | " THEN (select tenant_id from customer where id = entity_id)" + |
@@ -127,7 +129,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -127,7 +129,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
127 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + | 129 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + |
128 | " THEN (select tenant_id from entity_view where id = entity_id)" + | 130 | " THEN (select tenant_id from entity_view where id = entity_id)" + |
129 | " END as tenant_id"; | 131 | " END as tenant_id"; |
130 | - public static final String SELECT_CREATED_TIME = " CASE" + | 132 | + private static final String SELECT_CREATED_TIME = " CASE" + |
131 | " WHEN entity.entity_type = 'TENANT'" + | 133 | " WHEN entity.entity_type = 'TENANT'" + |
132 | " THEN (select created_time from tenant where id = entity_id)" + | 134 | " THEN (select created_time from tenant where id = entity_id)" + |
133 | " WHEN entity.entity_type = 'CUSTOMER' " + | 135 | " WHEN entity.entity_type = 'CUSTOMER' " + |
@@ -143,7 +145,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -143,7 +145,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
143 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + | 145 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + |
144 | " THEN (select created_time from entity_view where id = entity_id)" + | 146 | " THEN (select created_time from entity_view where id = entity_id)" + |
145 | " END as created_time"; | 147 | " END as created_time"; |
146 | - public static final String SELECT_NAME = " CASE" + | 148 | + private static final String SELECT_NAME = " CASE" + |
147 | " WHEN entity.entity_type = 'TENANT'" + | 149 | " WHEN entity.entity_type = 'TENANT'" + |
148 | " THEN (select title from tenant where id = entity_id)" + | 150 | " THEN (select title from tenant where id = entity_id)" + |
149 | " WHEN entity.entity_type = 'CUSTOMER' " + | 151 | " WHEN entity.entity_type = 'CUSTOMER' " + |
@@ -159,7 +161,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -159,7 +161,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
159 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + | 161 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + |
160 | " THEN (select name from entity_view where id = entity_id)" + | 162 | " THEN (select name from entity_view where id = entity_id)" + |
161 | " END as name"; | 163 | " END as name"; |
162 | - public static final String SELECT_TYPE = " CASE" + | 164 | + private static final String SELECT_TYPE = " CASE" + |
163 | " WHEN entity.entity_type = 'USER'" + | 165 | " WHEN entity.entity_type = 'USER'" + |
164 | " THEN (select authority from tb_user where id = entity_id)" + | 166 | " THEN (select authority from tb_user where id = entity_id)" + |
165 | " WHEN entity.entity_type = 'ASSET'" + | 167 | " WHEN entity.entity_type = 'ASSET'" + |
@@ -169,7 +171,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -169,7 +171,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
169 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + | 171 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + |
170 | " THEN (select type from entity_view where id = entity_id)" + | 172 | " THEN (select type from entity_view where id = entity_id)" + |
171 | " ELSE entity.entity_type END as type"; | 173 | " ELSE entity.entity_type END as type"; |
172 | - public static final String SELECT_LABEL = " CASE" + | 174 | + private static final String SELECT_LABEL = " CASE" + |
173 | " WHEN entity.entity_type = 'TENANT'" + | 175 | " WHEN entity.entity_type = 'TENANT'" + |
174 | " THEN (select title from tenant where id = entity_id)" + | 176 | " THEN (select title from tenant where id = entity_id)" + |
175 | " WHEN entity.entity_type = 'CUSTOMER' " + | 177 | " WHEN entity.entity_type = 'CUSTOMER' " + |
@@ -196,7 +198,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -196,7 +198,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
196 | entityTableMap.put(EntityType.TENANT, "tenant"); | 198 | entityTableMap.put(EntityType.TENANT, "tenant"); |
197 | } | 199 | } |
198 | 200 | ||
199 | - public static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" + | 201 | + private static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" + |
200 | " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" + | 202 | " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" + |
201 | " FROM relation" + | 203 | " FROM relation" + |
202 | " WHERE $in_id = :relation_root_id and $in_type = :relation_root_type and relation_type_group = 'COMMON'" + | 204 | " 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 { | @@ -209,11 +211,16 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
209 | " SELECT re.$out_id entity_id, re.$out_type entity_type, re.lvl lvl" + | 211 | " SELECT re.$out_id entity_id, re.$out_type entity_type, re.lvl lvl" + |
210 | " from related_entities re" + | 212 | " from related_entities re" + |
211 | " %s ) entity"; | 213 | " %s ) entity"; |
212 | - public static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from"); | ||
213 | - public static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to"); | 214 | + private static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from"); |
215 | + private static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to"); | ||
214 | 216 | ||
215 | - @Autowired | ||
216 | - protected NamedParameterJdbcTemplate jdbcTemplate; | 217 | + private final NamedParameterJdbcTemplate jdbcTemplate; |
218 | + private final TransactionTemplate transactionTemplate; | ||
219 | + | ||
220 | + public DefaultEntityQueryRepository(NamedParameterJdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) { | ||
221 | + this.jdbcTemplate = jdbcTemplate; | ||
222 | + this.transactionTemplate = transactionTemplate; | ||
223 | + } | ||
217 | 224 | ||
218 | @Override | 225 | @Override |
219 | public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { | 226 | public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { |
@@ -223,89 +230,91 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -223,89 +230,91 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
223 | ctx.append(addEntityTableQuery(ctx, query.getEntityFilter(), entityType)); | 230 | ctx.append(addEntityTableQuery(ctx, query.getEntityFilter(), entityType)); |
224 | ctx.append(" e where "); | 231 | ctx.append(" e where "); |
225 | ctx.append(buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), Collections.emptyList(), entityType)); | 232 | ctx.append(buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), Collections.emptyList(), entityType)); |
226 | - return jdbcTemplate.queryForObject(ctx.getQuery(), ctx, Long.class); | 233 | + return transactionTemplate.execute(status -> jdbcTemplate.queryForObject(ctx.getQuery(), ctx, Long.class)); |
227 | } | 234 | } |
228 | 235 | ||
229 | @Override | 236 | @Override |
230 | public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { | 237 | public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { |
231 | - QueryContext ctx = new QueryContext(); | ||
232 | - EntityType entityType = resolveEntityType(query.getEntityFilter()); | ||
233 | - EntityDataPageLink pageLink = query.getPageLink(); | ||
234 | - | ||
235 | - List<EntityKeyMapping> mappings = EntityKeyMapping.prepareKeyMapping(query); | ||
236 | - | ||
237 | - List<EntityKeyMapping> selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection) | ||
238 | - .collect(Collectors.toList()); | ||
239 | - List<EntityKeyMapping> entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest()) | ||
240 | - .collect(Collectors.toList()); | ||
241 | - List<EntityKeyMapping> latestSelectionMapping = selectionMapping.stream().filter(EntityKeyMapping::isLatest) | ||
242 | - .collect(Collectors.toList()); | ||
243 | - | ||
244 | - List<EntityKeyMapping> filterMapping = mappings.stream().filter(EntityKeyMapping::hasFilter) | ||
245 | - .collect(Collectors.toList()); | ||
246 | - List<EntityKeyMapping> entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest()) | ||
247 | - .collect(Collectors.toList()); | ||
248 | - List<EntityKeyMapping> latestFiltersMapping = filterMapping.stream().filter(EntityKeyMapping::isLatest) | ||
249 | - .collect(Collectors.toList()); | ||
250 | - | ||
251 | - List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest) | ||
252 | - .collect(Collectors.toList()); | ||
253 | - | ||
254 | - | ||
255 | - String entityWhereClause = this.buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType); | ||
256 | - String latestJoins = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings); | ||
257 | - String whereClause = this.buildWhere(ctx, latestFiltersMapping, query.getEntityFilter().getType(), entityType); | ||
258 | - String textSearchQuery = this.buildTextSearchQuery(ctx, selectionMapping, pageLink.getTextSearch()); | ||
259 | - String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType); | ||
260 | - String entityTypeStr; | ||
261 | - if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) { | ||
262 | - entityTypeStr = "e.entity_type"; | ||
263 | - } else { | ||
264 | - entityTypeStr = "'" + entityType.name() + "'"; | ||
265 | - } | ||
266 | - if (!StringUtils.isEmpty(entityFieldsSelection)) { | ||
267 | - entityFieldsSelection = String.format("e.id id, %s entity_type, %s", entityTypeStr, entityFieldsSelection); | ||
268 | - } else { | ||
269 | - entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr); | ||
270 | - } | ||
271 | - String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping, query.getEntityFilter().getType(), entityType); | ||
272 | - String topSelection = "entities.*"; | ||
273 | - if (!StringUtils.isEmpty(latestSelection)) { | ||
274 | - topSelection = topSelection + ", " + latestSelection; | ||
275 | - } | 238 | + return transactionTemplate.execute(status -> { |
239 | + QueryContext ctx = new QueryContext(); | ||
240 | + EntityType entityType = resolveEntityType(query.getEntityFilter()); | ||
241 | + EntityDataPageLink pageLink = query.getPageLink(); | ||
242 | + | ||
243 | + List<EntityKeyMapping> mappings = EntityKeyMapping.prepareKeyMapping(query); | ||
244 | + | ||
245 | + List<EntityKeyMapping> selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection) | ||
246 | + .collect(Collectors.toList()); | ||
247 | + List<EntityKeyMapping> entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest()) | ||
248 | + .collect(Collectors.toList()); | ||
249 | + List<EntityKeyMapping> latestSelectionMapping = selectionMapping.stream().filter(EntityKeyMapping::isLatest) | ||
250 | + .collect(Collectors.toList()); | ||
251 | + | ||
252 | + List<EntityKeyMapping> filterMapping = mappings.stream().filter(EntityKeyMapping::hasFilter) | ||
253 | + .collect(Collectors.toList()); | ||
254 | + List<EntityKeyMapping> entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest()) | ||
255 | + .collect(Collectors.toList()); | ||
256 | + List<EntityKeyMapping> latestFiltersMapping = filterMapping.stream().filter(EntityKeyMapping::isLatest) | ||
257 | + .collect(Collectors.toList()); | ||
258 | + | ||
259 | + List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest) | ||
260 | + .collect(Collectors.toList()); | ||
261 | + | ||
262 | + | ||
263 | + String entityWhereClause = DefaultEntityQueryRepository.this.buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType); | ||
264 | + String latestJoins = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings); | ||
265 | + String whereClause = DefaultEntityQueryRepository.this.buildWhere(ctx, latestFiltersMapping, query.getEntityFilter().getType(), entityType); | ||
266 | + String textSearchQuery = DefaultEntityQueryRepository.this.buildTextSearchQuery(ctx, selectionMapping, pageLink.getTextSearch()); | ||
267 | + String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType); | ||
268 | + String entityTypeStr; | ||
269 | + if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) { | ||
270 | + entityTypeStr = "e.entity_type"; | ||
271 | + } else { | ||
272 | + entityTypeStr = "'" + entityType.name() + "'"; | ||
273 | + } | ||
274 | + if (!StringUtils.isEmpty(entityFieldsSelection)) { | ||
275 | + entityFieldsSelection = String.format("e.id id, %s entity_type, %s", entityTypeStr, entityFieldsSelection); | ||
276 | + } else { | ||
277 | + entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr); | ||
278 | + } | ||
279 | + String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping, query.getEntityFilter().getType(), entityType); | ||
280 | + String topSelection = "entities.*"; | ||
281 | + if (!StringUtils.isEmpty(latestSelection)) { | ||
282 | + topSelection = topSelection + ", " + latestSelection; | ||
283 | + } | ||
276 | 284 | ||
277 | - String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result %s", | ||
278 | - topSelection, | ||
279 | - entityFieldsSelection, | ||
280 | - addEntityTableQuery(ctx, query.getEntityFilter(), entityType), | ||
281 | - entityWhereClause, | ||
282 | - latestJoins, | ||
283 | - whereClause, | ||
284 | - textSearchQuery); | ||
285 | - | ||
286 | - int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) %s", fromClause), ctx, Integer.class); | ||
287 | - | ||
288 | - String dataQuery = String.format("select * %s", fromClause); | ||
289 | - | ||
290 | - EntityDataSortOrder sortOrder = pageLink.getSortOrder(); | ||
291 | - if (sortOrder != null) { | ||
292 | - Optional<EntityKeyMapping> sortOrderMappingOpt = mappings.stream().filter(EntityKeyMapping::isSortOrder).findFirst(); | ||
293 | - if (sortOrderMappingOpt.isPresent()) { | ||
294 | - EntityKeyMapping sortOrderMapping = sortOrderMappingOpt.get(); | ||
295 | - dataQuery = String.format("%s order by %s", dataQuery, sortOrderMapping.getValueAlias()); | ||
296 | - if (sortOrder.getDirection() == EntityDataSortOrder.Direction.ASC) { | ||
297 | - dataQuery += " asc"; | ||
298 | - } else { | ||
299 | - dataQuery += " desc"; | 285 | + String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result %s", |
286 | + topSelection, | ||
287 | + entityFieldsSelection, | ||
288 | + addEntityTableQuery(ctx, query.getEntityFilter(), entityType), | ||
289 | + entityWhereClause, | ||
290 | + latestJoins, | ||
291 | + whereClause, | ||
292 | + textSearchQuery); | ||
293 | + | ||
294 | + int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) %s", fromClause), ctx, Integer.class); | ||
295 | + | ||
296 | + String dataQuery = String.format("select * %s", fromClause); | ||
297 | + | ||
298 | + EntityDataSortOrder sortOrder = pageLink.getSortOrder(); | ||
299 | + if (sortOrder != null) { | ||
300 | + Optional<EntityKeyMapping> sortOrderMappingOpt = mappings.stream().filter(EntityKeyMapping::isSortOrder).findFirst(); | ||
301 | + if (sortOrderMappingOpt.isPresent()) { | ||
302 | + EntityKeyMapping sortOrderMapping = sortOrderMappingOpt.get(); | ||
303 | + dataQuery = String.format("%s order by %s", dataQuery, sortOrderMapping.getValueAlias()); | ||
304 | + if (sortOrder.getDirection() == EntityDataSortOrder.Direction.ASC) { | ||
305 | + dataQuery += " asc"; | ||
306 | + } else { | ||
307 | + dataQuery += " desc"; | ||
308 | + } | ||
300 | } | 309 | } |
301 | } | 310 | } |
302 | - } | ||
303 | - int startIndex = pageLink.getPageSize() * pageLink.getPage(); | ||
304 | - if (pageLink.getPageSize() > 0) { | ||
305 | - dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); | ||
306 | - } | ||
307 | - List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx); | ||
308 | - return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements); | 311 | + int startIndex = pageLink.getPageSize() * pageLink.getPage(); |
312 | + if (pageLink.getPageSize() > 0) { | ||
313 | + dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); | ||
314 | + } | ||
315 | + List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx); | ||
316 | + return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements); | ||
317 | + }); | ||
309 | } | 318 | } |
310 | 319 | ||
311 | private String buildEntityWhere(QueryContext ctx, | 320 | private String buildEntityWhere(QueryContext ctx, |
@@ -16,7 +16,9 @@ | @@ -16,7 +16,9 @@ | ||
16 | 16 | ||
17 | CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC); | 17 | CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC); |
18 | 18 | ||
19 | -CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_time ON alarm(originator_id, created_time DESC); | 19 | +CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator_id, created_time DESC); |
20 | + | ||
21 | +CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC); | ||
20 | 22 | ||
21 | CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id); | 23 | CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id); |
22 | 24 |
@@ -119,7 +119,7 @@ | @@ -119,7 +119,7 @@ | ||
119 | </mat-cell> | 119 | </mat-cell> |
120 | </ng-container> | 120 | </ng-container> |
121 | <mat-header-row [ngClass]="{'mat-row-select': enableSelection}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> | 121 | <mat-header-row [ngClass]="{'mat-row-select': enableSelection}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> |
122 | - <mat-row [ngClass]="{'mat-row-select': enableSelection, | 122 | + <mat-row [fxShow]="!alarmsDatasource.dataLoading" [ngClass]="{'mat-row-select': enableSelection, |
123 | 'mat-selected': alarmsDatasource.isSelected(alarm), | 123 | 'mat-selected': alarmsDatasource.isSelected(alarm), |
124 | 'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm)}" | 124 | 'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm)}" |
125 | *matRowDef="let alarm; columns: displayedColumns;" | 125 | *matRowDef="let alarm; columns: displayedColumns;" |
@@ -368,7 +368,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -368,7 +368,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
368 | dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); | 368 | dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); |
369 | dataKey.def = 'def' + this.columns.length; | 369 | dataKey.def = 'def' + this.columns.length; |
370 | const keySettings: TableWidgetDataKeySettings = dataKey.settings; | 370 | const keySettings: TableWidgetDataKeySettings = dataKey.settings; |
371 | - | 371 | + if (dataKey.type === DataKeyType.alarm && !isDefined(keySettings.columnWidth)) { |
372 | + const alarmField = alarmFields[dataKey.name]; | ||
373 | + if (alarmField && alarmField.time) { | ||
374 | + keySettings.columnWidth = '120px'; | ||
375 | + } | ||
376 | + } | ||
372 | this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings); | 377 | this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings); |
373 | this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx'); | 378 | this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx'); |
374 | this.columnWidth[dataKey.def] = getColumnWidth(keySettings); | 379 | this.columnWidth[dataKey.def] = getColumnWidth(keySettings); |
@@ -863,7 +868,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> { | @@ -863,7 +868,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> { | ||
863 | 868 | ||
864 | loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { | 869 | loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { |
865 | this.dataLoading = true; | 870 | this.dataLoading = true; |
866 | - this.clear(); | 871 | + // this.clear(); |
867 | this.appliedPageLink = pageLink; | 872 | this.appliedPageLink = pageLink; |
868 | this.appliedSortOrderLabel = sortOrderLabel; | 873 | this.appliedSortOrderLabel = sortOrderLabel; |
869 | this.subscription.subscribeForAlarms(pageLink, keyFilters); | 874 | this.subscription.subscribeForAlarms(pageLink, keyFilters); |
@@ -82,7 +82,7 @@ | @@ -82,7 +82,7 @@ | ||
82 | </mat-cell> | 82 | </mat-cell> |
83 | </ng-container> | 83 | </ng-container> |
84 | <mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> | 84 | <mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> |
85 | - <mat-row [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity)}" | 85 | + <mat-row [fxShow]="!entityDatasource.dataLoading" [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity)}" |
86 | *matRowDef="let entity; columns: displayedColumns;" | 86 | *matRowDef="let entity; columns: displayedColumns;" |
87 | (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row> | 87 | (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row> |
88 | </table> | 88 | </table> |
@@ -87,6 +87,7 @@ import { | @@ -87,6 +87,7 @@ import { | ||
87 | import { sortItems } from '@shared/models/page/page-link'; | 87 | import { sortItems } from '@shared/models/page/page-link'; |
88 | import { entityFields } from '@shared/models/entity.models'; | 88 | import { entityFields } from '@shared/models/entity.models'; |
89 | import { DatePipe } from '@angular/common'; | 89 | import { DatePipe } from '@angular/common'; |
90 | +import { alarmFields } from '@shared/models/alarm.models'; | ||
90 | 91 | ||
91 | interface EntitiesTableWidgetSettings extends TableWidgetSettings { | 92 | interface EntitiesTableWidgetSettings extends TableWidgetSettings { |
92 | entitiesTitle: string; | 93 | entitiesTitle: string; |
@@ -348,6 +349,13 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni | @@ -348,6 +349,13 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni | ||
348 | dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); | 349 | dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); |
349 | dataKey.def = 'def' + this.columns.length; | 350 | dataKey.def = 'def' + this.columns.length; |
350 | const keySettings: TableWidgetDataKeySettings = dataKey.settings; | 351 | const keySettings: TableWidgetDataKeySettings = dataKey.settings; |
352 | + if (dataKey.type === DataKeyType.entityField && | ||
353 | + !isDefined(keySettings.columnWidth) || keySettings.columnWidth === '0px') { | ||
354 | + const entityField = entityFields[dataKey.name]; | ||
355 | + if (entityField && entityField.time) { | ||
356 | + keySettings.columnWidth = '120px'; | ||
357 | + } | ||
358 | + } | ||
351 | 359 | ||
352 | this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings); | 360 | this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings); |
353 | this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx'); | 361 | this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx'); |
@@ -595,7 +603,7 @@ class EntityDatasource implements DataSource<EntityData> { | @@ -595,7 +603,7 @@ class EntityDatasource implements DataSource<EntityData> { | ||
595 | 603 | ||
596 | loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { | 604 | loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { |
597 | this.dataLoading = true; | 605 | this.dataLoading = true; |
598 | - this.clear(); | 606 | + // this.clear(); |
599 | this.appliedPageLink = pageLink; | 607 | this.appliedPageLink = pageLink; |
600 | this.appliedSortOrderLabel = sortOrderLabel; | 608 | this.appliedSortOrderLabel = sortOrderLabel; |
601 | this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters); | 609 | this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters); |