Showing
9 changed files
with
337 additions
and
156 deletions
... | ... | @@ -20,12 +20,14 @@ import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; |
20 | 20 | import org.thingsboard.server.dao.util.HsqlDao; |
21 | 21 | import org.thingsboard.server.dao.util.SqlDao; |
22 | 22 | |
23 | +import javax.persistence.Query; | |
24 | + | |
23 | 25 | @SqlDao |
24 | 26 | @HsqlDao |
25 | 27 | @Repository |
26 | 28 | public class HsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository { |
27 | 29 | |
28 | - private static final String P_KEY_CONFLICT_STATEMENT = "(component_descriptor.id=I.id)"; | |
30 | + private static final String P_KEY_CONFLICT_STATEMENT = "(component_descriptor.id=UUID(I.id))"; | |
29 | 31 | private static final String UNQ_KEY_CONFLICT_STATEMENT = "(component_descriptor.clazz=I.clazz)"; |
30 | 32 | |
31 | 33 | private static final String INSERT_OR_UPDATE_ON_P_KEY_CONFLICT = getInsertString(P_KEY_CONFLICT_STATEMENT); |
... | ... | @@ -37,13 +39,29 @@ public class HsqlComponentDescriptorInsertRepository extends AbstractComponentDe |
37 | 39 | } |
38 | 40 | |
39 | 41 | @Override |
42 | + protected Query getQuery(ComponentDescriptorEntity entity, String query) { | |
43 | + return entityManager.createNativeQuery(query, ComponentDescriptorEntity.class) | |
44 | + .setParameter("id", entity.getUuid().toString()) | |
45 | + .setParameter("created_time", entity.getCreatedTime()) | |
46 | + .setParameter("actions", entity.getActions()) | |
47 | + .setParameter("clazz", entity.getClazz()) | |
48 | + .setParameter("configuration_descriptor", entity.getConfigurationDescriptor().toString()) | |
49 | + .setParameter("name", entity.getName()) | |
50 | + .setParameter("scope", entity.getScope().name()) | |
51 | + .setParameter("search_text", entity.getSearchText()) | |
52 | + .setParameter("type", entity.getType().name()); | |
53 | + } | |
54 | + | |
55 | + @Override | |
40 | 56 | protected ComponentDescriptorEntity doProcessSaveOrUpdate(ComponentDescriptorEntity entity, String query) { |
41 | 57 | getQuery(entity, query).executeUpdate(); |
42 | 58 | return entityManager.find(ComponentDescriptorEntity.class, entity.getUuid()); |
43 | 59 | } |
44 | 60 | |
45 | 61 | private static String getInsertString(String conflictStatement) { |
46 | - return "MERGE INTO component_descriptor USING (VALUES :id, :created_time, :actions, :clazz, :configuration_descriptor, :name, :scope, :search_text, :type) I (id, craeted_time, actions, clazz, configuration_descriptor, name, scope, search_text, type) ON " + conflictStatement + " WHEN MATCHED THEN UPDATE SET component_descriptor.id = I.id, component_descriptor.actions = I.actions, component_descriptor.clazz = I.clazz, component_descriptor.configuration_descriptor = I.configuration_descriptor, component_descriptor.name = I.name, component_descriptor.scope = I.scope, component_descriptor.search_text = I.search_text, component_descriptor.type = I.type" + | |
47 | - " WHEN NOT MATCHED THEN INSERT (id, created_time, actions, clazz, configuration_descriptor, name, scope, search_text, type) VALUES (I.id, I.created_time, I.actions, I.clazz, I.configuration_descriptor, I.name, I.scope, I.search_text, I.type)"; | |
62 | + return "MERGE INTO component_descriptor USING (VALUES :id, :created_time, :actions, :clazz, :configuration_descriptor, :name, :scope, :search_text, :type) I (id, created_time, actions, clazz, configuration_descriptor, name, scope, search_text, type) ON " | |
63 | + + conflictStatement | |
64 | + + " WHEN MATCHED THEN UPDATE SET component_descriptor.id = UUID(I.id), component_descriptor.actions = I.actions, component_descriptor.clazz = I.clazz, component_descriptor.configuration_descriptor = I.configuration_descriptor, component_descriptor.name = I.name, component_descriptor.scope = I.scope, component_descriptor.search_text = I.search_text, component_descriptor.type = I.type" + | |
65 | + " WHEN NOT MATCHED THEN INSERT (id, created_time, actions, clazz, configuration_descriptor, name, scope, search_text, type) VALUES (UUID(I.id), I.created_time, I.actions, I.clazz, I.configuration_descriptor, I.name, I.scope, I.search_text, I.type)"; | |
48 | 66 | } |
49 | 67 | } | ... | ... |
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | * you may not use this file except in compliance with the License. |
6 | 6 | * You may obtain a copy of the License at |
7 | 7 | * |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | 9 | * |
10 | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | ... | ... |
... | ... | @@ -17,6 +17,8 @@ package org.thingsboard.server.dao.sql.query; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.apache.commons.lang3.StringUtils; |
20 | +import org.springframework.beans.factory.annotation.Autowired; | |
21 | +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; | |
20 | 22 | import org.springframework.stereotype.Repository; |
21 | 23 | import org.thingsboard.server.common.data.EntityType; |
22 | 24 | import org.thingsboard.server.common.data.id.CustomerId; |
... | ... | @@ -44,8 +46,6 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
44 | 46 | import org.thingsboard.server.common.data.relation.EntityTypeFilter; |
45 | 47 | import org.thingsboard.server.dao.util.SqlDao; |
46 | 48 | |
47 | -import javax.persistence.EntityManager; | |
48 | -import javax.persistence.PersistenceContext; | |
49 | 49 | import java.math.BigInteger; |
50 | 50 | import java.util.Collections; |
51 | 51 | import java.util.HashMap; |
... | ... | @@ -75,7 +75,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
75 | 75 | public static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" + |
76 | 76 | " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" + |
77 | 77 | " FROM relation" + |
78 | - " WHERE $in_id = '%s' and $in_type = '%s' and relation_type_group = 'COMMON'" + | |
78 | + " WHERE $in_id = :relation_root_id and $in_type = :relation_root_type and relation_type_group = 'COMMON'" + | |
79 | 79 | " UNION ALL" + |
80 | 80 | " SELECT r.from_id, r.from_type, r.to_id, r.to_type, r.relation_type, lvl + 1" + |
81 | 81 | " FROM relation r" + |
... | ... | @@ -88,21 +88,23 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
88 | 88 | public static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from"); |
89 | 89 | public static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to"); |
90 | 90 | |
91 | - @PersistenceContext | |
92 | - private EntityManager entityManager; | |
91 | + @Autowired | |
92 | + protected NamedParameterJdbcTemplate jdbcTemplate; | |
93 | 93 | |
94 | 94 | @Override |
95 | 95 | public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { |
96 | 96 | EntityType entityType = resolveEntityType(query.getEntityFilter()); |
97 | - String countQuery = String.format("select count(e.id) from %s e where %s", | |
98 | - getEntityTableQuery(query.getEntityFilter(), entityType), this.buildEntityWhere(tenantId, customerId, query.getEntityFilter(), | |
99 | - Collections.emptyList(), entityType)); | |
100 | - return ((BigInteger) entityManager.createNativeQuery(countQuery) | |
101 | - .getSingleResult()).longValue(); | |
97 | + EntityQueryContext ctx = new EntityQueryContext(); | |
98 | + ctx.append("select count(e.id) from "); | |
99 | + ctx.append(addEntityTableQuery(ctx, query.getEntityFilter(), entityType)); | |
100 | + ctx.append(" e where "); | |
101 | + ctx.append(buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), Collections.emptyList(), entityType)); | |
102 | + return jdbcTemplate.queryForObject(ctx.getQuery(), ctx, Long.class); | |
102 | 103 | } |
103 | 104 | |
104 | 105 | @Override |
105 | 106 | public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { |
107 | + EntityQueryContext ctx = new EntityQueryContext(); | |
106 | 108 | EntityType entityType = resolveEntityType(query.getEntityFilter()); |
107 | 109 | EntityDataPageLink pageLink = query.getPageLink(); |
108 | 110 | |
... | ... | @@ -126,9 +128,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
126 | 128 | .collect(Collectors.toList()); |
127 | 129 | |
128 | 130 | |
129 | - String entityWhereClause = this.buildEntityWhere(tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType); | |
130 | - String latestJoins = EntityKeyMapping.buildLatestJoins(query.getEntityFilter(), entityType, allLatestMappings); | |
131 | - String whereClause = this.buildWhere(selectionMapping, latestFiltersMapping, pageLink.getTextSearch()); | |
131 | + String entityWhereClause = this.buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType); | |
132 | + String latestJoins = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings); | |
133 | + String whereClause = this.buildWhere(ctx, selectionMapping, latestFiltersMapping, pageLink.getTextSearch()); | |
132 | 134 | String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping); |
133 | 135 | String entityTypeStr; |
134 | 136 | if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) { |
... | ... | @@ -137,9 +139,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
137 | 139 | entityTypeStr = "'" + entityType.name() + "'"; |
138 | 140 | } |
139 | 141 | if (!StringUtils.isEmpty(entityFieldsSelection)) { |
140 | - entityFieldsSelection = String.format("e.id, %s, %s", entityTypeStr, entityFieldsSelection); | |
142 | + entityFieldsSelection = String.format("e.id id, %s entity_type, %s", entityTypeStr, entityFieldsSelection); | |
141 | 143 | } else { |
142 | - entityFieldsSelection = String.format("e.id, %s", entityTypeStr); | |
144 | + entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr); | |
143 | 145 | } |
144 | 146 | String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping); |
145 | 147 | String topSelection = "entities.*"; |
... | ... | @@ -150,13 +152,13 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
150 | 152 | String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result", |
151 | 153 | topSelection, |
152 | 154 | entityFieldsSelection, |
153 | - getEntityTableQuery(query.getEntityFilter(), entityType), | |
155 | + addEntityTableQuery(ctx, query.getEntityFilter(), entityType), | |
154 | 156 | entityWhereClause, |
155 | 157 | latestJoins, |
156 | 158 | whereClause); |
157 | 159 | |
158 | - int totalElements = ((BigInteger) entityManager.createNativeQuery(String.format("select count(*) %s", fromClause)) | |
159 | - .getSingleResult()).intValue(); | |
160 | + | |
161 | + int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) %s", fromClause), ctx, Integer.class); | |
160 | 162 | |
161 | 163 | String dataQuery = String.format("select * %s", fromClause); |
162 | 164 | |
... | ... | @@ -177,17 +179,18 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
177 | 179 | if (pageLink.getPageSize() > 0) { |
178 | 180 | dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); |
179 | 181 | } |
180 | - List rows = entityManager.createNativeQuery(dataQuery).getResultList(); | |
182 | + List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx); | |
181 | 183 | return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements); |
182 | 184 | } |
183 | 185 | |
184 | - private String buildEntityWhere(TenantId tenantId, | |
186 | + private String buildEntityWhere(EntityQueryContext ctx, | |
187 | + TenantId tenantId, | |
185 | 188 | CustomerId customerId, |
186 | 189 | EntityFilter entityFilter, |
187 | 190 | List<EntityKeyMapping> entityFieldsFilters, |
188 | 191 | EntityType entityType) { |
189 | - String permissionQuery = this.buildPermissionQuery(entityFilter, tenantId, customerId, entityType); | |
190 | - String entityFilterQuery = this.buildEntityFilterQuery(entityFilter); | |
192 | + String permissionQuery = this.buildPermissionQuery(ctx, entityFilter, tenantId, customerId, entityType); | |
193 | + String entityFilterQuery = this.buildEntityFilterQuery(ctx, entityFilter); | |
191 | 194 | String result = permissionQuery; |
192 | 195 | if (!entityFilterQuery.isEmpty()) { |
193 | 196 | result += " and " + entityFilterQuery; |
... | ... | @@ -198,35 +201,42 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
198 | 201 | return result; |
199 | 202 | } |
200 | 203 | |
201 | - private String buildPermissionQuery(EntityFilter entityFilter, TenantId tenantId, CustomerId customerId, EntityType entityType) { | |
204 | + private String buildPermissionQuery(EntityQueryContext ctx, EntityFilter entityFilter, TenantId tenantId, CustomerId customerId, EntityType entityType) { | |
202 | 205 | switch (entityFilter.getType()) { |
203 | 206 | case RELATIONS_QUERY: |
204 | 207 | case DEVICE_SEARCH_QUERY: |
205 | 208 | case ASSET_SEARCH_QUERY: |
206 | - return String.format("e.tenant_id='%s' and e.customer_id='%s'", tenantId.getId(), customerId.getId()); | |
209 | + ctx.addUuidParameter("permissions_tenant_id", tenantId.getId()); | |
210 | + ctx.addUuidParameter("permissions_customer_id", customerId.getId()); | |
211 | + return "e.tenant_id=:permissions_tenant_id and e.customer_id=:permissions_customer_id"; | |
207 | 212 | default: |
208 | 213 | if (entityType == EntityType.TENANT) { |
209 | - return String.format("e.id='%s'", tenantId.getId()); | |
214 | + ctx.addUuidParameter("permissions_tenant_id", tenantId.getId()); | |
215 | + return "e.id=:permissions_tenant_id"; | |
210 | 216 | } else if (entityType == EntityType.CUSTOMER) { |
211 | - return String.format("e.tenant_id='%s' and e.id='%s'", tenantId.getId(), customerId.getId()); | |
217 | + ctx.addUuidParameter("permissions_tenant_id", tenantId.getId()); | |
218 | + ctx.addUuidParameter("permissions_customer_id", customerId.getId()); | |
219 | + return "e.tenant_id=:permissions_tenant_id and e.id=:permissions_customer_id"; | |
212 | 220 | } else { |
213 | - return String.format("e.tenant_id='%s' and e.customer_id='%s'", tenantId.getId(), customerId.getId()); | |
221 | + ctx.addUuidParameter("permissions_tenant_id", tenantId.getId()); | |
222 | + ctx.addUuidParameter("permissions_customer_id", customerId.getId()); | |
223 | + return "e.tenant_id=:permissions_tenant_id and e.customer_id=:permissions_customer_id"; | |
214 | 224 | } |
215 | 225 | } |
216 | 226 | } |
217 | 227 | |
218 | - private String buildEntityFilterQuery(EntityFilter entityFilter) { | |
228 | + private String buildEntityFilterQuery(EntityQueryContext ctx, EntityFilter entityFilter) { | |
219 | 229 | switch (entityFilter.getType()) { |
220 | 230 | case SINGLE_ENTITY: |
221 | - return this.singleEntityQuery((SingleEntityFilter) entityFilter); | |
231 | + return this.singleEntityQuery(ctx, (SingleEntityFilter) entityFilter); | |
222 | 232 | case ENTITY_LIST: |
223 | - return this.entityListQuery((EntityListFilter) entityFilter); | |
233 | + return this.entityListQuery(ctx, (EntityListFilter) entityFilter); | |
224 | 234 | case ENTITY_NAME: |
225 | - return this.entityNameQuery((EntityNameFilter) entityFilter); | |
235 | + return this.entityNameQuery(ctx, (EntityNameFilter) entityFilter); | |
226 | 236 | case ASSET_TYPE: |
227 | 237 | case DEVICE_TYPE: |
228 | 238 | case ENTITY_VIEW_TYPE: |
229 | - return this.typeQuery(entityFilter); | |
239 | + return this.typeQuery(ctx, entityFilter); | |
230 | 240 | case RELATIONS_QUERY: |
231 | 241 | case DEVICE_SEARCH_QUERY: |
232 | 242 | case ASSET_SEARCH_QUERY: |
... | ... | @@ -236,53 +246,60 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
236 | 246 | } |
237 | 247 | } |
238 | 248 | |
239 | - private String getEntityTableQuery(EntityFilter entityFilter, EntityType entityType) { | |
249 | + private String addEntityTableQuery(EntityQueryContext ctx, EntityFilter entityFilter, EntityType entityType) { | |
240 | 250 | switch (entityFilter.getType()) { |
241 | 251 | case RELATIONS_QUERY: |
242 | - return relationQuery((RelationsQueryFilter) entityFilter); | |
252 | + return relationQuery(ctx, (RelationsQueryFilter) entityFilter); | |
243 | 253 | case DEVICE_SEARCH_QUERY: |
244 | 254 | DeviceSearchQueryFilter deviceQuery = (DeviceSearchQueryFilter) entityFilter; |
245 | - return entitySearchQuery(deviceQuery, EntityType.DEVICE, deviceQuery.getDeviceTypes()); | |
255 | + return entitySearchQuery(ctx, deviceQuery, EntityType.DEVICE, deviceQuery.getDeviceTypes()); | |
246 | 256 | case ASSET_SEARCH_QUERY: |
247 | 257 | AssetSearchQueryFilter assetQuery = (AssetSearchQueryFilter) entityFilter; |
248 | - return entitySearchQuery(assetQuery, EntityType.ASSET, assetQuery.getAssetTypes()); | |
258 | + return entitySearchQuery(ctx, assetQuery, EntityType.ASSET, assetQuery.getAssetTypes()); | |
249 | 259 | default: |
250 | 260 | return entityTableMap.get(entityType); |
251 | 261 | } |
252 | 262 | } |
253 | 263 | |
254 | - private String entitySearchQuery(EntitySearchQueryFilter entityFilter, EntityType entityType, List<String> types) { | |
264 | + private String entitySearchQuery(EntityQueryContext ctx, EntitySearchQueryFilter entityFilter, EntityType entityType, List<String> types) { | |
255 | 265 | EntityId rootId = entityFilter.getRootEntity(); |
256 | 266 | //TODO: fetch last level only. |
257 | 267 | //TODO: fetch distinct records. |
258 | 268 | String lvlFilter = getLvlFilter(entityFilter.getMaxLevel()); |
259 | 269 | String selectFields = "SELECT tenant_id, customer_id, id, type, name, label FROM " + entityType.name() + " WHERE id in ( SELECT entity_id"; |
260 | 270 | String from = getQueryTemplate(entityFilter.getDirection()); |
271 | + String whereFilter = " WHERE re.relation_type = :where_relation_type AND re.to_type = :where_entity_type"; | |
261 | 272 | |
262 | - String whereFilter = " WHERE " + " re.relation_type = '" + entityFilter.getRelationType() + "'" + | |
263 | - " AND re.to_type = '" + entityType.name() + "'"; | |
264 | - from = String.format(from, rootId.getId(), rootId.getEntityType().name(), lvlFilter, whereFilter); | |
273 | + from = String.format(from, lvlFilter, whereFilter); | |
265 | 274 | String query = "( " + selectFields + from + ")"; |
266 | 275 | if (types != null && !types.isEmpty()) { |
267 | - query += " and type in (" + types.stream().map(type -> "'" + type + "'").collect(Collectors.joining(", ")) + ")"; | |
276 | + query += " and type in (:relation_sub_types)"; | |
277 | + ctx.addStringListParameter("relation_sub_types", types); | |
268 | 278 | } |
269 | 279 | query += " )"; |
280 | + ctx.addUuidParameter("relation_root_id", rootId.getId()); | |
281 | + ctx.addStringParameter("relation_root_type", rootId.getEntityType().name()); | |
282 | + ctx.addStringParameter("where_relation_type", entityFilter.getRelationType()); | |
283 | + ctx.addStringParameter("where_entity_type", entityType.name()); | |
270 | 284 | return query; |
271 | 285 | } |
272 | 286 | |
273 | - private String relationQuery(RelationsQueryFilter entityFilter) { | |
287 | + private String relationQuery(EntityQueryContext ctx, RelationsQueryFilter entityFilter) { | |
274 | 288 | EntityId rootId = entityFilter.getRootEntity(); |
275 | 289 | String lvlFilter = getLvlFilter(entityFilter.getMaxLevel()); |
276 | 290 | String selectFields = getSelectTenantId() + ", " + getSelectCustomerId() + ", " + |
277 | 291 | " entity.entity_id as id," + getSelectType() + ", " + getSelectName() + ", " + |
278 | 292 | getSelectLabel() + ", entity.entity_type as entity_type"; |
279 | 293 | String from = getQueryTemplate(entityFilter.getDirection()); |
294 | + ctx.addUuidParameter("relation_root_id", rootId.getId()); | |
295 | + ctx.addStringParameter("relation_root_type", rootId.getEntityType().name()); | |
280 | 296 | |
281 | 297 | StringBuilder whereFilter; |
282 | 298 | if (entityFilter.getFilters() != null && !entityFilter.getFilters().isEmpty()) { |
283 | 299 | whereFilter = new StringBuilder(" WHERE "); |
284 | 300 | boolean first = true; |
285 | 301 | boolean single = entityFilter.getFilters().size() == 1; |
302 | + int entityTypeFilterIdx = 0; | |
286 | 303 | for (EntityTypeFilter etf : entityFilter.getFilters()) { |
287 | 304 | if (first) { |
288 | 305 | first = false; |
... | ... | @@ -290,21 +307,23 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
290 | 307 | whereFilter.append(" AND "); |
291 | 308 | } |
292 | 309 | String relationType = etf.getRelationType(); |
293 | - String entityTypes = etf.getEntityTypes().stream().map(type -> "'" + type + "'").collect(Collectors.joining(", ")); | |
294 | 310 | if (!single) { |
295 | 311 | whereFilter.append(" ("); |
296 | 312 | } |
297 | - whereFilter.append(" re.relation_type = '").append(relationType).append("' and re.") | |
313 | + whereFilter.append(" re.relation_type = :where_relation_type").append(entityTypeFilterIdx).append(" and re.") | |
298 | 314 | .append(entityFilter.getDirection().equals(EntitySearchDirection.FROM) ? "to" : "from") |
299 | - .append("_type in (").append(entityTypes).append(")"); | |
315 | + .append("_type in (:where_entity_types").append(entityTypeFilterIdx).append(")"); | |
300 | 316 | if (!single) { |
301 | 317 | whereFilter.append(" )"); |
302 | 318 | } |
319 | + ctx.addStringParameter("where_relation_type" + entityTypeFilterIdx, relationType); | |
320 | + ctx.addStringListParameter("where_entity_types" + entityTypeFilterIdx, etf.getEntityTypes().stream().map(EntityType::name).collect(Collectors.toList())); | |
321 | + entityTypeFilterIdx++; | |
303 | 322 | } |
304 | 323 | } else { |
305 | 324 | whereFilter = new StringBuilder(); |
306 | 325 | } |
307 | - from = String.format(from, rootId.getId(), rootId.getEntityType().name(), lvlFilter, whereFilter); | |
326 | + from = String.format(from, lvlFilter, whereFilter); | |
308 | 327 | return "( " + selectFields + from + ")"; |
309 | 328 | } |
310 | 329 | |
... | ... | @@ -410,10 +429,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
410 | 429 | " END as label"; |
411 | 430 | } |
412 | 431 | |
413 | - private String buildWhere | |
414 | - (List<EntityKeyMapping> selectionMapping, List<EntityKeyMapping> latestFiltersMapping, String searchText) { | |
415 | - String latestFilters = EntityKeyMapping.buildQuery(latestFiltersMapping); | |
416 | - String textSearchQuery = this.buildTextSearchQuery(selectionMapping, searchText); | |
432 | + private String buildWhere(EntityQueryContext ctx, List<EntityKeyMapping> selectionMapping, List<EntityKeyMapping> latestFiltersMapping, String searchText) { | |
433 | + String latestFilters = EntityKeyMapping.buildQuery(ctx, latestFiltersMapping); | |
434 | + String textSearchQuery = this.buildTextSearchQuery(ctx, selectionMapping, searchText); | |
417 | 435 | String query; |
418 | 436 | if (!StringUtils.isEmpty(latestFilters) && !StringUtils.isEmpty(textSearchQuery)) { |
419 | 437 | query = String.join(" AND ", latestFilters, textSearchQuery); |
... | ... | @@ -429,32 +447,38 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
429 | 447 | } |
430 | 448 | } |
431 | 449 | |
432 | - private String buildTextSearchQuery(List<EntityKeyMapping> selectionMapping, String searchText) { | |
450 | + private String buildTextSearchQuery(EntityQueryContext ctx, List<EntityKeyMapping> selectionMapping, String searchText) { | |
433 | 451 | if (!StringUtils.isEmpty(searchText) && !selectionMapping.isEmpty()) { |
434 | 452 | String lowerSearchText = searchText.toLowerCase() + "%"; |
435 | - List<String> searchPredicates = selectionMapping.stream().map(mapping -> String.format("LOWER(%s) LIKE '%s'", | |
436 | - mapping.getValueAlias(), lowerSearchText)).collect(Collectors.toList()); | |
453 | + List<String> searchPredicates = selectionMapping.stream().map(mapping -> { | |
454 | + String paramName = mapping.getValueAlias() + "_lowerSearchText"; | |
455 | + ctx.addStringParameter(paramName, lowerSearchText); | |
456 | + return String.format("LOWER(%s) LIKE :%s", mapping.getValueAlias(), paramName); | |
457 | + } | |
458 | + ).collect(Collectors.toList()); | |
437 | 459 | return String.format("(%s)", String.join(" or ", searchPredicates)); |
438 | 460 | } else { |
439 | 461 | return null; |
440 | 462 | } |
463 | + | |
441 | 464 | } |
442 | 465 | |
443 | - private String singleEntityQuery(SingleEntityFilter filter) { | |
444 | - return String.format("e.id='%s'", filter.getSingleEntity().getId()); | |
466 | + private String singleEntityQuery(EntityQueryContext ctx, SingleEntityFilter filter) { | |
467 | + ctx.addUuidParameter("entity_filter_single_entity_id", filter.getSingleEntity().getId()); | |
468 | + return "e.id=:entity_filter_single_entity_id"; | |
445 | 469 | } |
446 | 470 | |
447 | - private String entityListQuery(EntityListFilter filter) { | |
448 | - return String.format("e.id in (%s)", | |
449 | - filter.getEntityList().stream().map(UUID::fromString) | |
450 | - .map(s -> String.format("'%s'", s)).collect(Collectors.joining(","))); | |
471 | + private String entityListQuery(EntityQueryContext ctx, EntityListFilter filter) { | |
472 | + ctx.addUuidListParameter("entity_filter_entity_ids", filter.getEntityList().stream().map(UUID::fromString).collect(Collectors.toList())); | |
473 | + return "e.id in (:entity_filter_entity_ids)"; | |
451 | 474 | } |
452 | 475 | |
453 | - private String entityNameQuery(EntityNameFilter filter) { | |
454 | - return String.format("lower(e.search_text) like lower(concat(%s, '%%'))", filter.getEntityNameFilter()); | |
476 | + private String entityNameQuery(EntityQueryContext ctx, EntityNameFilter filter) { | |
477 | + ctx.addStringParameter("entity_filter_name_filter", filter.getEntityNameFilter()); | |
478 | + return "lower(e.search_text) like lower(concat(:entity_filter_name_filter, '%%'))"; | |
455 | 479 | } |
456 | 480 | |
457 | - private String typeQuery(EntityFilter filter) { | |
481 | + private String typeQuery(EntityQueryContext ctx, EntityFilter filter) { | |
458 | 482 | String type; |
459 | 483 | String name; |
460 | 484 | switch (filter.getType()) { |
... | ... | @@ -473,7 +497,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
473 | 497 | default: |
474 | 498 | throw new RuntimeException("Not supported!"); |
475 | 499 | } |
476 | - return String.format("e.type = '%s' and lower(e.search_text) like lower(concat('%s', '%%'))", type, name); | |
500 | + ctx.addStringParameter("entity_filter_type_query_type", type); | |
501 | + ctx.addStringParameter("entity_filter_type_query_name", name); | |
502 | + return "e.type = :entity_filter_type_query_type and lower(e.search_text) like lower(concat(:entity_filter_type_query_name, '%%'))"; | |
477 | 503 | } |
478 | 504 | |
479 | 505 | private EntityType resolveEntityType(EntityFilter entityFilter) { | ... | ... |
... | ... | @@ -37,31 +37,30 @@ public class EntityDataAdapter { |
37 | 37 | |
38 | 38 | public static PageData<EntityData> createEntityData(EntityDataPageLink pageLink, |
39 | 39 | List<EntityKeyMapping> selectionMapping, |
40 | - List<Object[]> rows, | |
40 | + List<Map<String, Object>> rows, | |
41 | 41 | int totalElements) { |
42 | - int totalPages = pageLink.getPageSize() > 0 ? (int)Math.ceil((float)totalElements / pageLink.getPageSize()) : 1; | |
42 | + int totalPages = pageLink.getPageSize() > 0 ? (int) Math.ceil((float) totalElements / pageLink.getPageSize()) : 1; | |
43 | 43 | int startIndex = pageLink.getPageSize() * pageLink.getPage(); |
44 | 44 | boolean hasNext = pageLink.getPageSize() > 0 && totalElements > startIndex + rows.size(); |
45 | 45 | List<EntityData> entitiesData = convertListToEntityData(rows, selectionMapping); |
46 | 46 | return new PageData<>(entitiesData, totalPages, totalElements, hasNext); |
47 | 47 | } |
48 | 48 | |
49 | - private static List<EntityData> convertListToEntityData(List<Object[]> result, List<EntityKeyMapping> selectionMapping) { | |
49 | + private static List<EntityData> convertListToEntityData(List<Map<String, Object>> result, List<EntityKeyMapping> selectionMapping) { | |
50 | 50 | return result.stream().map(row -> toEntityData(row, selectionMapping)).collect(Collectors.toList()); |
51 | 51 | } |
52 | 52 | |
53 | - private static EntityData toEntityData(Object[] row, List<EntityKeyMapping> selectionMapping) { | |
54 | - ByteBuffer bb = ByteBuffer.wrap((byte[])row[0]); | |
55 | - UUID id = new UUID(bb.getLong(), bb.getLong()); | |
56 | - EntityType entityType = EntityType.valueOf((String)row[1]); | |
53 | + private static EntityData toEntityData(Map<String, Object> row, List<EntityKeyMapping> selectionMapping) { | |
54 | + UUID id = (UUID)row.get("id"); | |
55 | + EntityType entityType = EntityType.valueOf((String) row.get("entity_type")); | |
57 | 56 | EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id); |
58 | 57 | Map<EntityKeyType, Map<String, TsValue>> latest = new HashMap<>(); |
59 | 58 | Map<String, TsValue[]> timeseries = new HashMap<>(); |
60 | 59 | EntityData entityData = new EntityData(entityId, latest, timeseries); |
61 | - for (EntityKeyMapping mapping: selectionMapping) { | |
60 | + for (EntityKeyMapping mapping : selectionMapping) { | |
62 | 61 | if (!mapping.isIgnore()) { |
63 | 62 | EntityKey entityKey = mapping.getEntityKey(); |
64 | - Object value = row[mapping.getIndex()]; | |
63 | + Object value = row.get(mapping.getValueAlias()); | |
65 | 64 | String strValue; |
66 | 65 | long ts; |
67 | 66 | if (entityKey.getType().equals(EntityKeyType.ENTITY_FIELD)) { |
... | ... | @@ -69,7 +68,7 @@ public class EntityDataAdapter { |
69 | 68 | ts = System.currentTimeMillis(); |
70 | 69 | } else { |
71 | 70 | strValue = convertValue(value); |
72 | - Object tsObject = row[mapping.getIndex() + 1]; | |
71 | + Object tsObject = row.get(mapping.getTsAlias()); | |
73 | 72 | ts = tsObject != null ? Long.parseLong(tsObject.toString()) : 0; |
74 | 73 | } |
75 | 74 | TsValue tsValue = new TsValue(ts, strValue); | ... | ... |
... | ... | @@ -72,6 +72,7 @@ public class EntityKeyMapping { |
72 | 72 | private boolean ignore = false; |
73 | 73 | private List<KeyFilter> keyFilters; |
74 | 74 | private EntityKey entityKey; |
75 | + private int paramIdx = 0; | |
75 | 76 | |
76 | 77 | public boolean hasFilter() { |
77 | 78 | return keyFilters != null && !keyFilters.isEmpty(); |
... | ... | @@ -100,17 +101,17 @@ public class EntityKeyMapping { |
100 | 101 | } |
101 | 102 | } |
102 | 103 | |
103 | - public Stream<String> toQueries() { | |
104 | + public Stream<String> toQueries(EntityQueryContext ctx) { | |
104 | 105 | if (hasFilter()) { |
105 | 106 | String keyAlias = entityKey.getType().equals(EntityKeyType.ENTITY_FIELD) ? "e" : alias; |
106 | 107 | return keyFilters.stream().map(keyFilter -> |
107 | - this.buildKeyQuery(keyAlias, keyFilter)); | |
108 | + this.buildKeyQuery(ctx, keyAlias, keyFilter)); | |
108 | 109 | } else { |
109 | 110 | return null; |
110 | 111 | } |
111 | 112 | } |
112 | 113 | |
113 | - public String toLatestJoin(EntityFilter entityFilter, EntityType entityType) { | |
114 | + public String toLatestJoin(EntityQueryContext ctx, EntityFilter entityFilter, EntityType entityType) { | |
114 | 115 | String entityTypeStr; |
115 | 116 | if (entityFilter.getType().equals(EntityFilterType.RELATIONS_QUERY)) { |
116 | 117 | entityTypeStr = "entities.entity_type"; |
... | ... | @@ -118,12 +119,13 @@ public class EntityKeyMapping { |
118 | 119 | entityTypeStr = "'" + entityType.name() + "'"; |
119 | 120 | } |
120 | 121 | String join = hasFilter() ? "left join" : "left outer join"; |
122 | + ctx.addStringParameter(alias + "_key_id", entityKey.getKey()); | |
121 | 123 | if (entityKey.getType().equals(EntityKeyType.TIME_SERIES)) { |
122 | - return String.format("%s ts_kv_latest %s ON %s.entity_id=to_uuid(entities.id) AND %s.key = (select key_id from ts_kv_dictionary where key = '%s')", | |
123 | - join, alias, alias, alias, entityKey.getKey()); | |
124 | + return String.format("%s ts_kv_latest %s ON %s.entity_id=to_uuid(entities.id) AND %s.key = (select key_id from ts_kv_dictionary where key = :%s_key_id)", | |
125 | + join, alias, alias, alias, alias); | |
124 | 126 | } else { |
125 | - String query = String.format("%s attribute_kv %s ON %s.entity_id=entities.id AND %s.entity_type=%s AND %s.attribute_key='%s'", | |
126 | - join, alias, alias, alias, entityTypeStr, alias, entityKey.getKey()); | |
127 | + String query = String.format("%s attribute_kv %s ON %s.entity_id=entities.id AND %s.entity_type=%s AND %s.attribute_key=:%s_key_id", | |
128 | + join, alias, alias, alias, entityTypeStr, alias, alias); | |
127 | 129 | if (!entityKey.getType().equals(EntityKeyType.ATTRIBUTE)) { |
128 | 130 | String scope; |
129 | 131 | if (entityKey.getType().equals(EntityKeyType.CLIENT_ATTRIBUTE)) { |
... | ... | @@ -144,13 +146,13 @@ public class EntityKeyMapping { |
144 | 146 | Collectors.joining(", ")); |
145 | 147 | } |
146 | 148 | |
147 | - public static String buildLatestJoins(EntityFilter entityFilter, EntityType entityType, List<EntityKeyMapping> latestMappings) { | |
148 | - return latestMappings.stream().map(mapping -> mapping.toLatestJoin(entityFilter, entityType)).collect( | |
149 | + public static String buildLatestJoins(EntityQueryContext ctx, EntityFilter entityFilter, EntityType entityType, List<EntityKeyMapping> latestMappings) { | |
150 | + return latestMappings.stream().map(mapping -> mapping.toLatestJoin(ctx, entityFilter, entityType)).collect( | |
149 | 151 | Collectors.joining(" ")); |
150 | 152 | } |
151 | 153 | |
152 | - public static String buildQuery(List<EntityKeyMapping> mappings) { | |
153 | - return mappings.stream().flatMap(EntityKeyMapping::toQueries).collect( | |
154 | + public static String buildQuery(EntityQueryContext ctx, List<EntityKeyMapping> mappings) { | |
155 | + return mappings.stream().flatMap(mapping -> mapping.toQueries(ctx)).collect( | |
154 | 156 | Collectors.joining(" AND ")); |
155 | 157 | } |
156 | 158 | |
... | ... | @@ -262,33 +264,33 @@ public class EntityKeyMapping { |
262 | 264 | return String.join(", ", attrValSelection, attrTsSelection); |
263 | 265 | } |
264 | 266 | |
265 | - private String buildKeyQuery(String alias, KeyFilter keyFilter) { | |
266 | - return this.buildPredicateQuery(alias, keyFilter.getKey(), keyFilter.getPredicate()); | |
267 | + private String buildKeyQuery(EntityQueryContext ctx, String alias, KeyFilter keyFilter) { | |
268 | + return this.buildPredicateQuery(ctx, alias, keyFilter.getKey(), keyFilter.getPredicate()); | |
267 | 269 | } |
268 | 270 | |
269 | - private String buildPredicateQuery(String alias, EntityKey key, KeyFilterPredicate predicate) { | |
271 | + private String buildPredicateQuery(EntityQueryContext ctx, String alias, EntityKey key, KeyFilterPredicate predicate) { | |
270 | 272 | if (predicate.getType().equals(FilterPredicateType.COMPLEX)) { |
271 | - return this.buildComplexPredicateQuery(alias, key, (ComplexFilterPredicate) predicate); | |
273 | + return this.buildComplexPredicateQuery(ctx, alias, key, (ComplexFilterPredicate) predicate); | |
272 | 274 | } else { |
273 | - return this.buildSimplePredicateQuery(alias, key, predicate); | |
275 | + return this.buildSimplePredicateQuery(ctx, alias, key, predicate); | |
274 | 276 | } |
275 | 277 | } |
276 | 278 | |
277 | - private String buildComplexPredicateQuery(String alias, EntityKey key, ComplexFilterPredicate predicate) { | |
279 | + private String buildComplexPredicateQuery(EntityQueryContext ctx, String alias, EntityKey key, ComplexFilterPredicate predicate) { | |
278 | 280 | return predicate.getPredicates().stream() |
279 | - .map(keyFilterPredicate -> this.buildPredicateQuery(alias, key, keyFilterPredicate)).collect(Collectors.joining( | |
281 | + .map(keyFilterPredicate -> this.buildPredicateQuery(ctx, alias, key, keyFilterPredicate)).collect(Collectors.joining( | |
280 | 282 | " " + predicate.getOperation().name() + " " |
281 | 283 | )); |
282 | 284 | } |
283 | 285 | |
284 | - private String buildSimplePredicateQuery(String alias, EntityKey key, KeyFilterPredicate predicate) { | |
286 | + private String buildSimplePredicateQuery(EntityQueryContext ctx, String alias, EntityKey key, KeyFilterPredicate predicate) { | |
285 | 287 | if (predicate.getType().equals(FilterPredicateType.NUMERIC)) { |
286 | 288 | if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) { |
287 | 289 | String column = entityFieldColumnMap.get(key.getKey()); |
288 | - return this.buildNumericPredicateQuery(alias + "." + column, (NumericFilterPredicate) predicate); | |
290 | + return this.buildNumericPredicateQuery(ctx, alias + "." + column, (NumericFilterPredicate) predicate); | |
289 | 291 | } else { |
290 | - String longQuery = this.buildNumericPredicateQuery(alias + ".long_v", (NumericFilterPredicate) predicate); | |
291 | - String doubleQuery = this.buildNumericPredicateQuery(alias + ".dbl_v", (NumericFilterPredicate) predicate); | |
292 | + String longQuery = this.buildNumericPredicateQuery(ctx, alias + ".long_v", (NumericFilterPredicate) predicate); | |
293 | + String doubleQuery = this.buildNumericPredicateQuery(ctx, alias + ".dbl_v", (NumericFilterPredicate) predicate); | |
292 | 294 | return String.format("(%s or %s)", longQuery, doubleQuery); |
293 | 295 | } |
294 | 296 | } else { |
... | ... | @@ -300,15 +302,16 @@ public class EntityKeyMapping { |
300 | 302 | } |
301 | 303 | String field = alias + "." + column; |
302 | 304 | if (predicate.getType().equals(FilterPredicateType.STRING)) { |
303 | - return this.buildStringPredicateQuery(field, (StringFilterPredicate) predicate); | |
305 | + return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate); | |
304 | 306 | } else { |
305 | - return this.buildBooleanPredicateQuery(field, (BooleanFilterPredicate) predicate); | |
307 | + return this.buildBooleanPredicateQuery(ctx, field, (BooleanFilterPredicate) predicate); | |
306 | 308 | } |
307 | 309 | } |
308 | 310 | } |
309 | 311 | |
310 | - private String buildStringPredicateQuery(String field, StringFilterPredicate stringFilterPredicate) { | |
312 | + private String buildStringPredicateQuery(EntityQueryContext ctx, String field, StringFilterPredicate stringFilterPredicate) { | |
311 | 313 | String operationField = field; |
314 | + String paramName = getNextParameterName(field); | |
312 | 315 | String value = stringFilterPredicate.getValue(); |
313 | 316 | String stringOperationQuery = ""; |
314 | 317 | if (stringFilterPredicate.isIgnoreCase()) { |
... | ... | @@ -317,65 +320,77 @@ public class EntityKeyMapping { |
317 | 320 | } |
318 | 321 | switch (stringFilterPredicate.getOperation()) { |
319 | 322 | case EQUAL: |
320 | - stringOperationQuery = String.format("%s = '%s'", operationField, value); | |
323 | + stringOperationQuery = String.format("%s = :%s", operationField, paramName); | |
321 | 324 | break; |
322 | 325 | case NOT_EQUAL: |
323 | - stringOperationQuery = String.format("%s != '%s'", operationField, value); | |
326 | + stringOperationQuery = String.format("%s != :%s", operationField, paramName); | |
324 | 327 | break; |
325 | 328 | case STARTS_WITH: |
326 | - stringOperationQuery = String.format("%s like '%s%%'", operationField, value); | |
329 | + value += "%"; | |
330 | + stringOperationQuery = String.format("%s like :%s", operationField, paramName); | |
327 | 331 | break; |
328 | 332 | case ENDS_WITH: |
329 | - stringOperationQuery = String.format("%s like '%%%s'", operationField, value); | |
333 | + value = "%" + value; | |
334 | + stringOperationQuery = String.format("%s like :%s", operationField, paramName); | |
330 | 335 | break; |
331 | 336 | case CONTAINS: |
332 | - stringOperationQuery = String.format("%s like '%%%s%%'", operationField, value); | |
337 | + value = "%" + value + "%"; | |
338 | + stringOperationQuery = String.format("%s like :%s", operationField, paramName); | |
333 | 339 | break; |
334 | 340 | case NOT_CONTAINS: |
335 | - stringOperationQuery = String.format("%s not like '%%%s%%'", operationField, value); | |
341 | + value = "%" + value + "%"; | |
342 | + stringOperationQuery = String.format("%s not like :%s", operationField, paramName); | |
336 | 343 | break; |
337 | 344 | } |
345 | + ctx.addStringParameter(paramName, value); | |
338 | 346 | return String.format("(%s is not null and %s)", field, stringOperationQuery); |
339 | 347 | } |
340 | 348 | |
341 | - private String buildNumericPredicateQuery(String field, NumericFilterPredicate numericFilterPredicate) { | |
342 | - double value = numericFilterPredicate.getValue(); | |
349 | + private String buildNumericPredicateQuery(EntityQueryContext ctx, String field, NumericFilterPredicate numericFilterPredicate) { | |
350 | + String paramName = getNextParameterName(field); | |
351 | + ctx.addDoubleParameter(paramName, numericFilterPredicate.getValue()); | |
343 | 352 | String numericOperationQuery = ""; |
344 | 353 | switch (numericFilterPredicate.getOperation()) { |
345 | 354 | case EQUAL: |
346 | - numericOperationQuery = String.format("%s = %s", field, value); | |
355 | + numericOperationQuery = String.format("%s = :%s", field, paramName); | |
347 | 356 | break; |
348 | 357 | case NOT_EQUAL: |
349 | - numericOperationQuery = String.format("%s != '%s'", field, value); | |
358 | + numericOperationQuery = String.format("%s != :%s", field, paramName); | |
350 | 359 | break; |
351 | 360 | case GREATER: |
352 | - numericOperationQuery = String.format("%s > %s", field, value); | |
361 | + numericOperationQuery = String.format("%s > :%s", field, paramName); | |
353 | 362 | break; |
354 | 363 | case GREATER_OR_EQUAL: |
355 | - numericOperationQuery = String.format("%s >= %s", field, value); | |
364 | + numericOperationQuery = String.format("%s >= :%s", field, paramName); | |
356 | 365 | break; |
357 | 366 | case LESS: |
358 | - numericOperationQuery = String.format("%s < %s", field, value); | |
367 | + numericOperationQuery = String.format("%s < :%s", field, paramName); | |
359 | 368 | break; |
360 | 369 | case LESS_OR_EQUAL: |
361 | - numericOperationQuery = String.format("%s <= %s", field, value); | |
370 | + numericOperationQuery = String.format("%s <= :%s", field, paramName); | |
362 | 371 | break; |
363 | 372 | } |
364 | 373 | return String.format("(%s is not null and %s)", field, numericOperationQuery); |
365 | 374 | } |
366 | 375 | |
367 | - private String buildBooleanPredicateQuery(String field, | |
376 | + private String buildBooleanPredicateQuery(EntityQueryContext ctx, String field, | |
368 | 377 | BooleanFilterPredicate booleanFilterPredicate) { |
369 | - boolean value = booleanFilterPredicate.isValue(); | |
378 | + String paramName = getNextParameterName(field); | |
379 | + ctx.addBooleanParameter(paramName, booleanFilterPredicate.isValue()); | |
370 | 380 | String booleanOperationQuery = ""; |
371 | 381 | switch (booleanFilterPredicate.getOperation()) { |
372 | 382 | case EQUAL: |
373 | - booleanOperationQuery = String.format("%s = %s", field, value); | |
383 | + booleanOperationQuery = String.format("%s = :%s", field, paramName); | |
374 | 384 | break; |
375 | 385 | case NOT_EQUAL: |
376 | - booleanOperationQuery = String.format("%s != %s", field, value); | |
386 | + booleanOperationQuery = String.format("%s != :%s", field, paramName); | |
377 | 387 | break; |
378 | 388 | } |
379 | 389 | return String.format("(%s is not null and %s)", field, booleanOperationQuery); |
380 | 390 | } |
391 | + | |
392 | + private String getNextParameterName(String field) { | |
393 | + paramIdx++; | |
394 | + return field.replace(".", "_") + "_" + paramIdx; | |
395 | + } | |
381 | 396 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao.sql.query; | |
17 | + | |
18 | +import org.hibernate.type.PostgresUUIDType; | |
19 | +import org.springframework.jdbc.core.namedparam.SqlParameterSource; | |
20 | + | |
21 | +import java.sql.Types; | |
22 | +import java.util.HashMap; | |
23 | +import java.util.List; | |
24 | +import java.util.Map; | |
25 | +import java.util.UUID; | |
26 | + | |
27 | +public class EntityQueryContext implements SqlParameterSource { | |
28 | + private static final PostgresUUIDType UUID_TYPE = new PostgresUUIDType(); | |
29 | + | |
30 | + private final StringBuilder query; | |
31 | + private final Map<String, Parameter> params; | |
32 | + | |
33 | + public EntityQueryContext() { | |
34 | + query = new StringBuilder(); | |
35 | + params = new HashMap<>(); | |
36 | + } | |
37 | + | |
38 | + void addParameter(String name, Object value, int type, String typeName) { | |
39 | + Parameter existing = params.put(name, new Parameter(value, type, typeName)); | |
40 | + if (existing != null) { | |
41 | + throw new RuntimeException("Parameter with name: " + name + " was already registered!"); | |
42 | + } | |
43 | + } | |
44 | + | |
45 | + public void append(String s) { | |
46 | + query.append(s); | |
47 | + } | |
48 | + | |
49 | + @Override | |
50 | + public boolean hasValue(String paramName) { | |
51 | + return params.containsKey(paramName); | |
52 | + } | |
53 | + | |
54 | + @Override | |
55 | + public Object getValue(String paramName) throws IllegalArgumentException { | |
56 | + return checkParameter(paramName).value; | |
57 | + } | |
58 | + | |
59 | + @Override | |
60 | + public int getSqlType(String paramName) { | |
61 | + return checkParameter(paramName).type; | |
62 | + } | |
63 | + | |
64 | + private Parameter checkParameter(String paramName) { | |
65 | + Parameter param = params.get(paramName); | |
66 | + if (param == null) { | |
67 | + throw new RuntimeException("Parameter with name: " + paramName + " is not set!"); | |
68 | + } | |
69 | + return param; | |
70 | + } | |
71 | + | |
72 | + @Override | |
73 | + public String getTypeName(String paramName) { | |
74 | + return params.get(paramName).name; | |
75 | + } | |
76 | + | |
77 | + @Override | |
78 | + public String[] getParameterNames() { | |
79 | + return params.keySet().toArray(new String[]{}); | |
80 | + } | |
81 | + | |
82 | + public void addUuidParameter(String name, UUID value) { | |
83 | + addParameter(name, value, UUID_TYPE.sqlType(), UUID_TYPE.getName()); | |
84 | + } | |
85 | + | |
86 | + public void addStringParameter(String name, String value) { | |
87 | + addParameter(name, value, Types.VARCHAR, "VARCHAR"); | |
88 | + } | |
89 | + | |
90 | + public void addDoubleParameter(String name, double value) { | |
91 | + addParameter(name, value, Types.DOUBLE, "DOUBLE"); | |
92 | + } | |
93 | + | |
94 | + public void addStringListParameter(String name, List<String> value) { | |
95 | + addParameter(name, value, Types.VARCHAR, "VARCHAR"); | |
96 | + } | |
97 | + | |
98 | + public void addBooleanParameter(String name, boolean value) { | |
99 | + addParameter(name, value, Types.BOOLEAN, "BOOLEAN"); | |
100 | + } | |
101 | + | |
102 | + public void addUuidListParameter(String name, List<UUID> value) { | |
103 | + addParameter(name, value, UUID_TYPE.sqlType(), UUID_TYPE.getName()); | |
104 | + } | |
105 | + | |
106 | + public String getQuery() { | |
107 | + return query.toString(); | |
108 | + } | |
109 | + | |
110 | + | |
111 | + public static class Parameter { | |
112 | + private final Object value; | |
113 | + private final int type; | |
114 | + private final String name; | |
115 | + | |
116 | + public Parameter(Object value, int type, String name) { | |
117 | + this.value = value; | |
118 | + this.type = type; | |
119 | + this.name = name; | |
120 | + } | |
121 | + } | |
122 | +} | ... | ... |
... | ... | @@ -24,32 +24,32 @@ import java.util.Arrays; |
24 | 24 | |
25 | 25 | @RunWith(ClasspathSuite.class) |
26 | 26 | @ClassnameFilters({ |
27 | - "org.thingsboard.server.dao.service.sql.EntityServiceSqlTest" | |
27 | + "org.thingsboard.server.dao.service.sql.*SqlTest" | |
28 | 28 | }) |
29 | 29 | public class SqlDaoServiceTestSuite { |
30 | 30 | |
31 | -// @ClassRule | |
32 | -// public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | |
33 | -// Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql" | |
34 | -// , "sql/system-data.sql" | |
35 | -// , "sql/system-test.sql" | |
36 | -// ), | |
37 | -// "sql/hsql/drop-all-tables.sql", | |
38 | -// "sql-test.properties" | |
39 | -// ); | |
40 | - | |
41 | 31 | @ClassRule |
42 | 32 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
43 | - Arrays.asList("sql/schema-ts-psql.sql" | |
44 | - , "sql/schema-entities.sql", "sql/schema-entities-idx.sql" | |
45 | - , "sql/system-data.sql", "sql/system-test.sql" | |
33 | + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql" | |
34 | + , "sql/system-data.sql" | |
35 | + , "sql/system-test.sql" | |
46 | 36 | ), |
47 | - "sql/psql/drop-all-tables.sql", | |
37 | + "sql/hsql/drop-all-tables.sql", | |
48 | 38 | "sql-test.properties" |
49 | 39 | ); |
50 | 40 | |
51 | 41 | // @ClassRule |
52 | 42 | // public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
43 | +// Arrays.asList("sql/schema-ts-psql.sql" | |
44 | +// , "sql/schema-entities.sql", "sql/schema-entities-idx.sql" | |
45 | +// , "sql/system-data.sql", "sql/system-test.sql" | |
46 | +// ), | |
47 | +// "sql/psql/drop-all-tables.sql", | |
48 | +// "sql-test.properties" | |
49 | +// ); | |
50 | + | |
51 | +// @ClassRule | |
52 | +// public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | |
53 | 53 | // Arrays.asList("sql/schema-timescale.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"), |
54 | 54 | // "sql/timescale/drop-all-tables.sql", |
55 | 55 | // "sql-test.properties" | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture; |
20 | 20 | import org.junit.After; |
21 | 21 | import org.junit.Assert; |
22 | 22 | import org.junit.Before; |
23 | +import org.junit.Ignore; | |
23 | 24 | import org.junit.Test; |
24 | 25 | import org.springframework.beans.factory.annotation.Autowired; |
25 | 26 | import org.thingsboard.server.common.data.DataConstants; | ... | ... |
... | ... | @@ -3,18 +3,18 @@ database.ts.type=sql |
3 | 3 | sql.ts_inserts_executor_type=fixed |
4 | 4 | sql.ts_inserts_fixed_thread_pool_size=200 |
5 | 5 | sql.ts_key_value_partitioning=MONTHS |
6 | - | |
7 | -#spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true | |
8 | -#spring.jpa.properties.hibernate.order_by.default_null_ordering=last | |
9 | -#spring.jpa.show-sql=false | |
10 | -#spring.jpa.hibernate.ddl-auto=validate | |
11 | -#spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect | |
12 | 6 | # |
13 | -#spring.datasource.username=sa | |
14 | -#spring.datasource.password= | |
15 | -#spring.datasource.url=jdbc:hsqldb:file:/tmp/testDb;sql.enforce_size=false | |
16 | -#spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver | |
17 | -#spring.datasource.hikari.maximumPoolSize = 50 | |
7 | +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true | |
8 | +spring.jpa.properties.hibernate.order_by.default_null_ordering=last | |
9 | +spring.jpa.show-sql=false | |
10 | +spring.jpa.hibernate.ddl-auto=validate | |
11 | +spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect | |
12 | + | |
13 | +spring.datasource.username=sa | |
14 | +spring.datasource.password= | |
15 | +spring.datasource.url=jdbc:hsqldb:file:/tmp/testDb;sql.enforce_size=false | |
16 | +spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver | |
17 | +spring.datasource.hikari.maximumPoolSize = 50 | |
18 | 18 | |
19 | 19 | service.type=monolith |
20 | 20 | |
... | ... | @@ -26,16 +26,16 @@ service.type=monolith |
26 | 26 | #sql.ts_inserts_fixed_thread_pool_size=200 |
27 | 27 | #sql.ts_key_value_partitioning=MONTHS |
28 | 28 | # |
29 | -spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true | |
30 | -spring.jpa.show-sql=false | |
31 | -spring.jpa.hibernate.ddl-auto=none | |
32 | -spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect | |
33 | - | |
34 | -spring.datasource.username=postgres | |
35 | -spring.datasource.password=postgres | |
36 | -spring.datasource.url=jdbc:postgresql://localhost:5432/sqltest | |
37 | -spring.datasource.driverClassName=org.postgresql.Driver | |
38 | -spring.datasource.hikari.maximumPoolSize = 50 | |
29 | +#spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true | |
30 | +#spring.jpa.show-sql=false | |
31 | +#spring.jpa.hibernate.ddl-auto=none | |
32 | +#spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect | |
33 | +# | |
34 | +#spring.datasource.username=postgres | |
35 | +#spring.datasource.password=postgres | |
36 | +#spring.datasource.url=jdbc:postgresql://localhost:5432/sqltest | |
37 | +#spring.datasource.driverClassName=org.postgresql.Driver | |
38 | +#spring.datasource.hikari.maximumPoolSize = 50 | |
39 | 39 | |
40 | 40 | queue.core.pack-processing-timeout=3000 |
41 | 41 | queue.rule-engine.pack-processing-timeout=3000 | ... | ... |