Showing
10 changed files
with
250 additions
and
214 deletions
... | ... | @@ -75,7 +75,8 @@ CREATE OR REPLACE PROCEDURE drop_all_idx() |
75 | 75 | $$ |
76 | 76 | BEGIN |
77 | 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 | 80 | DROP INDEX IF EXISTS idx_event_type_entity_id; |
80 | 81 | DROP INDEX IF EXISTS idx_relation_to_id; |
81 | 82 | DROP INDEX IF EXISTS idx_relation_from_id; |
... | ... | @@ -93,7 +94,8 @@ CREATE OR REPLACE PROCEDURE create_all_idx() |
93 | 94 | $$ |
94 | 95 | BEGIN |
95 | 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 | 99 | CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id); |
98 | 100 | CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id); |
99 | 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 | 286 | int dynamicQueryInvocationCntValue = stats.getDynamicQueryInvocationCnt().getAndSet(0); |
287 | 287 | long dynamicQueryInvocationTimeValue = stats.getDynamicQueryTimeSpent().getAndSet(0); |
288 | 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 | 290 | log.info("Stats: regularQueryInvocationCnt = [{}], regularQueryInvocationTime = [{}], " + |
291 | 291 | "dynamicQueryCnt = [{}] dynamicQueryInvocationCnt = [{}], dynamicQueryInvocationTime = [{}], " + |
292 | 292 | "alarmQueryInvocationCnt = [{}], alarmQueryInvocationTime = [{}]", | ... | ... |
... | ... | @@ -58,6 +58,9 @@ import org.thingsboard.server.service.queue.TbClusterService; |
58 | 58 | import org.thingsboard.server.service.state.DeviceStateService; |
59 | 59 | |
60 | 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 | 64 | import java.util.concurrent.locks.ReentrantLock; |
62 | 65 | |
63 | 66 | /** |
... | ... | @@ -92,7 +95,7 @@ public class DefaultTransportApiService implements TransportApiService { |
92 | 95 | @Autowired |
93 | 96 | protected TbClusterService tbClusterService; |
94 | 97 | |
95 | - private ReentrantLock deviceCreationLock = new ReentrantLock(); | |
98 | + private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); | |
96 | 99 | |
97 | 100 | @Override |
98 | 101 | public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) { |
... | ... | @@ -125,6 +128,7 @@ public class DefaultTransportApiService implements TransportApiService { |
125 | 128 | DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); |
126 | 129 | ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId); |
127 | 130 | return Futures.transform(gatewayFuture, gateway -> { |
131 | + Lock deviceCreationLock = deviceCreationLocks.computeIfAbsent(requestMsg.getDeviceName(), id -> new ReentrantLock()); | |
128 | 132 | deviceCreationLock.lock(); |
129 | 133 | try { |
130 | 134 | Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName()); | ... | ... |
... | ... | @@ -20,6 +20,9 @@ import org.apache.commons.lang3.StringUtils; |
20 | 20 | import org.springframework.beans.factory.annotation.Autowired; |
21 | 21 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; |
22 | 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 | 26 | import org.thingsboard.server.common.data.EntityType; |
24 | 27 | import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; |
25 | 28 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
... | ... | @@ -53,7 +56,6 @@ import java.util.stream.Collectors; |
53 | 56 | public class DefaultAlarmQueryRepository implements AlarmQueryRepository { |
54 | 57 | |
55 | 58 | private static final Map<String, String> alarmFieldColumnMap = new HashMap<>(); |
56 | - private static final List<String> uniqueAlarmFields = new ArrayList<>(); | |
57 | 59 | |
58 | 60 | static { |
59 | 61 | alarmFieldColumnMap.put("createdTime", ModelConstants.CREATED_TIME_PROPERTY); |
... | ... | @@ -72,11 +74,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { |
72 | 74 | alarmFieldColumnMap.put("originator_id", ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY); |
73 | 75 | alarmFieldColumnMap.put("originator_type", ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY); |
74 | 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 | 80 | " WHEN a.originator_type = " + EntityType.TENANT.ordinal() + |
81 | 81 | " THEN (select title from tenant where id = a.originator_id)" + |
82 | 82 | " WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() + |
... | ... | @@ -93,7 +93,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { |
93 | 93 | " THEN (select name from entity_view where id = a.originator_id)" + |
94 | 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 | 97 | " a.created_time as created_time," + |
98 | 98 | " a.ack_ts as ack_ts," + |
99 | 99 | " a.clear_ts as clear_ts," + |
... | ... | @@ -109,130 +109,136 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { |
109 | 109 | " a.propagate_relation_types as propagate_relation_types, " + |
110 | 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 | 122 | @Override |
119 | 123 | public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, |
120 | 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 | 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 | 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 | 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 | 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 | 209 | addAndIfNeeded(wherePart, addAnd); |
214 | 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 | 244 | private String buildTextSearchQuery(QueryContext ctx, List<EntityKey> selectionMapping, String searchText) { | ... | ... |
... | ... | @@ -20,6 +20,9 @@ import org.apache.commons.lang3.StringUtils; |
20 | 20 | import org.springframework.beans.factory.annotation.Autowired; |
21 | 21 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; |
22 | 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 | 26 | import org.thingsboard.server.common.data.EntityType; |
24 | 27 | import org.thingsboard.server.common.data.id.CustomerId; |
25 | 28 | import org.thingsboard.server.common.data.id.EntityId; |
... | ... | @@ -58,37 +61,36 @@ import java.util.stream.Collectors; |
58 | 61 | @Repository |
59 | 62 | @Slf4j |
60 | 63 | public class DefaultEntityQueryRepository implements EntityQueryRepository { |
61 | - //TODO: rafactoring to protect from SQL injections; | |
62 | 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 | 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 | 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 | 70 | " THEN (select address2 from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
69 | 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 | 73 | " THEN (select address from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
72 | 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 | 76 | " THEN (select city from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
75 | 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 | 79 | " THEN (select state from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
78 | 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 | 82 | " THEN (select country from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
81 | 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 | 85 | " THEN (select title from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " + |
84 | 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 | 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 | 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 | 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 | 94 | " WHEN entity.entity_type = 'TENANT'" + |
93 | 95 | " THEN (select email from tenant where id = entity_id)" + |
94 | 96 | " WHEN entity.entity_type = 'CUSTOMER' " + |
... | ... | @@ -96,7 +98,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
96 | 98 | " WHEN entity.entity_type = 'USER'" + |
97 | 99 | " THEN (select email from tb_user where id = entity_id)" + |
98 | 100 | " END as email"; |
99 | - public static final String SELECT_CUSTOMER_ID = "CASE" + | |
101 | + private static final String SELECT_CUSTOMER_ID = "CASE" + | |
100 | 102 | " WHEN entity.entity_type = 'TENANT'" + |
101 | 103 | " THEN UUID('" + TenantId.NULL_UUID + "')" + |
102 | 104 | " WHEN entity.entity_type = 'CUSTOMER' THEN entity_id" + |
... | ... | @@ -112,7 +114,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
112 | 114 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + |
113 | 115 | " THEN (select customer_id from entity_view where id = entity_id)" + |
114 | 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 | 118 | " WHEN entity.entity_type = 'TENANT' THEN entity_id" + |
117 | 119 | " WHEN entity.entity_type = 'CUSTOMER'" + |
118 | 120 | " THEN (select tenant_id from customer where id = entity_id)" + |
... | ... | @@ -127,7 +129,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
127 | 129 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + |
128 | 130 | " THEN (select tenant_id from entity_view where id = entity_id)" + |
129 | 131 | " END as tenant_id"; |
130 | - public static final String SELECT_CREATED_TIME = " CASE" + | |
132 | + private static final String SELECT_CREATED_TIME = " CASE" + | |
131 | 133 | " WHEN entity.entity_type = 'TENANT'" + |
132 | 134 | " THEN (select created_time from tenant where id = entity_id)" + |
133 | 135 | " WHEN entity.entity_type = 'CUSTOMER' " + |
... | ... | @@ -143,7 +145,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
143 | 145 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + |
144 | 146 | " THEN (select created_time from entity_view where id = entity_id)" + |
145 | 147 | " END as created_time"; |
146 | - public static final String SELECT_NAME = " CASE" + | |
148 | + private static final String SELECT_NAME = " CASE" + | |
147 | 149 | " WHEN entity.entity_type = 'TENANT'" + |
148 | 150 | " THEN (select title from tenant where id = entity_id)" + |
149 | 151 | " WHEN entity.entity_type = 'CUSTOMER' " + |
... | ... | @@ -159,7 +161,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
159 | 161 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + |
160 | 162 | " THEN (select name from entity_view where id = entity_id)" + |
161 | 163 | " END as name"; |
162 | - public static final String SELECT_TYPE = " CASE" + | |
164 | + private static final String SELECT_TYPE = " CASE" + | |
163 | 165 | " WHEN entity.entity_type = 'USER'" + |
164 | 166 | " THEN (select authority from tb_user where id = entity_id)" + |
165 | 167 | " WHEN entity.entity_type = 'ASSET'" + |
... | ... | @@ -169,7 +171,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
169 | 171 | " WHEN entity.entity_type = 'ENTITY_VIEW'" + |
170 | 172 | " THEN (select type from entity_view where id = entity_id)" + |
171 | 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 | 175 | " WHEN entity.entity_type = 'TENANT'" + |
174 | 176 | " THEN (select title from tenant where id = entity_id)" + |
175 | 177 | " WHEN entity.entity_type = 'CUSTOMER' " + |
... | ... | @@ -196,7 +198,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
196 | 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 | 202 | " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" + |
201 | 203 | " FROM relation" + |
202 | 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 | 211 | " SELECT re.$out_id entity_id, re.$out_type entity_type, re.lvl lvl" + |
210 | 212 | " from related_entities re" + |
211 | 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 | 225 | @Override |
219 | 226 | public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { |
... | ... | @@ -223,89 +230,91 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
223 | 230 | ctx.append(addEntityTableQuery(ctx, query.getEntityFilter(), entityType)); |
224 | 231 | ctx.append(" e where "); |
225 | 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 | 236 | @Override |
230 | 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 | 320 | private String buildEntityWhere(QueryContext ctx, | ... | ... |
... | ... | @@ -16,7 +16,9 @@ |
16 | 16 | |
17 | 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 | 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 | 119 | </mat-cell> |
120 | 120 | </ng-container> |
121 | 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 | 123 | 'mat-selected': alarmsDatasource.isSelected(alarm), |
124 | 124 | 'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm)}" |
125 | 125 | *matRowDef="let alarm; columns: displayedColumns;" | ... | ... |
... | ... | @@ -368,7 +368,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, |
368 | 368 | dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); |
369 | 369 | dataKey.def = 'def' + this.columns.length; |
370 | 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 | 377 | this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings); |
373 | 378 | this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx'); |
374 | 379 | this.columnWidth[dataKey.def] = getColumnWidth(keySettings); |
... | ... | @@ -863,7 +868,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> { |
863 | 868 | |
864 | 869 | loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { |
865 | 870 | this.dataLoading = true; |
866 | - this.clear(); | |
871 | + // this.clear(); | |
867 | 872 | this.appliedPageLink = pageLink; |
868 | 873 | this.appliedSortOrderLabel = sortOrderLabel; |
869 | 874 | this.subscription.subscribeForAlarms(pageLink, keyFilters); | ... | ... |
... | ... | @@ -82,7 +82,7 @@ |
82 | 82 | </mat-cell> |
83 | 83 | </ng-container> |
84 | 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 | 86 | *matRowDef="let entity; columns: displayedColumns;" |
87 | 87 | (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row> |
88 | 88 | </table> | ... | ... |
... | ... | @@ -87,6 +87,7 @@ import { |
87 | 87 | import { sortItems } from '@shared/models/page/page-link'; |
88 | 88 | import { entityFields } from '@shared/models/entity.models'; |
89 | 89 | import { DatePipe } from '@angular/common'; |
90 | +import { alarmFields } from '@shared/models/alarm.models'; | |
90 | 91 | |
91 | 92 | interface EntitiesTableWidgetSettings extends TableWidgetSettings { |
92 | 93 | entitiesTitle: string; |
... | ... | @@ -348,6 +349,13 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni |
348 | 349 | dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); |
349 | 350 | dataKey.def = 'def' + this.columns.length; |
350 | 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 | 360 | this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings); |
353 | 361 | this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx'); |
... | ... | @@ -595,7 +603,7 @@ class EntityDatasource implements DataSource<EntityData> { |
595 | 603 | |
596 | 604 | loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { |
597 | 605 | this.dataLoading = true; |
598 | - this.clear(); | |
606 | + // this.clear(); | |
599 | 607 | this.appliedPageLink = pageLink; |
600 | 608 | this.appliedSortOrderLabel = sortOrderLabel; |
601 | 609 | this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters); | ... | ... |