You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2014/11/02 10:57:52 UTC
git commit: cleanup before refactoring .. mostly generics-related
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 <aa...@apache.org>
Authored: Sun Nov 2 12:55:15 2014 +0300
Committer: aadamchik <aa...@apache.org>
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;
+ }
}