Commit ac1ded94b70a1a4f8c8a1d2c29a9b35e3fac0d8a

Authored by Andrii Shvaika
1 parent 1f8d790f

UUID refactoring

@@ -20,12 +20,14 @@ import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; @@ -20,12 +20,14 @@ import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity;
20 import org.thingsboard.server.dao.util.HsqlDao; 20 import org.thingsboard.server.dao.util.HsqlDao;
21 import org.thingsboard.server.dao.util.SqlDao; 21 import org.thingsboard.server.dao.util.SqlDao;
22 22
  23 +import javax.persistence.Query;
  24 +
23 @SqlDao 25 @SqlDao
24 @HsqlDao 26 @HsqlDao
25 @Repository 27 @Repository
26 public class HsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository { 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 private static final String UNQ_KEY_CONFLICT_STATEMENT = "(component_descriptor.clazz=I.clazz)"; 31 private static final String UNQ_KEY_CONFLICT_STATEMENT = "(component_descriptor.clazz=I.clazz)";
30 32
31 private static final String INSERT_OR_UPDATE_ON_P_KEY_CONFLICT = getInsertString(P_KEY_CONFLICT_STATEMENT); 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,13 +39,29 @@ public class HsqlComponentDescriptorInsertRepository extends AbstractComponentDe
37 } 39 }
38 40
39 @Override 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 protected ComponentDescriptorEntity doProcessSaveOrUpdate(ComponentDescriptorEntity entity, String query) { 56 protected ComponentDescriptorEntity doProcessSaveOrUpdate(ComponentDescriptorEntity entity, String query) {
41 getQuery(entity, query).executeUpdate(); 57 getQuery(entity, query).executeUpdate();
42 return entityManager.find(ComponentDescriptorEntity.class, entity.getUuid()); 58 return entityManager.find(ComponentDescriptorEntity.class, entity.getUuid());
43 } 59 }
44 60
45 private static String getInsertString(String conflictStatement) { 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,7 +5,7 @@
5 * you may not use this file except in compliance with 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 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 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
@@ -17,6 +17,8 @@ package org.thingsboard.server.dao.sql.query; @@ -17,6 +17,8 @@ package org.thingsboard.server.dao.sql.query;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.apache.commons.lang3.StringUtils; 19 import org.apache.commons.lang3.StringUtils;
  20 +import org.springframework.beans.factory.annotation.Autowired;
  21 +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
20 import org.springframework.stereotype.Repository; 22 import org.springframework.stereotype.Repository;
21 import org.thingsboard.server.common.data.EntityType; 23 import org.thingsboard.server.common.data.EntityType;
22 import org.thingsboard.server.common.data.id.CustomerId; 24 import org.thingsboard.server.common.data.id.CustomerId;
@@ -44,8 +46,6 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection; @@ -44,8 +46,6 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection;
44 import org.thingsboard.server.common.data.relation.EntityTypeFilter; 46 import org.thingsboard.server.common.data.relation.EntityTypeFilter;
45 import org.thingsboard.server.dao.util.SqlDao; 47 import org.thingsboard.server.dao.util.SqlDao;
46 48
47 -import javax.persistence.EntityManager;  
48 -import javax.persistence.PersistenceContext;  
49 import java.math.BigInteger; 49 import java.math.BigInteger;
50 import java.util.Collections; 50 import java.util.Collections;
51 import java.util.HashMap; 51 import java.util.HashMap;
@@ -75,7 +75,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -75,7 +75,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
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 (" + 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 " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" + 76 " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" +
77 " FROM relation" + 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 " UNION ALL" + 79 " UNION ALL" +
80 " SELECT r.from_id, r.from_type, r.to_id, r.to_type, r.relation_type, lvl + 1" + 80 " SELECT r.from_id, r.from_type, r.to_id, r.to_type, r.relation_type, lvl + 1" +
81 " FROM relation r" + 81 " FROM relation r" +
@@ -88,21 +88,23 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -88,21 +88,23 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
88 public static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from"); 88 public static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from");
89 public static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to"); 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 @Override 94 @Override
95 public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { 95 public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) {
96 EntityType entityType = resolveEntityType(query.getEntityFilter()); 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 @Override 105 @Override
105 public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { 106 public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) {
  107 + EntityQueryContext ctx = new EntityQueryContext();
106 EntityType entityType = resolveEntityType(query.getEntityFilter()); 108 EntityType entityType = resolveEntityType(query.getEntityFilter());
107 EntityDataPageLink pageLink = query.getPageLink(); 109 EntityDataPageLink pageLink = query.getPageLink();
108 110
@@ -126,9 +128,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -126,9 +128,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
126 .collect(Collectors.toList()); 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 String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping); 134 String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping);
133 String entityTypeStr; 135 String entityTypeStr;
134 if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) { 136 if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) {
@@ -137,9 +139,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -137,9 +139,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
137 entityTypeStr = "'" + entityType.name() + "'"; 139 entityTypeStr = "'" + entityType.name() + "'";
138 } 140 }
139 if (!StringUtils.isEmpty(entityFieldsSelection)) { 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 } else { 143 } else {
142 - entityFieldsSelection = String.format("e.id, %s", entityTypeStr); 144 + entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr);
143 } 145 }
144 String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping); 146 String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping);
145 String topSelection = "entities.*"; 147 String topSelection = "entities.*";
@@ -150,13 +152,13 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -150,13 +152,13 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
150 String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result", 152 String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result",
151 topSelection, 153 topSelection,
152 entityFieldsSelection, 154 entityFieldsSelection,
153 - getEntityTableQuery(query.getEntityFilter(), entityType), 155 + addEntityTableQuery(ctx, query.getEntityFilter(), entityType),
154 entityWhereClause, 156 entityWhereClause,
155 latestJoins, 157 latestJoins,
156 whereClause); 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 String dataQuery = String.format("select * %s", fromClause); 163 String dataQuery = String.format("select * %s", fromClause);
162 164
@@ -177,17 +179,18 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -177,17 +179,18 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
177 if (pageLink.getPageSize() > 0) { 179 if (pageLink.getPageSize() > 0) {
178 dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); 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 return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements); 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 CustomerId customerId, 188 CustomerId customerId,
186 EntityFilter entityFilter, 189 EntityFilter entityFilter,
187 List<EntityKeyMapping> entityFieldsFilters, 190 List<EntityKeyMapping> entityFieldsFilters,
188 EntityType entityType) { 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 String result = permissionQuery; 194 String result = permissionQuery;
192 if (!entityFilterQuery.isEmpty()) { 195 if (!entityFilterQuery.isEmpty()) {
193 result += " and " + entityFilterQuery; 196 result += " and " + entityFilterQuery;
@@ -198,35 +201,42 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -198,35 +201,42 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
198 return result; 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 switch (entityFilter.getType()) { 205 switch (entityFilter.getType()) {
203 case RELATIONS_QUERY: 206 case RELATIONS_QUERY:
204 case DEVICE_SEARCH_QUERY: 207 case DEVICE_SEARCH_QUERY:
205 case ASSET_SEARCH_QUERY: 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 default: 212 default:
208 if (entityType == EntityType.TENANT) { 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 } else if (entityType == EntityType.CUSTOMER) { 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 } else { 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 switch (entityFilter.getType()) { 229 switch (entityFilter.getType()) {
220 case SINGLE_ENTITY: 230 case SINGLE_ENTITY:
221 - return this.singleEntityQuery((SingleEntityFilter) entityFilter); 231 + return this.singleEntityQuery(ctx, (SingleEntityFilter) entityFilter);
222 case ENTITY_LIST: 232 case ENTITY_LIST:
223 - return this.entityListQuery((EntityListFilter) entityFilter); 233 + return this.entityListQuery(ctx, (EntityListFilter) entityFilter);
224 case ENTITY_NAME: 234 case ENTITY_NAME:
225 - return this.entityNameQuery((EntityNameFilter) entityFilter); 235 + return this.entityNameQuery(ctx, (EntityNameFilter) entityFilter);
226 case ASSET_TYPE: 236 case ASSET_TYPE:
227 case DEVICE_TYPE: 237 case DEVICE_TYPE:
228 case ENTITY_VIEW_TYPE: 238 case ENTITY_VIEW_TYPE:
229 - return this.typeQuery(entityFilter); 239 + return this.typeQuery(ctx, entityFilter);
230 case RELATIONS_QUERY: 240 case RELATIONS_QUERY:
231 case DEVICE_SEARCH_QUERY: 241 case DEVICE_SEARCH_QUERY:
232 case ASSET_SEARCH_QUERY: 242 case ASSET_SEARCH_QUERY:
@@ -236,53 +246,60 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -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 switch (entityFilter.getType()) { 250 switch (entityFilter.getType()) {
241 case RELATIONS_QUERY: 251 case RELATIONS_QUERY:
242 - return relationQuery((RelationsQueryFilter) entityFilter); 252 + return relationQuery(ctx, (RelationsQueryFilter) entityFilter);
243 case DEVICE_SEARCH_QUERY: 253 case DEVICE_SEARCH_QUERY:
244 DeviceSearchQueryFilter deviceQuery = (DeviceSearchQueryFilter) entityFilter; 254 DeviceSearchQueryFilter deviceQuery = (DeviceSearchQueryFilter) entityFilter;
245 - return entitySearchQuery(deviceQuery, EntityType.DEVICE, deviceQuery.getDeviceTypes()); 255 + return entitySearchQuery(ctx, deviceQuery, EntityType.DEVICE, deviceQuery.getDeviceTypes());
246 case ASSET_SEARCH_QUERY: 256 case ASSET_SEARCH_QUERY:
247 AssetSearchQueryFilter assetQuery = (AssetSearchQueryFilter) entityFilter; 257 AssetSearchQueryFilter assetQuery = (AssetSearchQueryFilter) entityFilter;
248 - return entitySearchQuery(assetQuery, EntityType.ASSET, assetQuery.getAssetTypes()); 258 + return entitySearchQuery(ctx, assetQuery, EntityType.ASSET, assetQuery.getAssetTypes());
249 default: 259 default:
250 return entityTableMap.get(entityType); 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 EntityId rootId = entityFilter.getRootEntity(); 265 EntityId rootId = entityFilter.getRootEntity();
256 //TODO: fetch last level only. 266 //TODO: fetch last level only.
257 //TODO: fetch distinct records. 267 //TODO: fetch distinct records.
258 String lvlFilter = getLvlFilter(entityFilter.getMaxLevel()); 268 String lvlFilter = getLvlFilter(entityFilter.getMaxLevel());
259 String selectFields = "SELECT tenant_id, customer_id, id, type, name, label FROM " + entityType.name() + " WHERE id in ( SELECT entity_id"; 269 String selectFields = "SELECT tenant_id, customer_id, id, type, name, label FROM " + entityType.name() + " WHERE id in ( SELECT entity_id";
260 String from = getQueryTemplate(entityFilter.getDirection()); 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 String query = "( " + selectFields + from + ")"; 274 String query = "( " + selectFields + from + ")";
266 if (types != null && !types.isEmpty()) { 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 query += " )"; 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 return query; 284 return query;
271 } 285 }
272 286
273 - private String relationQuery(RelationsQueryFilter entityFilter) { 287 + private String relationQuery(EntityQueryContext ctx, RelationsQueryFilter entityFilter) {
274 EntityId rootId = entityFilter.getRootEntity(); 288 EntityId rootId = entityFilter.getRootEntity();
275 String lvlFilter = getLvlFilter(entityFilter.getMaxLevel()); 289 String lvlFilter = getLvlFilter(entityFilter.getMaxLevel());
276 String selectFields = getSelectTenantId() + ", " + getSelectCustomerId() + ", " + 290 String selectFields = getSelectTenantId() + ", " + getSelectCustomerId() + ", " +
277 " entity.entity_id as id," + getSelectType() + ", " + getSelectName() + ", " + 291 " entity.entity_id as id," + getSelectType() + ", " + getSelectName() + ", " +
278 getSelectLabel() + ", entity.entity_type as entity_type"; 292 getSelectLabel() + ", entity.entity_type as entity_type";
279 String from = getQueryTemplate(entityFilter.getDirection()); 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 StringBuilder whereFilter; 297 StringBuilder whereFilter;
282 if (entityFilter.getFilters() != null && !entityFilter.getFilters().isEmpty()) { 298 if (entityFilter.getFilters() != null && !entityFilter.getFilters().isEmpty()) {
283 whereFilter = new StringBuilder(" WHERE "); 299 whereFilter = new StringBuilder(" WHERE ");
284 boolean first = true; 300 boolean first = true;
285 boolean single = entityFilter.getFilters().size() == 1; 301 boolean single = entityFilter.getFilters().size() == 1;
  302 + int entityTypeFilterIdx = 0;
286 for (EntityTypeFilter etf : entityFilter.getFilters()) { 303 for (EntityTypeFilter etf : entityFilter.getFilters()) {
287 if (first) { 304 if (first) {
288 first = false; 305 first = false;
@@ -290,21 +307,23 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -290,21 +307,23 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
290 whereFilter.append(" AND "); 307 whereFilter.append(" AND ");
291 } 308 }
292 String relationType = etf.getRelationType(); 309 String relationType = etf.getRelationType();
293 - String entityTypes = etf.getEntityTypes().stream().map(type -> "'" + type + "'").collect(Collectors.joining(", "));  
294 if (!single) { 310 if (!single) {
295 whereFilter.append(" ("); 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 .append(entityFilter.getDirection().equals(EntitySearchDirection.FROM) ? "to" : "from") 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 if (!single) { 316 if (!single) {
301 whereFilter.append(" )"); 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 } else { 323 } else {
305 whereFilter = new StringBuilder(); 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 return "( " + selectFields + from + ")"; 327 return "( " + selectFields + from + ")";
309 } 328 }
310 329
@@ -410,10 +429,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -410,10 +429,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
410 " END as label"; 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 String query; 435 String query;
418 if (!StringUtils.isEmpty(latestFilters) && !StringUtils.isEmpty(textSearchQuery)) { 436 if (!StringUtils.isEmpty(latestFilters) && !StringUtils.isEmpty(textSearchQuery)) {
419 query = String.join(" AND ", latestFilters, textSearchQuery); 437 query = String.join(" AND ", latestFilters, textSearchQuery);
@@ -429,32 +447,38 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -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 if (!StringUtils.isEmpty(searchText) && !selectionMapping.isEmpty()) { 451 if (!StringUtils.isEmpty(searchText) && !selectionMapping.isEmpty()) {
434 String lowerSearchText = searchText.toLowerCase() + "%"; 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 return String.format("(%s)", String.join(" or ", searchPredicates)); 459 return String.format("(%s)", String.join(" or ", searchPredicates));
438 } else { 460 } else {
439 return null; 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 String type; 482 String type;
459 String name; 483 String name;
460 switch (filter.getType()) { 484 switch (filter.getType()) {
@@ -473,7 +497,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -473,7 +497,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
473 default: 497 default:
474 throw new RuntimeException("Not supported!"); 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 private EntityType resolveEntityType(EntityFilter entityFilter) { 505 private EntityType resolveEntityType(EntityFilter entityFilter) {
@@ -37,31 +37,30 @@ public class EntityDataAdapter { @@ -37,31 +37,30 @@ public class EntityDataAdapter {
37 37
38 public static PageData<EntityData> createEntityData(EntityDataPageLink pageLink, 38 public static PageData<EntityData> createEntityData(EntityDataPageLink pageLink,
39 List<EntityKeyMapping> selectionMapping, 39 List<EntityKeyMapping> selectionMapping,
40 - List<Object[]> rows, 40 + List<Map<String, Object>> rows,
41 int totalElements) { 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 int startIndex = pageLink.getPageSize() * pageLink.getPage(); 43 int startIndex = pageLink.getPageSize() * pageLink.getPage();
44 boolean hasNext = pageLink.getPageSize() > 0 && totalElements > startIndex + rows.size(); 44 boolean hasNext = pageLink.getPageSize() > 0 && totalElements > startIndex + rows.size();
45 List<EntityData> entitiesData = convertListToEntityData(rows, selectionMapping); 45 List<EntityData> entitiesData = convertListToEntityData(rows, selectionMapping);
46 return new PageData<>(entitiesData, totalPages, totalElements, hasNext); 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 return result.stream().map(row -> toEntityData(row, selectionMapping)).collect(Collectors.toList()); 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 EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id); 56 EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id);
58 Map<EntityKeyType, Map<String, TsValue>> latest = new HashMap<>(); 57 Map<EntityKeyType, Map<String, TsValue>> latest = new HashMap<>();
59 Map<String, TsValue[]> timeseries = new HashMap<>(); 58 Map<String, TsValue[]> timeseries = new HashMap<>();
60 EntityData entityData = new EntityData(entityId, latest, timeseries); 59 EntityData entityData = new EntityData(entityId, latest, timeseries);
61 - for (EntityKeyMapping mapping: selectionMapping) { 60 + for (EntityKeyMapping mapping : selectionMapping) {
62 if (!mapping.isIgnore()) { 61 if (!mapping.isIgnore()) {
63 EntityKey entityKey = mapping.getEntityKey(); 62 EntityKey entityKey = mapping.getEntityKey();
64 - Object value = row[mapping.getIndex()]; 63 + Object value = row.get(mapping.getValueAlias());
65 String strValue; 64 String strValue;
66 long ts; 65 long ts;
67 if (entityKey.getType().equals(EntityKeyType.ENTITY_FIELD)) { 66 if (entityKey.getType().equals(EntityKeyType.ENTITY_FIELD)) {
@@ -69,7 +68,7 @@ public class EntityDataAdapter { @@ -69,7 +68,7 @@ public class EntityDataAdapter {
69 ts = System.currentTimeMillis(); 68 ts = System.currentTimeMillis();
70 } else { 69 } else {
71 strValue = convertValue(value); 70 strValue = convertValue(value);
72 - Object tsObject = row[mapping.getIndex() + 1]; 71 + Object tsObject = row.get(mapping.getTsAlias());
73 ts = tsObject != null ? Long.parseLong(tsObject.toString()) : 0; 72 ts = tsObject != null ? Long.parseLong(tsObject.toString()) : 0;
74 } 73 }
75 TsValue tsValue = new TsValue(ts, strValue); 74 TsValue tsValue = new TsValue(ts, strValue);
@@ -72,6 +72,7 @@ public class EntityKeyMapping { @@ -72,6 +72,7 @@ public class EntityKeyMapping {
72 private boolean ignore = false; 72 private boolean ignore = false;
73 private List<KeyFilter> keyFilters; 73 private List<KeyFilter> keyFilters;
74 private EntityKey entityKey; 74 private EntityKey entityKey;
  75 + private int paramIdx = 0;
75 76
76 public boolean hasFilter() { 77 public boolean hasFilter() {
77 return keyFilters != null && !keyFilters.isEmpty(); 78 return keyFilters != null && !keyFilters.isEmpty();
@@ -100,17 +101,17 @@ public class EntityKeyMapping { @@ -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 if (hasFilter()) { 105 if (hasFilter()) {
105 String keyAlias = entityKey.getType().equals(EntityKeyType.ENTITY_FIELD) ? "e" : alias; 106 String keyAlias = entityKey.getType().equals(EntityKeyType.ENTITY_FIELD) ? "e" : alias;
106 return keyFilters.stream().map(keyFilter -> 107 return keyFilters.stream().map(keyFilter ->
107 - this.buildKeyQuery(keyAlias, keyFilter)); 108 + this.buildKeyQuery(ctx, keyAlias, keyFilter));
108 } else { 109 } else {
109 return null; 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 String entityTypeStr; 115 String entityTypeStr;
115 if (entityFilter.getType().equals(EntityFilterType.RELATIONS_QUERY)) { 116 if (entityFilter.getType().equals(EntityFilterType.RELATIONS_QUERY)) {
116 entityTypeStr = "entities.entity_type"; 117 entityTypeStr = "entities.entity_type";
@@ -118,12 +119,13 @@ public class EntityKeyMapping { @@ -118,12 +119,13 @@ public class EntityKeyMapping {
118 entityTypeStr = "'" + entityType.name() + "'"; 119 entityTypeStr = "'" + entityType.name() + "'";
119 } 120 }
120 String join = hasFilter() ? "left join" : "left outer join"; 121 String join = hasFilter() ? "left join" : "left outer join";
  122 + ctx.addStringParameter(alias + "_key_id", entityKey.getKey());
121 if (entityKey.getType().equals(EntityKeyType.TIME_SERIES)) { 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 } else { 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 if (!entityKey.getType().equals(EntityKeyType.ATTRIBUTE)) { 129 if (!entityKey.getType().equals(EntityKeyType.ATTRIBUTE)) {
128 String scope; 130 String scope;
129 if (entityKey.getType().equals(EntityKeyType.CLIENT_ATTRIBUTE)) { 131 if (entityKey.getType().equals(EntityKeyType.CLIENT_ATTRIBUTE)) {
@@ -144,13 +146,13 @@ public class EntityKeyMapping { @@ -144,13 +146,13 @@ public class EntityKeyMapping {
144 Collectors.joining(", ")); 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 Collectors.joining(" ")); 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 Collectors.joining(" AND ")); 156 Collectors.joining(" AND "));
155 } 157 }
156 158
@@ -262,33 +264,33 @@ public class EntityKeyMapping { @@ -262,33 +264,33 @@ public class EntityKeyMapping {
262 return String.join(", ", attrValSelection, attrTsSelection); 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 if (predicate.getType().equals(FilterPredicateType.COMPLEX)) { 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 } else { 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 return predicate.getPredicates().stream() 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 " " + predicate.getOperation().name() + " " 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 if (predicate.getType().equals(FilterPredicateType.NUMERIC)) { 287 if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
286 if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) { 288 if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
287 String column = entityFieldColumnMap.get(key.getKey()); 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 } else { 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 return String.format("(%s or %s)", longQuery, doubleQuery); 294 return String.format("(%s or %s)", longQuery, doubleQuery);
293 } 295 }
294 } else { 296 } else {
@@ -300,15 +302,16 @@ public class EntityKeyMapping { @@ -300,15 +302,16 @@ public class EntityKeyMapping {
300 } 302 }
301 String field = alias + "." + column; 303 String field = alias + "." + column;
302 if (predicate.getType().equals(FilterPredicateType.STRING)) { 304 if (predicate.getType().equals(FilterPredicateType.STRING)) {
303 - return this.buildStringPredicateQuery(field, (StringFilterPredicate) predicate); 305 + return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate);
304 } else { 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 String operationField = field; 313 String operationField = field;
  314 + String paramName = getNextParameterName(field);
312 String value = stringFilterPredicate.getValue(); 315 String value = stringFilterPredicate.getValue();
313 String stringOperationQuery = ""; 316 String stringOperationQuery = "";
314 if (stringFilterPredicate.isIgnoreCase()) { 317 if (stringFilterPredicate.isIgnoreCase()) {
@@ -317,65 +320,77 @@ public class EntityKeyMapping { @@ -317,65 +320,77 @@ public class EntityKeyMapping {
317 } 320 }
318 switch (stringFilterPredicate.getOperation()) { 321 switch (stringFilterPredicate.getOperation()) {
319 case EQUAL: 322 case EQUAL:
320 - stringOperationQuery = String.format("%s = '%s'", operationField, value); 323 + stringOperationQuery = String.format("%s = :%s", operationField, paramName);
321 break; 324 break;
322 case NOT_EQUAL: 325 case NOT_EQUAL:
323 - stringOperationQuery = String.format("%s != '%s'", operationField, value); 326 + stringOperationQuery = String.format("%s != :%s", operationField, paramName);
324 break; 327 break;
325 case STARTS_WITH: 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 break; 331 break;
328 case ENDS_WITH: 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 break; 335 break;
331 case CONTAINS: 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 break; 339 break;
334 case NOT_CONTAINS: 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 break; 343 break;
337 } 344 }
  345 + ctx.addStringParameter(paramName, value);
338 return String.format("(%s is not null and %s)", field, stringOperationQuery); 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 String numericOperationQuery = ""; 352 String numericOperationQuery = "";
344 switch (numericFilterPredicate.getOperation()) { 353 switch (numericFilterPredicate.getOperation()) {
345 case EQUAL: 354 case EQUAL:
346 - numericOperationQuery = String.format("%s = %s", field, value); 355 + numericOperationQuery = String.format("%s = :%s", field, paramName);
347 break; 356 break;
348 case NOT_EQUAL: 357 case NOT_EQUAL:
349 - numericOperationQuery = String.format("%s != '%s'", field, value); 358 + numericOperationQuery = String.format("%s != :%s", field, paramName);
350 break; 359 break;
351 case GREATER: 360 case GREATER:
352 - numericOperationQuery = String.format("%s > %s", field, value); 361 + numericOperationQuery = String.format("%s > :%s", field, paramName);
353 break; 362 break;
354 case GREATER_OR_EQUAL: 363 case GREATER_OR_EQUAL:
355 - numericOperationQuery = String.format("%s >= %s", field, value); 364 + numericOperationQuery = String.format("%s >= :%s", field, paramName);
356 break; 365 break;
357 case LESS: 366 case LESS:
358 - numericOperationQuery = String.format("%s < %s", field, value); 367 + numericOperationQuery = String.format("%s < :%s", field, paramName);
359 break; 368 break;
360 case LESS_OR_EQUAL: 369 case LESS_OR_EQUAL:
361 - numericOperationQuery = String.format("%s <= %s", field, value); 370 + numericOperationQuery = String.format("%s <= :%s", field, paramName);
362 break; 371 break;
363 } 372 }
364 return String.format("(%s is not null and %s)", field, numericOperationQuery); 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 BooleanFilterPredicate booleanFilterPredicate) { 377 BooleanFilterPredicate booleanFilterPredicate) {
369 - boolean value = booleanFilterPredicate.isValue(); 378 + String paramName = getNextParameterName(field);
  379 + ctx.addBooleanParameter(paramName, booleanFilterPredicate.isValue());
370 String booleanOperationQuery = ""; 380 String booleanOperationQuery = "";
371 switch (booleanFilterPredicate.getOperation()) { 381 switch (booleanFilterPredicate.getOperation()) {
372 case EQUAL: 382 case EQUAL:
373 - booleanOperationQuery = String.format("%s = %s", field, value); 383 + booleanOperationQuery = String.format("%s = :%s", field, paramName);
374 break; 384 break;
375 case NOT_EQUAL: 385 case NOT_EQUAL:
376 - booleanOperationQuery = String.format("%s != %s", field, value); 386 + booleanOperationQuery = String.format("%s != :%s", field, paramName);
377 break; 387 break;
378 } 388 }
379 return String.format("(%s is not null and %s)", field, booleanOperationQuery); 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,32 +24,32 @@ import java.util.Arrays;
24 24
25 @RunWith(ClasspathSuite.class) 25 @RunWith(ClasspathSuite.class)
26 @ClassnameFilters({ 26 @ClassnameFilters({
27 - "org.thingsboard.server.dao.service.sql.EntityServiceSqlTest" 27 + "org.thingsboard.server.dao.service.sql.*SqlTest"
28 }) 28 })
29 public class SqlDaoServiceTestSuite { 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 @ClassRule 31 @ClassRule
42 public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 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 "sql-test.properties" 38 "sql-test.properties"
49 ); 39 );
50 40
51 // @ClassRule 41 // @ClassRule
52 // public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 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 // Arrays.asList("sql/schema-timescale.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"), 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 // "sql/timescale/drop-all-tables.sql", 54 // "sql/timescale/drop-all-tables.sql",
55 // "sql-test.properties" 55 // "sql-test.properties"
@@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture;
20 import org.junit.After; 20 import org.junit.After;
21 import org.junit.Assert; 21 import org.junit.Assert;
22 import org.junit.Before; 22 import org.junit.Before;
  23 +import org.junit.Ignore;
23 import org.junit.Test; 24 import org.junit.Test;
24 import org.springframework.beans.factory.annotation.Autowired; 25 import org.springframework.beans.factory.annotation.Autowired;
25 import org.thingsboard.server.common.data.DataConstants; 26 import org.thingsboard.server.common.data.DataConstants;
@@ -3,18 +3,18 @@ database.ts.type=sql @@ -3,18 +3,18 @@ database.ts.type=sql
3 sql.ts_inserts_executor_type=fixed 3 sql.ts_inserts_executor_type=fixed
4 sql.ts_inserts_fixed_thread_pool_size=200 4 sql.ts_inserts_fixed_thread_pool_size=200
5 sql.ts_key_value_partitioning=MONTHS 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 service.type=monolith 19 service.type=monolith
20 20
@@ -26,16 +26,16 @@ service.type=monolith @@ -26,16 +26,16 @@ service.type=monolith
26 #sql.ts_inserts_fixed_thread_pool_size=200 26 #sql.ts_inserts_fixed_thread_pool_size=200
27 #sql.ts_key_value_partitioning=MONTHS 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 queue.core.pack-processing-timeout=3000 40 queue.core.pack-processing-timeout=3000
41 queue.rule-engine.pack-processing-timeout=3000 41 queue.rule-engine.pack-processing-timeout=3000