GenericTableHandler.java revision a32c32954ffe171a0437ff1bc1287e9086f0bd26
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff * Copyright © 2011-2014 ForgeRock AS. All rights reserved.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * The contents of this file are subject to the terms
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * of the Common Development and Distribution License
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * (the License). You may not use this file except in
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * compliance with the License.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * You can obtain a copy of the License at
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * See the License for the specific language governing
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * permission and limitations under the License.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * When distributing Covered Code, include this CDDL
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Header Notice in each file and include the License file
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * If applicable, add the following below the CDDL Header,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * with the fields enclosed by brackets [] replaced by
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * your own identifying information:
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * "Portions Copyrighted [year] [name of copyright owner]"
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienleimport static org.forgerock.openidm.repo.QueryConstants.PAGED_RESULTS_OFFSET;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienleimport static org.forgerock.openidm.repo.QueryConstants.PAGE_SIZE;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienleimport static org.forgerock.openidm.repo.QueryConstants.SORT_KEYS;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.json.resource.InternalServerErrorException;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.json.resource.NotFoundException;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.json.resource.PreconditionFailedException;
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmillerimport org.forgerock.json.resource.QueryFilterVisitor;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.json.resource.ResourceException;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.openidm.repo.jdbc.ErrorType;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.openidm.repo.jdbc.SQLExceptionHandler;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.openidm.repo.jdbc.TableHandler;
37c999796383567df3db1f9d2a09942d83d413caChad Kienleimport org.forgerock.openidm.repo.jdbc.impl.query.QueryResultMapper;
37c999796383567df3db1f9d2a09942d83d413caChad Kienleimport org.forgerock.openidm.repo.jdbc.impl.query.TableQueries;
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmillerimport org.forgerock.openidm.repo.util.SQLQueryFilterVisitor;
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmillerimport org.forgerock.openidm.util.ResourceUtil;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Handling of tables in a generic (not object specific) layout
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @author aegloff
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienlepublic class GenericTableHandler implements TableHandler {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final static Logger logger = LoggerFactory.getLogger(GenericTableHandler.class);
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmiller private static final QueryFilterVisitor<String, Map<String, Object>> QUERY_FILTER_VISITOR =
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmiller new SQLQueryFilterVisitor<Map<String, Object>>() {
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmiller // key/value number for each key/value placeholder
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmiller public String visitValueAssertion(Map<String, Object> objects, String operand, JsonPointer field, Object valueAssertion) {
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller if (ResourceUtil.RESOURCE_FIELD_CONTENT_ID_POINTER.equals(field)) {
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller return "(obj.objectid " + operand + " ${" + value + "})";
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller return "(EXISTS "
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller + "(SELECT * FROM ${_dbSchema}.${_propTable} prop "
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller + "WHERE prop.${_mainTable}_id = obj.id "
7351ef6cfcb358c8090ba10b003ad6d6c7da9a1eBrendan Mmiller + "AND prop.propvalue " + operand + " ${" + value + "} "
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmiller public String visitPresentFilter(Map<String, Object> objects, JsonPointer field) {
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller if (ResourceUtil.RESOURCE_FIELD_CONTENT_ID_POINTER.equals(field)) {
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller // NOT NULL is enforced by the schema
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller return "(obj.objectid IS NOT NULL)";
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller return "(EXISTS "
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller + "(SELECT * FROM ${_dbSchema}.${_propTable} prop "
d3b4413fd3942e33d4d0ef0488669889b60d61e6Brendan Mmiller + "WHERE prop.${_mainTable}_id = obj.id "
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Jackson parser
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Type information for the Jackson parser
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final TypeReference<LinkedHashMap<String,Object>> typeRef = new TypeReference<LinkedHashMap<String,Object>>() {};
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle final QueryFilterVisitor<String, Map<String, Object>> queryFilterVisitor;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final boolean enableBatching; // Whether to use JDBC statement batching.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int maxBatchSize; // The maximum number of statements to batch together. If max batch size is 1, do not use batching.
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener public boolean queryIdExists(String queryId) {
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * Create a generic table handler using a QueryFilterVisitor that uses generic object property tables to process
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * query filters.
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param tableConfig the table config
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param dbSchemaName the schem name
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param queriesConfig a map of named queries
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param commandsConfig a map of named commands
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param maxBatchSize the maximum batch size
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param sqlExceptionHandler a handler for SQLExceptions
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller public GenericTableHandler(JsonValue tableConfig, String dbSchemaName, JsonValue queriesConfig, JsonValue commandsConfig, int maxBatchSize, SQLExceptionHandler sqlExceptionHandler) {
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller this(tableConfig, dbSchemaName, queriesConfig, commandsConfig, maxBatchSize, QUERY_FILTER_VISITOR, sqlExceptionHandler);
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * Create a generic table handler.
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param tableConfig the table config
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param dbSchemaName the schem name
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param queriesConfig a map of named queries
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param commandsConfig a map of named commands
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param maxBatchSize the maximum batch size
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param queryFilterVisitor the {@link QueryFilterVisitor} for converting a query filter to an SQL statement
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param sqlExceptionHandler a handler for SQLExceptions
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller public GenericTableHandler(JsonValue tableConfig, String dbSchemaName, JsonValue queriesConfig, JsonValue commandsConfig, int maxBatchSize, QueryFilterVisitor<String, Map<String, Object>> queryFilterVisitor, SQLExceptionHandler sqlExceptionHandler) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle this.sqlExceptionHandler = new DefaultSQLExceptionHandler();
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle queries = new TableQueries(this, mainTableName, propTableName, dbSchemaName, queryFilterVisitor, new GenericQueryResultMapper());
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle queryMap = Collections.unmodifiableMap(initializeQueryMap());
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmiller queries.setConfiguredQueries(queriesConfig, commandsConfig, queryMap);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // TODO: Consider taking into account DB meta-data rather than just configuration
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle //DatabaseMetaData metadata = connection.getMetaData();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle //boolean isBatchingSupported = metadata.supportsBatchUpdates();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle //if (!isBatchingSupported) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // maxBatchSize = 1;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.info("JDBC statement batching enabled, maximum batch size {}", this.maxBatchSize);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.info("JDBC statement batching disabled.");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle protected Map<QueryDefinition, String> initializeQueryMap() {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Map<QueryDefinition, String> result = new EnumMap<QueryDefinition, String>(QueryDefinition.class);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String typeTable = dbSchemaName == null ? "objecttypes" : dbSchemaName + ".objecttypes";
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String mainTable = dbSchemaName == null ? mainTableName : dbSchemaName + "." + mainTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String propertyTable = dbSchemaName == null ? propTableName : dbSchemaName + "." + propTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // objecttypes table
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.CREATETYPEQUERYSTR, "INSERT INTO " + typeTable + " (objecttype) VALUES (?)");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.READTYPEQUERYSTR, "SELECT id FROM " + typeTable + " objtype WHERE objtype.objecttype = ?");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Main object table
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.READFORUPDATEQUERYSTR, "SELECT obj.* FROM " + mainTable + " obj INNER JOIN " + typeTable + " objtype ON obj.objecttypes_id = objtype.id AND objtype.objecttype = ? WHERE obj.objectid = ? FOR UPDATE");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.READQUERYSTR, "SELECT obj.rev, obj.fullobject FROM " + typeTable + " objtype, " + mainTable + " obj WHERE obj.objecttypes_id = objtype.id AND objtype.objecttype = ? AND obj.objectid = ?");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.CREATEQUERYSTR, "INSERT INTO " + mainTable + " (objecttypes_id, objectid, rev, fullobject) VALUES (?,?,?,?)");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.UPDATEQUERYSTR, "UPDATE " + mainTable + " obj SET obj.objectid = ?, obj.rev = ?, obj.fullobject = ? WHERE obj.id = ?");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.DELETEQUERYSTR, "DELETE obj FROM " + mainTable + " obj INNER JOIN " + typeTable + " objtype ON obj.objecttypes_id = objtype.id AND objtype.objecttype = ? WHERE obj.objectid = ? AND obj.rev = ?");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /* DB2 Script
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deleteQueryStr = "DELETE FROM " + dbSchemaName + "." + mainTableName + " obj WHERE EXISTS (SELECT 1 FROM " + dbSchemaName + ".objecttypes objtype WHERE obj.objecttypes_id = objtype.id AND objtype.objecttype = ?) AND obj.objectid = ? AND obj.rev = ?";
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Object properties table
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.PROPCREATEQUERYSTR, "INSERT INTO " + propertyTable + " ( " + mainTableName + "_id, propkey, proptype, propvalue) VALUES (?,?,?,?)");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.PROPDELETEQUERYSTR, "DELETE prop FROM " + propertyTable + " prop INNER JOIN " + mainTable + " obj ON prop." + mainTableName + "_id = obj.id INNER JOIN " + typeTable + " objtype ON obj.objecttypes_id = objtype.id WHERE objtype.objecttype = ? AND obj.objectid = ?");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Default object queries
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String tableVariable = dbSchemaName == null ? "${_mainTable}" : "${_dbSchema}.${_mainTable}";
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.QUERYALLIDS, "SELECT obj.objectid FROM " + tableVariable + " obj INNER JOIN " + typeTable + " objtype ON obj.objecttypes_id = objtype.id WHERE objtype.objecttype = ${_resource}");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /* (non-Javadoc)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @see org.forgerock.openidm.repo.jdbc.impl.TableHandler#read(java.lang.String, java.lang.String, java.lang.String, java.sql.Connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public Resource read(String fullId, String type, String localId, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws ResourceException, SQLException, IOException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readStatement = getPreparedStatement(connection, QueryDefinition.READQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating prepared statement {} for {}", readStatement, fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle resultMap = mapper.readValue(objString, typeRef);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug(" full id: {}, rev: {}, obj {}", new Object[]{fullId, rev, resultMap});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result = new Resource(localId, rev, new JsonValue(resultMap));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw ResourceException.getException(ResourceException.NOT_FOUND,
a1d206a2a22b5cde9b00633ea4472ae0b144d695Brendan Mmiller "Object " + fullId + " not found in " + type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /* (non-Javadoc)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @see org.forgerock.openidm.repo.jdbc.impl.TableHandler#create(java.lang.String, java.lang.String, java.lang.String, java.util.Map, java.sql.Connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public void create(String fullId, String type, String localId, Map<String, Object> obj, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws SQLException, IOException, InternalServerErrorException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long typeId = getTypeId(type, connection); // Note this call can commit and start a new transaction in some cases
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle createStatement = queries.getPreparedStatement(connection, queryMap.get(QueryDefinition.CREATEQUERYSTR), true);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle obj.put("_id", localId); // Save the id in the object
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle obj.put("_rev", rev); // Save the rev in the object, and return the changed rev from the create.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String objString = mapper.writeValueAsString(obj);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating statement {} with params {}, {}, {}, {}",
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle new Object[]{createStatement, typeId, localId, rev, objString});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ResultSet keys = createStatement.getGeneratedKeys();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new InternalServerErrorException("Object creation for " + fullId + " failed to retrieve an assigned ID from the DB.");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Created object for id {} with rev {}", fullId, rev);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle writeValueProperties(fullId, dbId, localId, jv, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Writes all properties of a given resource to the properties table and links them to the main table record.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param fullId the full URI of the resource the belongs to
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param dbId the generated identifier to link the properties table with the main table (foreign key)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param localId the local identifier of the resource these properties belong to
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param value the JSON value with the properties to write
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param connection the DB connection
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws SQLException if the insert failed
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle void writeValueProperties(String fullId, long dbId, String localId, JsonValue value, Connection connection) throws SQLException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement propCreateStatement = getPreparedStatement(connection, QueryDefinition.PROPCREATEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle batchingCount = writeValueProperties(fullId, dbId, localId, value, connection, propCreateStatement, batchingCount);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int[] numUpdates = propCreateStatement.executeBatch();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Batch update of objectproperties updated: {}", numUpdates);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Writing batch of objectproperties, updated: {}", Arrays.asList(numUpdates));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Internal recursive function to add/write properties.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * If batching is enabled, prepared statements are added to the batch and only executed if they hit the max limit.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * After completion returns the number of properties that have only been added to the batch but not yet executed.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * The caller is responsible for executing the batch on remaining items when it deems the batch complete.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * If batching is not enabled, prepared statements are immediately executed.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param fullId the full URI of the resource the belongs to
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param dbId the generated identifier to link the properties table with the main table (foreign key)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param localId the local identifier of the resource these properties belong to
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param value the JSON value with the properties to write
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param connection the DB connection
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param propCreateStatement the prepared properties insert statement
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param batchingCount the current number of statements that have been batched and not yet executed on the prepared statement
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @return status of the current batchingCount, i.e. how many statements are not yet executed in the PreparedStatement
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws SQLException if the insert failed
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle private int writeValueProperties(String fullId, long dbId, String localId, JsonValue value, Connection connection,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement propCreateStatement, int batchingCount) throws SQLException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle batchingCount = writeValueProperties(fullId, dbId, localId, entry, connection, propCreateStatement, batchingCount);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle propvalue = val.toString(); // TODO: proper type conversions?
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle proptype = entry.getObject().getClass().getName(); // TODO: proper type info
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating statement {} with params {}, {}, {}, {}, {}",
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle new Object[]{propCreateStatement, dbId, localId, propkey, proptype, propvalue});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Executing: {}", propCreateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int numUpdate = propCreateStatement.executeUpdate();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Inserting objectproperty id: {} propkey: {} proptype: {}, propvalue: {}", new Object[]{fullId, propkey, proptype, propvalue});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (enableBatching && batchingCount >= maxBatchSize) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int[] numUpdates = propCreateStatement.executeBatch();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Batch limit reached, update of objectproperties updated: {}", Arrays.asList(numUpdates));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @inheritDoc
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public boolean isErrorType(SQLException ex, ErrorType errorType) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return sqlExceptionHandler.isErrorType(ex, errorType);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * InheritDoc
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public boolean isRetryable(SQLException ex, Connection connection) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return sqlExceptionHandler.isRetryable(ex, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Ensure type is in objecttypes table and get its assigned id
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Callers should note that this may commit a transaction and start a new one if a new type gets added
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long getTypeId(String type, Connection connection) throws SQLException, InternalServerErrorException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle connection.setAutoCommit(true); // Commit the new type right away, and have no transaction isolation for read
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Rather than relying on DB specific ignore if exists functionality handle it here
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Could extend this in the future to more explicitly check for duplicate key error codes, but these again can be DB specific
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new InternalServerErrorException("Failed to populate and look up objecttypes table, no id could be retrieved for " + type, detectedEx);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle connection.setAutoCommit(false); // Start another transaction
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param type the object type URI
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param connection the DB connection
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @return the typeId for the given type if exists, or -1 if does not exist
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws java.sql.SQLException
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long readTypeId(String type, Connection connection) throws SQLException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readTypeStatement = getPreparedStatement(connection, QueryDefinition.READTYPEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating prepared statement {} for {}", readTypeStatement, type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Executing: {}", readTypeStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Type: {}, id: {}", new Object[]{type, typeId});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param type the object type URI
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param connection the DB connection
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @return true if a type was inserted
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws SQLException if the insert failed (e.g. concurrent insert by another thread)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean createTypeId(String type, Connection connection) throws SQLException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement createTypeStatement = getPreparedStatement(connection, QueryDefinition.CREATETYPEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Executing: {}", createTypeStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Reads an object with for update locking applied
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Note: statement associated with the returned resultset
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * is not closed upon return.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Aside from taking care to close the resultset it also is
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * the responsibility of the caller to close the associated
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * statement. Although the specification specifies that drivers/pools
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * should close the statement automatically, not all do this reliably.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param fullId qualified id of component type and id
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param type the component type
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param localId the id of the object within the component type
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param connection the connection to use
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @return the row for the requested object, selected FOR UPDATE
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws NotFoundException if the requested object was not found in the DB
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws java.sql.SQLException for general DB issues
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public ResultSet readForUpdate(String fullId, String type, String localId, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement readForUpdateStatement = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readForUpdateStatement = getPreparedStatement(connection, QueryDefinition.READFORUPDATEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating prepared statement {} for {}", readForUpdateStatement, fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Executing: {}", readForUpdateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Read for update full id: {}", fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(readForUpdateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new NotFoundException("Object " + fullId + " not found in " + type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(readForUpdateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /* (non-Javadoc)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @see org.forgerock.openidm.repo.jdbc.impl.TableHandler#update(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.util.Map, java.sql.Connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public void update(String fullId, String type, String localId, String rev, Map<String, Object> obj, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws SQLException, IOException, PreconditionFailedException, NotFoundException, InternalServerErrorException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle obj.put("_rev", newRev); // Save the rev in the object, and return the changed rev from the create.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle rs = readForUpdate(fullId, type, localId, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long objectTypeDbId = rs.getLong("objecttypes_id");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Update existing object {} rev: {} db id: {}, object type db id: {}", new Object[]{fullId, existingRev, dbId, objectTypeDbId});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new PreconditionFailedException("Update rejected as current Object revision " + existingRev + " is different than expected by caller (" + rev + "), the object has changed since retrieval.");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle updateStatement = getPreparedStatement(connection, QueryDefinition.UPDATEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deletePropStatement = getPreparedStatement(connection, QueryDefinition.PROPDELETEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Support changing object identifier
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (newLocalId != null && !localId.equals(newLocalId)) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Object identifier is changing from " + localId + " to " + newLocalId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle newLocalId = localId; // If it hasn't changed, use the existing ID
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle obj.put("_id", newLocalId); // Ensure the ID is saved in the object
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String objString = mapper.writeValueAsString(obj);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating prepared statement {} for {} {} {} {} {}", new Object[]{updateStatement, fullId, newLocalId, newRev, objString, dbId});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Update statement: {}", updateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int updateCount = updateStatement.executeUpdate();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Updated rows: {} for {}", updateCount, fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new InternalServerErrorException("Update execution did not result in updating 1 row as expected. Updated rows: " + updateCount);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // TODO: only update what changed?
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating prepared statement {} for {} {} {}", new Object[]{deletePropStatement, fullId, type, localId});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Update properties del statement: {}", deletePropStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int deleteCount = deletePropStatement.executeUpdate();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Deleted child rows: {} for: {}", deleteCount, fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle writeValueProperties(fullId, dbId, localId, jv, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Ensure associated statement also is closed
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @see org.forgerock.openidm.repo.jdbc.internal.GenericTableHandler#delete(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.sql.Connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public void delete(String fullId, String type, String localId, String rev, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws PreconditionFailedException, InternalServerErrorException, NotFoundException, SQLException, IOException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // First check if the revision matches and select it for UPDATE
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle existing = readForUpdate(fullId, type, localId, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new NotFoundException("Object does not exist for delete on: " + fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (!"*".equals(rev) && !rev.equals(existingRev)) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new PreconditionFailedException("Delete rejected as current Object revision " + existingRev + " is different than "
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle + "expected by caller " + rev + ", the object has changed since retrieval.");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Proceed with the valid delete
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deleteStatement = getPreparedStatement(connection, QueryDefinition.DELETEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating prepared statement {} for {} {} {} {}", new Object[]{deleteStatement, fullId, type, localId, rev});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Rely on ON DELETE CASCADE for connected object properties to be deleted
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Delete statement: {}", deleteStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int deletedRows = deleteStatement.executeUpdate();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Deleted {} rows for id : {} {}", deletedRows, localId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new InternalServerErrorException("Deleting object for " + fullId + " failed, DB reported " + deletedRows + " rows deleted");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("delete for id succeeded: {} revision: {}", localId, rev);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Ensure associated statement also is closed
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Statement existingStatement = existing.getStatement();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /* (non-Javadoc)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @see org.forgerock.openidm.repo.jdbc.impl.TableHandler#delete(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.sql.Connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public List<Map<String, Object>> query(String type, Map<String, Object> params, Connection connection)
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller public Integer command(String type, Map<String, Object> params, Connection connection) throws SQLException, ResourceException {
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller return queries.command(type, params, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return "Generic handler mapped to [" + mainTableName + ", " + propTableName + "]";
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle protected PreparedStatement getPreparedStatement(Connection connection, QueryDefinition queryDefinition) throws SQLException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return queries.getPreparedStatement(connection, queryMap.get(queryDefinition));
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle public String buildRawQuery(QueryFilter filter, Map<String, Object> replacementTokens, Map<String, Object> params) {
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle final String offsetParam = (String) params.get(PAGED_RESULTS_OFFSET);
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle final String pageSizeParam = (String) params.get(PAGE_SIZE);
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle String filterString = getFilterString(filter, replacementTokens);
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle // Check for sort keys and build up order-by syntax
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle final List<SortKey> sortKeys = (List<SortKey>)params.get(SORT_KEYS);
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle List<String> innerJoins = new ArrayList<String>();
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle prepareSortKeyStatements(sortKeys, innerJoins, keys, replacementTokens);
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle filterString = StringUtils.join(innerJoins, " ") + " " + filterString + " ORDER BY " + StringUtils.join(keys, ", ");
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle return "SELECT obj.* FROM ${_dbSchema}.${_mainTable} obj "
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle + filterString + " LIMIT " + pageSizeParam + " OFFSET " + offsetParam;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle * Loops through sort keys constructing the inner join and key statements.
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle * @param sortKeys a {@link List} of sort keys
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle * @param innerJoins a {@link List} to store INNER JOIN statements
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle * @param keys a {@link List} to store ORDER BY keys
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle * @param replacementTokens a {@link Map} containing replacement tokens for the {@link PreparedStatement}
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle protected void prepareSortKeyStatements(List<SortKey> sortKeys, List<String> innerJoins, List<String> keys, Map<String, Object> replacementTokens) {
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle final String innerJoin = "INNER JOIN ${_dbSchema}.${_propTable} " + tableAlias + " ON " + tableAlias
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle + ".${_mainTable}_id = obj.id AND " + tableAlias + ".propkey = ${" + tokenName + "}";
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle keys.add(tableAlias + ".propvalue " + (sortKey.isAscendingOrder() ? "ASC" : " DESC"));
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle replacementTokens.put(tokenName, sortKey.getField().toString());
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle * Returns a query string representing the supplied filter.
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle * @param filter the {@link QueryFilter} object
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle * @param replacementTokens replacement tokens for the query string
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle * @return a query string
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle protected String getFilterString(QueryFilter filter, Map<String, Object> replacementTokens) {
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle return " WHERE " + filter.accept(queryFilterVisitor, replacementTokens);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleclass GenericQueryResultMapper implements QueryResultMapper {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final static Logger logger = LoggerFactory.getLogger(GenericQueryResultMapper.class);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Jackson parser
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Type information for the Jackson parser
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle TypeReference<LinkedHashMap<String,Object>> typeRef = new TypeReference<LinkedHashMap<String,Object>>() {};
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public List<Map<String, Object>> mapQueryToObject(ResultSet rs, String queryId, String type, Map<String, Object> params, TableQueries tableQueries)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ResultSetMetaData rsMetaData = rs.getMetaData();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean hasFullObject = tableQueries.hasColumn(rsMetaData, "fullobject");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean hasId = false;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean hasRev = false;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean hasPropKey = false;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean hasPropValue = false;
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener boolean hasTotal = false;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle hasId = tableQueries.hasColumn(rsMetaData, "objectid");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle hasRev = tableQueries.hasColumn(rsMetaData, "rev");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle hasPropKey = tableQueries.hasColumn(rsMetaData, "propkey");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle hasPropValue = tableQueries.hasColumn(rsMetaData, "propvalue");
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener hasTotal = tableQueries.hasColumn(rsMetaData, "total");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Map<String, Object> obj = mapper.readValue(objString, typeRef);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // TODO: remove data logging
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Query result for queryId: {} type: {} converted obj: {}", new Object[] {queryId, type, obj});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Map<String, Object> obj = new HashMap<String, Object>();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Results from query on individual searchable property
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public boolean searchableDefault;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public boolean isSearchable(JsonPointer propPointer) {
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff // More specific configuration takes precedence
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff while (!propPointer.isEmpty() && explicit == null) {
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff explicit = properties.explicitlySearchable.get(propPointer);
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff * @return Approximation on whether this may have searchable properties
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff * It is only an approximation as we do not have an exhaustive list of possible properties
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff * to consider against a default setting of searchable.
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff public boolean hasPossibleSearchableProperties() {
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff return ((searchableDefault) ? true : properties.explicitSearchableProperties);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public static GenericTableConfig parse(JsonValue tableConfig) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle GenericTableConfig cfg = new GenericTableConfig();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle cfg.mainTableName = tableConfig.get("mainTable").required().asString();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle cfg.propertiesTableName = tableConfig.get("propertiesTable").required().asString();
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmiller cfg.searchableDefault = tableConfig.get("searchableDefault").defaultTo(Boolean.TRUE).asBoolean();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle cfg.properties = GenericPropertiesConfig.parse(tableConfig.get("properties"));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public Map<JsonPointer, Boolean> explicitlySearchable = new HashMap<JsonPointer, Boolean>();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public boolean searchableDefault;
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff // Whether there are any properties explicitly set to searchable true
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public static GenericPropertiesConfig parse(JsonValue propsConfig) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle GenericPropertiesConfig cfg = new GenericPropertiesConfig();
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff boolean propSearchable = detail.get("searchable").asBoolean();