Repository: cayenne Updated Branches: refs/heads/master 777b1d650 -> 77bd5b7f3
cleanup before refactoring .. mostly generics-related Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/77bd5b7f Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/77bd5b7f Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/77bd5b7f Branch: refs/heads/master Commit: 77bd5b7f321a6bacfbc75f00940902cdb02e212b Parents: 777b1d6 Author: aadamchik <[email protected]> Authored: Sun Nov 2 12:55:15 2014 +0300 Committer: aadamchik <[email protected]> Committed: Sun Nov 2 12:55:29 2014 +0300 ---------------------------------------------------------------------- .../access/jdbc/SQLTemplateProcessor.java | 52 +- .../org/apache/cayenne/query/SQLSelect.java | 2 +- .../org/apache/cayenne/query/SQLTemplate.java | 1217 +++++++++--------- 3 files changed, 628 insertions(+), 643 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/77bd5b7f/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java index 82b6dca..366e9a9 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java @@ -31,7 +31,7 @@ import org.apache.velocity.VelocityContext; import org.apache.velocity.context.InternalContextAdapterImpl; import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.RuntimeInstance; -import org.apache.velocity.runtime.log.NullLogSystem; +import org.apache.velocity.runtime.log.NullLogChute; import org.apache.velocity.runtime.parser.ParseException; import org.apache.velocity.runtime.parser.node.SimpleNode; @@ -65,7 +65,7 @@ class SQLTemplateProcessor { // set null logger sharedRuntime.addProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, - new NullLogSystem()); + new NullLogChute()); sharedRuntime.addProperty( RuntimeConstants.RESOURCE_MANAGER_CLASS, @@ -107,33 +107,29 @@ class SQLTemplateProcessor { * parameters in the map, {@link SQLTemplateRenderingUtils} as a "helper" variable and * SQLStatement object as "statement" variable. */ - SQLStatement processTemplate(String template, Map parameters) throws Exception { - // have to make a copy of parameter map since we are gonna modify it.. - Map internalParameters = (parameters != null && !parameters.isEmpty()) - ? new HashMap(parameters) - : new HashMap(3); - - List<ParameterBinding> bindings = new ArrayList<ParameterBinding>(); - List<ColumnDescriptor> results = new ArrayList<ColumnDescriptor>(); - internalParameters.put(BINDINGS_LIST_KEY, bindings); - internalParameters.put(RESULT_COLUMNS_LIST_KEY, results); - internalParameters.put(HELPER_KEY, renderingUtils); - - String sql = buildStatement( - new VelocityContext(internalParameters), - template, - parameters); - - ParameterBinding[] bindingsArray = new ParameterBinding[bindings.size()]; - bindings.toArray(bindingsArray); - - ColumnDescriptor[] resultsArray = new ColumnDescriptor[results.size()]; - results.toArray(resultsArray); - - return new SQLStatement(sql, resultsArray, bindingsArray); - } + SQLStatement processTemplate(String template, Map<String, ?> parameters) throws Exception { + // have to make a copy of parameter map since we are gonna modify it.. + Map<String, Object> internalParameters = (parameters != null && !parameters.isEmpty()) ? new HashMap<String, Object>( + parameters) : new HashMap<String, Object>(5); + + List<ParameterBinding> bindings = new ArrayList<ParameterBinding>(); + List<ColumnDescriptor> results = new ArrayList<ColumnDescriptor>(); + internalParameters.put(BINDINGS_LIST_KEY, bindings); + internalParameters.put(RESULT_COLUMNS_LIST_KEY, results); + internalParameters.put(HELPER_KEY, renderingUtils); + + String sql = buildStatement(new VelocityContext(internalParameters), template, parameters); + + ParameterBinding[] bindingsArray = new ParameterBinding[bindings.size()]; + bindings.toArray(bindingsArray); + + ColumnDescriptor[] resultsArray = new ColumnDescriptor[results.size()]; + results.toArray(resultsArray); + + return new SQLStatement(sql, resultsArray, bindingsArray); + } - String buildStatement(VelocityContext context, String template, Map parameters) + String buildStatement(VelocityContext context, String template, Map<String, ?> parameters) throws Exception { // Note: this method is a reworked version of // org.apache.velocity.app.Velocity.evaluate(..) http://git-wip-us.apache.org/repos/asf/cayenne/blob/77bd5b7f/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java index 3f75291..de1c43c 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java @@ -164,7 +164,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { /** * Returns mutable map of parameters that will be bound to SQL. A caller is * free to add/remove parameters from the returned map as needed. - * Alternatively one should use chained {@link #params(String, Object)} + * Alternatively one may use chained {@link #params(String, Object)} */ public Map<String, Object> getParameters() { return parameters; http://git-wip-us.apache.org/repos/asf/cayenne/blob/77bd5b7f/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplate.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplate.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplate.java index ee8e10e..8a4b787 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplate.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplate.java @@ -72,618 +72,607 @@ import org.apache.commons.collections.Transformer; * @since 1.1 */ public class SQLTemplate extends AbstractQuery implements ParameterizedQuery, - XMLSerializable { - - static final String COLUMN_NAME_CAPITALIZATION_PROPERTY = "cayenne.SQLTemplate.columnNameCapitalization"; - - private static final Transformer nullMapTransformer = new Transformer() { - - public Object transform(Object input) { - return (input != null) ? input : Collections.EMPTY_MAP; - } - }; - - protected String defaultTemplate; - protected Map<String, String> templates; - protected Map<String, ?>[] parameters; - protected CapsStrategy columnNamesCapitalization; - protected SQLResult result; - private String dataNodeName; - - SQLTemplateMetadata metaData = new SQLTemplateMetadata(); - - /** - * Creates an empty SQLTemplate. Note this constructor does not specify the "root" of - * the query, so a user must call "setRoot" later to make sure SQLTemplate can be - * executed. - * - * @since 1.2 - */ - public SQLTemplate() { - } - - /** - * Creates a SQLTemplate without an explicit root. - * - * @since 4.0 - */ - public SQLTemplate(String defaultTemplate, boolean isFetchingDataRows) { - setDefaultTemplate(defaultTemplate); - setRoot(null); - setFetchingDataRows(isFetchingDataRows); - } - - @Override - public void setRoot(Object value) { - // allow null root... - if (value == null) { - this.root = value; - } else { - super.setRoot(value); - } - } - - @Override - public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) { - DataMap map = getMetaData(resolver).getDataMap(); - - QueryEngine engine; - if (map != null) { - engine = router.engineForDataMap(map); - } else { - engine = router.engineForName(getDataNodeName()); - } - - router.route(engine, this, substitutedQuery); - } - - /** - * @since 3.1 - */ - public SQLTemplate(DataMap rootMap, String defaultTemplate, boolean isFetchingDataRows) { - setDefaultTemplate(defaultTemplate); - setRoot(rootMap); - setFetchingDataRows(isFetchingDataRows); - } - - /** - * @since 1.2 - */ - public SQLTemplate(ObjEntity rootEntity, String defaultTemplate) { - setDefaultTemplate(defaultTemplate); - setRoot(rootEntity); - } - - /** - * @since 1.2 - */ - public SQLTemplate(Class<?> rootClass, String defaultTemplate) { - setDefaultTemplate(defaultTemplate); - setRoot(rootClass); - } - - /** - * @since 1.2 - */ - public SQLTemplate(DbEntity rootEntity, String defaultTemplate) { - setDefaultTemplate(defaultTemplate); - setRoot(rootEntity); - } - - /** - * @since 1.2 - */ - public SQLTemplate(String objEntityName, String defaultTemplate) { - setRoot(objEntityName); - setDefaultTemplate(defaultTemplate); - } - - /** - * @since 1.2 - */ - @Override - public QueryMetadata getMetaData(EntityResolver resolver) { - metaData.resolve(root, resolver, this); - return metaData; - } - - /** - * Calls <em>sqlAction(this)</em> on the visitor. - * - * @since 1.2 - */ - @Override - public SQLAction createSQLAction(SQLActionVisitor visitor) { - return visitor.sqlAction(this); - } - - /** - * Prints itself as XML to the provided PrintWriter. - * - * @since 1.1 - */ - public void encodeAsXML(XMLEncoder encoder) { - encoder.print("<query name=\""); - encoder.print(getName()); - encoder.print("\" factory=\""); - encoder.print("org.apache.cayenne.map.SQLTemplateBuilder"); - - String rootString = null; - String rootType = null; - - if (root instanceof String) { - rootType = MapLoader.OBJ_ENTITY_ROOT; - rootString = root.toString(); - } - else if (root instanceof ObjEntity) { - rootType = MapLoader.OBJ_ENTITY_ROOT; - rootString = ((ObjEntity) root).getName(); - } - else if (root instanceof DbEntity) { - rootType = MapLoader.DB_ENTITY_ROOT; - rootString = ((DbEntity) root).getName(); - } - else if (root instanceof Procedure) { - rootType = MapLoader.PROCEDURE_ROOT; - rootString = ((Procedure) root).getName(); - } - else if (root instanceof Class<?>) { - rootType = MapLoader.JAVA_CLASS_ROOT; - rootString = ((Class<?>) root).getName(); - } - else if (root instanceof DataMap) { - rootType = MapLoader.DATA_MAP_ROOT; - rootString = ((DataMap) root).getName(); - } - - if (rootType != null) { - encoder.print("\" root=\""); - encoder.print(rootType); - encoder.print("\" root-name=\""); - encoder.print(rootString); - } - - encoder.println("\">"); - - encoder.indent(1); - - metaData.encodeAsXML(encoder); - - if (getColumnNamesCapitalization() != CapsStrategy.DEFAULT) { - encoder.printProperty( - COLUMN_NAME_CAPITALIZATION_PROPERTY, - getColumnNamesCapitalization().name()); - } - - // encode default SQL - if (defaultTemplate != null) { - encoder.print("<sql><![CDATA["); - encoder.print(defaultTemplate); - encoder.println("]]></sql>"); - } - - // encode adapter SQL - if (templates != null && !templates.isEmpty()) { - - //sorting entries by adapter name - TreeSet<String> keys = new TreeSet<String>(templates.keySet()); - for (String key : keys) { - String value = templates.get(key); - - if (key != null && value != null) { - String sql = value.trim(); - if (sql.length() > 0) { - encoder.print("<sql adapter-class=\""); - encoder.print(key); - encoder.print("\"><![CDATA["); - encoder.print(sql); - encoder.println("]]></sql>"); - } - } - } - } - - // TODO: support parameter encoding - - encoder.indent(-1); - encoder.println("</query>"); - } - - /** - * Initializes query parameters using a set of properties. - * - * @since 1.1 - */ - public void initWithProperties(Map<String, ?> properties) { - // must init defaults even if properties are empty - metaData.initWithProperties(properties); - - if (properties == null) { - properties = Collections.EMPTY_MAP; - } - - Object columnNamesCapitalization = properties - .get(COLUMN_NAME_CAPITALIZATION_PROPERTY); - this.columnNamesCapitalization = (columnNamesCapitalization != null) - ? CapsStrategy - .valueOf(columnNamesCapitalization.toString().toUpperCase()) - : null; - } - - /** - * Returns an iterator over parameter sets. Each element returned from the iterator is - * a java.util.Map. - */ - public Iterator<?> parametersIterator() { - return (parameters == null || parameters.length == 0) ? IteratorUtils - .emptyIterator() : IteratorUtils.transformedIterator(IteratorUtils - .arrayIterator(parameters), nullMapTransformer); - } - - /** - * Returns the number of parameter sets. - */ - public int parametersSize() { - return (parameters != null) ? parameters.length : 0; - } - - /** - * Returns a new query built using this query as a prototype and a new set of - * parameters. - */ - public SQLTemplate queryWithParameters(Map<String, ?>... parameters) { - // create a query replica - SQLTemplate query = new SQLTemplate(); - - query.setRoot(root); - query.setDefaultTemplate(getDefaultTemplate()); - - if (templates != null) { - query.templates = new HashMap<String, String>(templates); - } - - query.metaData.copyFromInfo(this.metaData); - query.setParameters(parameters); - query.setColumnNamesCapitalization(this.getColumnNamesCapitalization()); - - return query; - } - - /** - * Creates and returns a new SQLTemplate built using this query as a prototype and - * substituting template parameters with the values from the map. - * - * @since 1.1 - */ - public Query createQuery(Map<String, ?> parameters) { - return queryWithParameters(parameters); - } - - /** - * @since 3.0 - */ - public QueryCacheStrategy getCacheStrategy() { - return metaData.getCacheStrategy(); - } - - /** - * @since 3.0 - */ - public void setCacheStrategy(QueryCacheStrategy strategy) { - metaData.setCacheStrategy(strategy); - } - - /** - * @since 3.0 - */ - public String[] getCacheGroups() { - return metaData.getCacheGroups(); - } - - /** - * @since 3.0 - */ - public void setCacheGroups(String... cacheGroups) { - this.metaData.setCacheGroups(cacheGroups); - } - - /** - * Instructs Cayenne to look for query results in the "local" cache when - * running the query. This is a short-hand notation for: - * - * <pre> - * query.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE); - * query.setCacheGroups("group1", "group2"); - * </pre> - * - * @since 4.0 - */ - public void useLocalCache(String... cacheGroups) { - setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE); - setCacheGroups(cacheGroups); - } - - /** - * Instructs Cayenne to look for query results in the "shared" cache when - * running the query. This is a short-hand notation for: - * - * <pre> - * query.setCacheStrategy(QueryCacheStrategy.SHARED_CACHE); - * query.setCacheGroups("group1", "group2"); - * </pre> - * - * @since 4.0 - */ - public void useSharedCache(String... cacheGroups) { - setCacheStrategy(QueryCacheStrategy.SHARED_CACHE); - setCacheGroups(cacheGroups); - } - - public int getFetchLimit() { - return metaData.getFetchLimit(); - } - - public void setFetchLimit(int fetchLimit) { - this.metaData.setFetchLimit(fetchLimit); - } - - /** - * @since 3.0 - */ - public int getFetchOffset() { - return metaData.getFetchOffset(); - } - - /** - * @since 3.0 - */ - public void setFetchOffset(int fetchOffset) { - metaData.setFetchOffset(fetchOffset); - } - - public int getPageSize() { - return metaData.getPageSize(); - } - - public void setPageSize(int pageSize) { - metaData.setPageSize(pageSize); - } - - public void setFetchingDataRows(boolean flag) { - metaData.setFetchingDataRows(flag); - } - - public boolean isFetchingDataRows() { - return metaData.isFetchingDataRows(); - } - - /** - * Returns default SQL template for this query. - */ - public String getDefaultTemplate() { - return defaultTemplate; - } - - /** - * Sets default SQL template for this query. - */ - public void setDefaultTemplate(String string) { - defaultTemplate = string; - } - - /** - * Returns a template for key, or a default template if a template for key is not - * found. - */ - public synchronized String getTemplate(String key) { - if (templates == null) { - return defaultTemplate; - } - - String template = templates.get(key); - return (template != null) ? template : defaultTemplate; - } - - /** - * Returns template for key, or null if there is no template configured for this key. - * Unlike {@link #getTemplate(String)}this method does not return a default template - * as a failover strategy, rather it returns null. - */ - public synchronized String getCustomTemplate(String key) { - return (templates != null) ? templates.get(key) : null; - } - - /** - * Adds a SQL template string for a given key. Note the the keys understood by Cayenne - * must be fully qualified adapter class names. This way the framework can related - * current DataNode to the right template. E.g. - * "org.apache.cayenne.dba.oracle.OracleAdapter" is a key that should be used to setup - * an Oracle-specific template. - * - * @see #setDefaultTemplate(String) - */ - public synchronized void setTemplate(String key, String template) { - if (templates == null) { - templates = new HashMap<String, String>(); - } - - templates.put(key, template); - } - - public synchronized void removeTemplate(String key) { - if (templates != null) { - templates.remove(key); - } - } - - /** - * Returns a collection of configured template keys. - */ - public synchronized Collection<String> getTemplateKeys() { - return (templates != null) ? Collections.unmodifiableCollection(templates - .keySet()) : Collections.EMPTY_LIST; - } - - /** - * Utility method to get the first set of parameters, since most queries will only - * have one. - */ - public Map<String, ?> getParameters() { - Map<String, ?> map = (parameters != null && parameters.length > 0) - ? parameters[0] - : null; - return (map != null) ? map : Collections.EMPTY_MAP; - } - - /** - * Utility method to initialize query with one or more sets of parameters. - */ - public void setParameters(Map<String, ?>... parameters) { - - if (parameters == null) { - this.parameters = null; - } - else { - // clone parameters to ensure that we don't have immutable maps that are not - // serializable with Hessian... - this.parameters = new Map[parameters.length]; - for (int i = 0; i < parameters.length; i++) { - this.parameters[i] = parameters[i] != null ? new HashMap<String, Object>( - parameters[i]) : new HashMap<String, Object>(); - } - } - } - - /** - * @since 1.2 - */ - public PrefetchTreeNode getPrefetchTree() { - return metaData.getPrefetchTree(); - } - - /** - * Adds a prefetch. - * - * @since 1.2 - */ - public PrefetchTreeNode addPrefetch(String prefetchPath) { - // by default use JOINT_PREFETCH_SEMANTICS - return metaData.addPrefetch( - prefetchPath, - PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); - } - - /** - * @since 1.2 - */ - public void removePrefetch(String prefetch) { - metaData.removePrefetch(prefetch); - } - - /** - * Adds all prefetches from a provided collection. - * - * @since 1.2 - */ - public void addPrefetches(Collection<String> prefetches) { - metaData.addPrefetches(prefetches, PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); - } - - /** - * Clears all prefetches. - * - * @since 1.2 - */ - public void clearPrefetches() { - metaData.clearPrefetches(); - } - - /** - * Returns a column name capitalization policy applied to selecting queries. This is - * used to simplify mapping of the queries like "SELECT * FROM ...", ensuring that a - * chosen Cayenne column mapping strategy (e.g. all column names in uppercase) is - * portable across database engines that can have varying default capitalization. - * Default (null) value indicates that column names provided in result set are used - * unchanged. - * - * @since 3.0 - */ - public CapsStrategy getColumnNamesCapitalization() { - return columnNamesCapitalization != null - ? columnNamesCapitalization - : CapsStrategy.DEFAULT; - } - - /** - * Sets a column name capitalization policy applied to selecting queries. This is used - * to simplify mapping of the queries like "SELECT * FROM ...", ensuring that a chosen - * Cayenne column mapping strategy (e.g. all column names in uppercase) is portable - * across database engines that can have varying default capitalization. Default - * (null) value indicates that column names provided in result set are used unchanged. - * <p> - * Note that while a non-default setting is useful for queries that do not rely on a - * #result directive to describe columns, it works for all SQLTemplates the same way. - * - * @since 3.0 - */ - public void setColumnNamesCapitalization(CapsStrategy columnNameCapitalization) { - this.columnNamesCapitalization = columnNameCapitalization; - } - - /** - * Sets an optional explicit mapping of the result set. If result set mapping is - * specified, the result of SQLTemplate may not be a normal list of Persistent objects - * or DataRows, instead it will follow the {@link SQLResult} rules. - * - * @since 3.0 - */ - public void setResult(SQLResult resultSet) { - this.result = resultSet; - } - - /** - * @since 3.0 - */ - public SQLResult getResult() { - return result; - } - - /** - * Sets statement's fetch size (0 for no default size) - * - * @since 3.0 - */ - public void setStatementFetchSize(int size) { - metaData.setStatementFetchSize(size); - } - - /** - * @return statement's fetch size - * @since 3.0 - */ - public int getStatementFetchSize() { - return metaData.getStatementFetchSize(); - } - - /** - * Returns a name of the DataNode to use with this SQLTemplate. This - * information will be used during query execution if no other routing - * information is provided such as entity name or class, etc. - * - * @since 4.0 - */ - public String getDataNodeName() { - return dataNodeName; - } - - /** - * Sets a name of the DataNode to use with this SQLTemplate. This - * information will be used during query execution if no other routing - * information is provided such as entity name or class, etc. - * - * @since 4.0 - */ - public void setDataNodeName(String dataNodeName) { - this.dataNodeName = dataNodeName; - } + XMLSerializable { + + private static final long serialVersionUID = -3073521388289663641L; + + static final String COLUMN_NAME_CAPITALIZATION_PROPERTY = "cayenne.SQLTemplate.columnNameCapitalization"; + + private static final Transformer nullMapTransformer = new Transformer() { + + public Object transform(Object input) { + return (input != null) ? input : Collections.EMPTY_MAP; + } + }; + + protected String defaultTemplate; + protected Map<String, String> templates; + protected Map<String, ?>[] parameters; + protected CapsStrategy columnNamesCapitalization; + protected SQLResult result; + private String dataNodeName; + + SQLTemplateMetadata metaData = new SQLTemplateMetadata(); + + /** + * Creates an empty SQLTemplate. Note this constructor does not specify the + * "root" of the query, so a user must call "setRoot" later to make sure + * SQLTemplate can be executed. + * + * @since 1.2 + */ + public SQLTemplate() { + } + + /** + * Creates a SQLTemplate without an explicit root. + * + * @since 4.0 + */ + public SQLTemplate(String defaultTemplate, boolean isFetchingDataRows) { + setDefaultTemplate(defaultTemplate); + setRoot(null); + setFetchingDataRows(isFetchingDataRows); + } + + @Override + public void setRoot(Object value) { + // allow null root... + if (value == null) { + this.root = value; + } else { + super.setRoot(value); + } + } + + @Override + public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) { + DataMap map = getMetaData(resolver).getDataMap(); + + QueryEngine engine; + if (map != null) { + engine = router.engineForDataMap(map); + } else { + engine = router.engineForName(getDataNodeName()); + } + + router.route(engine, this, substitutedQuery); + } + + /** + * @since 3.1 + */ + public SQLTemplate(DataMap rootMap, String defaultTemplate, boolean isFetchingDataRows) { + setDefaultTemplate(defaultTemplate); + setRoot(rootMap); + setFetchingDataRows(isFetchingDataRows); + } + + /** + * @since 1.2 + */ + public SQLTemplate(ObjEntity rootEntity, String defaultTemplate) { + setDefaultTemplate(defaultTemplate); + setRoot(rootEntity); + } + + /** + * @since 1.2 + */ + public SQLTemplate(Class<?> rootClass, String defaultTemplate) { + setDefaultTemplate(defaultTemplate); + setRoot(rootClass); + } + + /** + * @since 1.2 + */ + public SQLTemplate(DbEntity rootEntity, String defaultTemplate) { + setDefaultTemplate(defaultTemplate); + setRoot(rootEntity); + } + + /** + * @since 1.2 + */ + public SQLTemplate(String objEntityName, String defaultTemplate) { + setRoot(objEntityName); + setDefaultTemplate(defaultTemplate); + } + + /** + * @since 1.2 + */ + @Override + public QueryMetadata getMetaData(EntityResolver resolver) { + metaData.resolve(root, resolver, this); + return metaData; + } + + /** + * Calls <em>sqlAction(this)</em> on the visitor. + * + * @since 1.2 + */ + @Override + public SQLAction createSQLAction(SQLActionVisitor visitor) { + return visitor.sqlAction(this); + } + + /** + * Prints itself as XML to the provided PrintWriter. + * + * @since 1.1 + */ + @Override + public void encodeAsXML(XMLEncoder encoder) { + encoder.print("<query name=\""); + encoder.print(getName()); + encoder.print("\" factory=\""); + encoder.print("org.apache.cayenne.map.SQLTemplateBuilder"); + + String rootString = null; + String rootType = null; + + if (root instanceof String) { + rootType = MapLoader.OBJ_ENTITY_ROOT; + rootString = root.toString(); + } else if (root instanceof ObjEntity) { + rootType = MapLoader.OBJ_ENTITY_ROOT; + rootString = ((ObjEntity) root).getName(); + } else if (root instanceof DbEntity) { + rootType = MapLoader.DB_ENTITY_ROOT; + rootString = ((DbEntity) root).getName(); + } else if (root instanceof Procedure) { + rootType = MapLoader.PROCEDURE_ROOT; + rootString = ((Procedure) root).getName(); + } else if (root instanceof Class<?>) { + rootType = MapLoader.JAVA_CLASS_ROOT; + rootString = ((Class<?>) root).getName(); + } else if (root instanceof DataMap) { + rootType = MapLoader.DATA_MAP_ROOT; + rootString = ((DataMap) root).getName(); + } + + if (rootType != null) { + encoder.print("\" root=\""); + encoder.print(rootType); + encoder.print("\" root-name=\""); + encoder.print(rootString); + } + + encoder.println("\">"); + + encoder.indent(1); + + metaData.encodeAsXML(encoder); + + if (getColumnNamesCapitalization() != CapsStrategy.DEFAULT) { + encoder.printProperty(COLUMN_NAME_CAPITALIZATION_PROPERTY, getColumnNamesCapitalization().name()); + } + + // encode default SQL + if (defaultTemplate != null) { + encoder.print("<sql><![CDATA["); + encoder.print(defaultTemplate); + encoder.println("]]></sql>"); + } + + // encode adapter SQL + if (templates != null && !templates.isEmpty()) { + + // sorting entries by adapter name + TreeSet<String> keys = new TreeSet<String>(templates.keySet()); + for (String key : keys) { + String value = templates.get(key); + + if (key != null && value != null) { + String sql = value.trim(); + if (sql.length() > 0) { + encoder.print("<sql adapter-class=\""); + encoder.print(key); + encoder.print("\"><![CDATA["); + encoder.print(sql); + encoder.println("]]></sql>"); + } + } + } + } + + // TODO: support parameter encoding + + encoder.indent(-1); + encoder.println("</query>"); + } + + /** + * Initializes query parameters using a set of properties. + * + * @since 1.1 + */ + public void initWithProperties(Map<String, ?> properties) { + // must init defaults even if properties are empty + metaData.initWithProperties(properties); + + if (properties == null) { + properties = Collections.emptyMap(); + } + + Object columnNamesCapitalization = properties.get(COLUMN_NAME_CAPITALIZATION_PROPERTY); + this.columnNamesCapitalization = (columnNamesCapitalization != null) ? CapsStrategy + .valueOf(columnNamesCapitalization.toString().toUpperCase()) : null; + } + + /** + * Returns an iterator over parameter sets. Each element returned from the + * iterator is a java.util.Map. + */ + public Iterator<?> parametersIterator() { + return (parameters == null || parameters.length == 0) ? IteratorUtils.emptyIterator() : IteratorUtils + .transformedIterator(IteratorUtils.arrayIterator(parameters), nullMapTransformer); + } + + /** + * Returns the number of parameter sets. + */ + public int parametersSize() { + return (parameters != null) ? parameters.length : 0; + } + + /** + * Returns a new query built using this query as a prototype and a new set + * of parameters. + */ + public SQLTemplate queryWithParameters(Map<String, ?>... parameters) { + // create a query replica + SQLTemplate query = new SQLTemplate(); + + query.setRoot(root); + query.setDefaultTemplate(getDefaultTemplate()); + + if (templates != null) { + query.templates = new HashMap<String, String>(templates); + } + + query.metaData.copyFromInfo(this.metaData); + query.setParameters(parameters); + query.setColumnNamesCapitalization(this.getColumnNamesCapitalization()); + + return query; + } + + /** + * Creates and returns a new SQLTemplate built using this query as a + * prototype and substituting template parameters with the values from the + * map. + * + * @since 1.1 + */ + @Override + public Query createQuery(Map<String, ?> parameters) { + return queryWithParameters(parameters); + } + + /** + * @since 3.0 + */ + public QueryCacheStrategy getCacheStrategy() { + return metaData.getCacheStrategy(); + } + + /** + * @since 3.0 + */ + public void setCacheStrategy(QueryCacheStrategy strategy) { + metaData.setCacheStrategy(strategy); + } + + /** + * @since 3.0 + */ + public String[] getCacheGroups() { + return metaData.getCacheGroups(); + } + + /** + * @since 3.0 + */ + public void setCacheGroups(String... cacheGroups) { + this.metaData.setCacheGroups(cacheGroups); + } + + /** + * Instructs Cayenne to look for query results in the "local" cache when + * running the query. This is a short-hand notation for: + * + * <pre> + * query.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE); + * query.setCacheGroups("group1", "group2"); + * </pre> + * + * @since 4.0 + */ + public void useLocalCache(String... cacheGroups) { + setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE); + setCacheGroups(cacheGroups); + } + + /** + * Instructs Cayenne to look for query results in the "shared" cache when + * running the query. This is a short-hand notation for: + * + * <pre> + * query.setCacheStrategy(QueryCacheStrategy.SHARED_CACHE); + * query.setCacheGroups("group1", "group2"); + * </pre> + * + * @since 4.0 + */ + public void useSharedCache(String... cacheGroups) { + setCacheStrategy(QueryCacheStrategy.SHARED_CACHE); + setCacheGroups(cacheGroups); + } + + public int getFetchLimit() { + return metaData.getFetchLimit(); + } + + public void setFetchLimit(int fetchLimit) { + this.metaData.setFetchLimit(fetchLimit); + } + + /** + * @since 3.0 + */ + public int getFetchOffset() { + return metaData.getFetchOffset(); + } + + /** + * @since 3.0 + */ + public void setFetchOffset(int fetchOffset) { + metaData.setFetchOffset(fetchOffset); + } + + public int getPageSize() { + return metaData.getPageSize(); + } + + public void setPageSize(int pageSize) { + metaData.setPageSize(pageSize); + } + + public void setFetchingDataRows(boolean flag) { + metaData.setFetchingDataRows(flag); + } + + public boolean isFetchingDataRows() { + return metaData.isFetchingDataRows(); + } + + /** + * Returns default SQL template for this query. + */ + public String getDefaultTemplate() { + return defaultTemplate; + } + + /** + * Sets default SQL template for this query. + */ + public void setDefaultTemplate(String string) { + defaultTemplate = string; + } + + /** + * Returns a template for key, or a default template if a template for key + * is not found. + */ + public synchronized String getTemplate(String key) { + if (templates == null) { + return defaultTemplate; + } + + String template = templates.get(key); + return (template != null) ? template : defaultTemplate; + } + + /** + * Returns template for key, or null if there is no template configured for + * this key. Unlike {@link #getTemplate(String)}this method does not return + * a default template as a failover strategy, rather it returns null. + */ + public synchronized String getCustomTemplate(String key) { + return (templates != null) ? templates.get(key) : null; + } + + /** + * Adds a SQL template string for a given key. Note the the keys understood + * by Cayenne must be fully qualified adapter class names. This way the + * framework can related current DataNode to the right template. E.g. + * "org.apache.cayenne.dba.oracle.OracleAdapter" is a key that should be + * used to setup an Oracle-specific template. + * + * @see #setDefaultTemplate(String) + */ + public synchronized void setTemplate(String key, String template) { + if (templates == null) { + templates = new HashMap<String, String>(); + } + + templates.put(key, template); + } + + public synchronized void removeTemplate(String key) { + if (templates != null) { + templates.remove(key); + } + } + + /** + * Returns a collection of configured template keys. + */ + public synchronized Collection<String> getTemplateKeys() { + return (templates != null) ? templates.keySet() : Collections.<String>emptyList(); + } + + /** + * Utility method to get the first set of parameters, since most queries + * will only have one. + */ + public Map<String, ?> getParameters() { + Map<String, ?> map = (parameters != null && parameters.length > 0) ? parameters[0] : null; + return (map != null) ? map : Collections.<String, Object> emptyMap(); + } + + /** + * Utility method to initialize query with one or more sets of parameters. + */ + public void setParameters(Map<String, ?>... parameters) { + + if (parameters == null) { + this.parameters = null; + } else { + // clone parameters to ensure that we don't have immutable maps that + // are not serializable with Hessian... + this.parameters = new Map[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + this.parameters[i] = parameters[i] != null ? new HashMap<String, Object>(parameters[i]) + : new HashMap<String, Object>(); + } + } + } + + /** + * @since 1.2 + */ + public PrefetchTreeNode getPrefetchTree() { + return metaData.getPrefetchTree(); + } + + /** + * Adds a prefetch. + * + * @since 1.2 + */ + public PrefetchTreeNode addPrefetch(String prefetchPath) { + // by default use JOINT_PREFETCH_SEMANTICS + return metaData.addPrefetch(prefetchPath, PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); + } + + /** + * @since 1.2 + */ + public void removePrefetch(String prefetch) { + metaData.removePrefetch(prefetch); + } + + /** + * Adds all prefetches from a provided collection. + * + * @since 1.2 + */ + public void addPrefetches(Collection<String> prefetches) { + metaData.addPrefetches(prefetches, PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); + } + + /** + * Clears all prefetches. + * + * @since 1.2 + */ + public void clearPrefetches() { + metaData.clearPrefetches(); + } + + /** + * Returns a column name capitalization policy applied to selecting queries. + * This is used to simplify mapping of the queries like "SELECT * FROM ...", + * ensuring that a chosen Cayenne column mapping strategy (e.g. all column + * names in uppercase) is portable across database engines that can have + * varying default capitalization. Default (null) value indicates that + * column names provided in result set are used unchanged. + * + * @since 3.0 + */ + public CapsStrategy getColumnNamesCapitalization() { + return columnNamesCapitalization != null ? columnNamesCapitalization : CapsStrategy.DEFAULT; + } + + /** + * Sets a column name capitalization policy applied to selecting queries. + * This is used to simplify mapping of the queries like "SELECT * FROM ...", + * ensuring that a chosen Cayenne column mapping strategy (e.g. all column + * names in uppercase) is portable across database engines that can have + * varying default capitalization. Default (null) value indicates that + * column names provided in result set are used unchanged. + * <p> + * Note that while a non-default setting is useful for queries that do not + * rely on a #result directive to describe columns, it works for all + * SQLTemplates the same way. + * + * @since 3.0 + */ + public void setColumnNamesCapitalization(CapsStrategy columnNameCapitalization) { + this.columnNamesCapitalization = columnNameCapitalization; + } + + /** + * Sets an optional explicit mapping of the result set. If result set + * mapping is specified, the result of SQLTemplate may not be a normal list + * of Persistent objects or DataRows, instead it will follow the + * {@link SQLResult} rules. + * + * @since 3.0 + */ + public void setResult(SQLResult resultSet) { + this.result = resultSet; + } + + /** + * @since 3.0 + */ + public SQLResult getResult() { + return result; + } + + /** + * Sets statement's fetch size (0 for no default size) + * + * @since 3.0 + */ + public void setStatementFetchSize(int size) { + metaData.setStatementFetchSize(size); + } + + /** + * @return statement's fetch size + * @since 3.0 + */ + public int getStatementFetchSize() { + return metaData.getStatementFetchSize(); + } + + /** + * Returns a name of the DataNode to use with this SQLTemplate. This + * information will be used during query execution if no other routing + * information is provided such as entity name or class, etc. + * + * @since 4.0 + */ + public String getDataNodeName() { + return dataNodeName; + } + + /** + * Sets a name of the DataNode to use with this SQLTemplate. This + * information will be used during query execution if no other routing + * information is provided such as entity name or class, etc. + * + * @since 4.0 + */ + public void setDataNodeName(String dataNodeName) { + this.dataNodeName = dataNodeName; + } }
