This is an automated email from the ASF dual-hosted git repository.

abulatski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new 7d8eef1  CAY-2590 Add method to set query timeout to queries
     new 731794d  Merge PR #396
7d8eef1 is described below

commit 7d8eef1432505f27fc4a96620d820010ca29fcc9
Author: Arseni Bulatski <ancars...@gmail.com>
AuthorDate: Thu Jul 4 15:56:00 2019 +0300

    CAY-2590 Add method to set query timeout to queries
---
 RELEASE-NOTES.txt                                  |   1 +
 .../apache/cayenne/lifecycle/id/StringIdQuery.java |   5 +
 .../org/apache/cayenne/access/DataDomainQuery.java |   5 +
 .../cayenne/access/ObjectsFromDataRowsQuery.java   |   5 +
 .../apache/cayenne/access/jdbc/EJBQLAction.java    |   8 +-
 .../cayenne/access/jdbc/ProcedureAction.java       |   9 +-
 .../cayenne/access/jdbc/SQLTemplateAction.java     |  29 +++---
 .../apache/cayenne/access/jdbc/SelectAction.java   |  15 ++-
 .../apache/cayenne/query/BaseQueryMetadata.java    |  20 +++-
 .../org/apache/cayenne/query/ColumnSelect.java     |   9 ++
 .../apache/cayenne/query/DefaultQueryMetadata.java |   5 +
 .../java/org/apache/cayenne/query/EJBQLQuery.java  |  29 ++++--
 .../org/apache/cayenne/query/FluentSelect.java     |   7 ++
 .../org/apache/cayenne/query/MappedSelect.java     |  30 +++++-
 .../org/apache/cayenne/query/ObjectSelect.java     |  11 ++
 .../org/apache/cayenne/query/ProcedureQuery.java   |  28 +++--
 .../org/apache/cayenne/query/QueryMetadata.java    |   8 ++
 .../apache/cayenne/query/QueryMetadataProxy.java   |   5 +
 .../java/org/apache/cayenne/query/SQLExec.java     |  22 ++--
 .../java/org/apache/cayenne/query/SQLSelect.java   |  19 ++++
 .../java/org/apache/cayenne/query/SQLTemplate.java |  32 ++++--
 .../java/org/apache/cayenne/query/SelectQuery.java |  16 +--
 .../apache/cayenne/query/MockQueryMetadata.java    |   5 +
 .../org/apache/cayenne/query/ObjectSelectTest.java |  21 ++--
 .../org/apache/cayenne/query/QueryTimeoutIT.java   | 113 +++++++++++++++++++++
 25 files changed, 384 insertions(+), 73 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 3aa2fa8..3d648ac 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -37,6 +37,7 @@ CAY-2570 Use MySQL adapter for latest versions of MariaDB
 CAY-2579 Review and possibly relax usage of readonly flag of ObjRelationship
 CAY-2585 Rename scalarQuery and params methods in SQLSelect
 CAY-2589 - Allow optionally using a local query cache that is separate from 
the shared query cache.
+CAY-2590 Add method to set query timeout to queries
 
 Bug Fixes:
 
diff --git 
a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java
 
b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java
index 4516dbd..b163250 100644
--- 
a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java
+++ 
b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java
@@ -226,6 +226,11 @@ public class StringIdQuery implements Query {
             }
 
             @Override
+            public int getQueryTimeout() {
+                return QueryMetadata.QUERY_TIMEOUT_DEFAULT;
+            }
+
+            @Override
             public boolean isSuppressingDistinct() {
                 return false;
             }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQuery.java 
b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQuery.java
index 647c88d..353c925 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQuery.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQuery.java
@@ -146,6 +146,11 @@ class DataDomainQuery implements Query, QueryMetadata {
     }
 
     @Override
+    public int getQueryTimeout() {
+        return QueryMetadata.QUERY_TIMEOUT_DEFAULT;
+    }
+
+    @Override
     public boolean isSuppressingDistinct() {
         return false;
     }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectsFromDataRowsQuery.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectsFromDataRowsQuery.java
index b80b832..d4ed479 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectsFromDataRowsQuery.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectsFromDataRowsQuery.java
@@ -146,6 +146,11 @@ class ObjectsFromDataRowsQuery implements Query, 
QueryMetadata {
     }
 
     @Override
+    public int getQueryTimeout() {
+        return QUERY_TIMEOUT_DEFAULT;
+    }
+
+    @Override
     public boolean isSuppressingDistinct() {
         return false;
     }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAction.java 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAction.java
index 58a86dd..c4bfb16 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAction.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAction.java
@@ -19,7 +19,6 @@
 package org.apache.cayenne.access.jdbc;
 
 import java.sql.Connection;
-import java.sql.SQLException;
 
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.OperationObserver;
@@ -52,7 +51,7 @@ public class EJBQLAction extends BaseSQLAction {
     }
 
     @Override
-    public void performAction(Connection connection, OperationObserver 
observer) throws SQLException, Exception {
+    public void performAction(Connection connection, OperationObserver 
observer) throws Exception {
         EJBQLCompiledExpression compiledExpression = 
query.getExpression(dataNode.getEntityResolver());
         final EJBQLTranslatorFactory translatorFactory = 
dataNode.getAdapter().getEjbqlTranslatorFactory();
         final EJBQLTranslationContext context = new 
EJBQLTranslationContext(dataNode.getEntityResolver(), query,
@@ -95,6 +94,11 @@ public class EJBQLAction extends BaseSQLAction {
             sqlQuery.setStatementFetchSize(md.getStatementFetchSize());
         }
 
+        int queryTimeout = md.getQueryTimeout();
+        if(queryTimeout != QueryMetadata.QUERY_TIMEOUT_DEFAULT) {
+            sqlQuery.setQueryTimeout(queryTimeout);
+        }
+
         actionFactory.sqlAction(sqlQuery).performAction(connection, observer);
     }
 }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java
index b2b2200..7af5142 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java
@@ -35,6 +35,7 @@ import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.map.Procedure;
 import org.apache.cayenne.map.ProcedureParameter;
 import org.apache.cayenne.query.ProcedureQuery;
+import org.apache.cayenne.query.QueryMetadata;
 
 /**
  * A SQLAction that runs a stored procedure. Note that ProcedureAction has
@@ -220,9 +221,15 @@ public class ProcedureAction extends BaseSQLAction {
         * @throws Exception
         */
        protected void initStatement(CallableStatement statement) throws 
Exception {
-               int statementFetchSize = 
query.getMetaData(dataNode.getEntityResolver()).getStatementFetchSize();
+               QueryMetadata queryMetadata = 
query.getMetaData(dataNode.getEntityResolver());
+               int statementFetchSize = queryMetadata.getStatementFetchSize();
                if (statementFetchSize != 0) {
                        statement.setFetchSize(statementFetchSize);
                }
+
+               int queryTimeout = queryMetadata.getQueryTimeout();
+               if(queryTimeout != QueryMetadata.QUERY_TIMEOUT_DEFAULT) {
+                       statement.setQueryTimeout(queryTimeout);
+               }
        }
 }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
index 90337e1..c3029ab 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
@@ -19,6 +19,18 @@
 
 package org.apache.cayenne.access.jdbc;
 
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.access.DataNode;
@@ -39,18 +51,6 @@ import org.apache.cayenne.query.SQLAction;
 import org.apache.cayenne.query.SQLTemplate;
 import org.apache.cayenne.util.Util;
 
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
 /**
  * Implements a strategy for execution of SQLTemplates.
  *
@@ -403,6 +403,11 @@ public class SQLTemplateAction implements SQLAction {
                if (queryMetadata.getStatementFetchSize() != 0) {
                        
preparedStatement.setFetchSize(queryMetadata.getStatementFetchSize());
                }
+
+               int queryTimeout = queryMetadata.getQueryTimeout();
+               if(queryTimeout != QueryMetadata.QUERY_TIMEOUT_DEFAULT) {
+                       preparedStatement.setQueryTimeout(queryTimeout);
+               }
        }
 
        /**
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java
index 085cc3e..4f640a9 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java
@@ -19,6 +19,11 @@
 
 package org.apache.cayenne.access.jdbc;
 
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.List;
+
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.OperationObserver;
@@ -32,11 +37,6 @@ import org.apache.cayenne.query.PrefetchTreeNode;
 import org.apache.cayenne.query.QueryMetadata;
 import org.apache.cayenne.query.Select;
 
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.List;
-
 /**
  * A SQLAction that handles SelectQuery execution.
  * 
@@ -97,6 +97,11 @@ public class SelectAction extends BaseSQLAction {
                        statement.setFetchSize(fetchSize);
                }
 
+               int queryTimeout = queryMetadata.getQueryTimeout();
+               if(queryTimeout != QueryMetadata.QUERY_TIMEOUT_DEFAULT) {
+                       statement.setQueryTimeout(queryTimeout);
+               }
+
                ResultSet rs;
 
                // need to run in try-catch block to close statement properly if
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
index ef31715..3b1142e 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
@@ -27,15 +27,12 @@ import java.util.Map;
 import java.util.StringTokenizer;
 
 import org.apache.cayenne.Persistent;
-import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.map.Procedure;
 import org.apache.cayenne.reflect.ClassDescriptor;
-import org.apache.cayenne.util.XMLEncoder;
-import org.apache.cayenne.util.XMLSerializable;
 
 /**
  * Default mutable implementation of {@link QueryMetadata}.
@@ -50,6 +47,7 @@ class BaseQueryMetadata implements QueryMetadata, 
Serializable {
        int fetchOffset = QueryMetadata.FETCH_OFFSET_DEFAULT;
 
        int statementFetchSize = QueryMetadata.FETCH_OFFSET_DEFAULT;
+       int queryTimeout = QueryMetadata.QUERY_TIMEOUT_DEFAULT;
 
        int pageSize = QueryMetadata.PAGE_SIZE_DEFAULT;
        boolean fetchingDataRows = QueryMetadata.FETCHING_DATA_ROWS_DEFAULT;
@@ -386,6 +384,22 @@ class BaseQueryMetadata implements QueryMetadata, 
Serializable {
        }
 
        /**
+        * Sets query timeout(0 means no limit, -1 if doesn't set)
+        * @since 4.2
+        */
+       void setQueryTimeout(int queryTimeout) {
+               this.queryTimeout = queryTimeout;
+       }
+
+       /**
+        * @return query timeout
+        * @since 4.2
+        */
+       public int getQueryTimeout() {
+               return queryTimeout;
+       }
+
+       /**
         * Adds a joint prefetch.
         * 
         * @since 1.2
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
index fb380e6..b82c8c8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
@@ -299,6 +299,15 @@ public class ColumnSelect<T> extends FluentSelect<T> {
         return this;
     }
 
+    /**
+     * Sets query timeout of PreparedStatement generated for this query.
+     * @see Statement#setQueryTimeout(int)
+     */
+    public ColumnSelect<T> queryTimeout(int timeout) {
+        this.metaData.setQueryTimeout(timeout);
+        return this;
+    }
+
     public ColumnSelect<T> cacheStrategy(QueryCacheStrategy strategy) {
         setCacheStrategy(strategy);
         setCacheGroup(null);
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/DefaultQueryMetadata.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/query/DefaultQueryMetadata.java
index 05a69ca..af89c59 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/query/DefaultQueryMetadata.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/query/DefaultQueryMetadata.java
@@ -147,6 +147,11 @@ class DefaultQueryMetadata implements QueryMetadata {
         return QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT;
     }
 
+    @Override
+    public int getQueryTimeout() {
+        return QueryMetadata.QUERY_TIMEOUT_DEFAULT;
+    }
+
     /**
      * @since 4.0
      */
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/EJBQLQuery.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/EJBQLQuery.java
index 67ff9e0..5bf9506 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/EJBQLQuery.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/EJBQLQuery.java
@@ -18,19 +18,16 @@
  ****************************************************************/
 package org.apache.cayenne.query;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.ejbql.EJBQLCompiledExpression;
 import org.apache.cayenne.ejbql.EJBQLException;
 import org.apache.cayenne.ejbql.EJBQLParserFactory;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.util.XMLEncoder;
-import org.apache.cayenne.util.XMLSerializable;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * An EJBQL query representation in Cayenne.
@@ -221,6 +218,20 @@ public class EJBQLQuery extends CacheableQuery {
     public int getStatementFetchSize() {
         return metadata.getStatementFetchSize();
     }
-    
-    
+
+    /**
+     * Sets query timeout.
+     * @since 4.2
+     */
+    public void setQueryTimeout(int queryTimeout) {
+        metadata.setQueryTimeout(queryTimeout);
+    }
+
+    /**
+     * @return query timeout
+     * @since 4.2
+     */
+    public int getQueryTimeout() {
+        return metadata.getQueryTimeout();
+    }
 }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/FluentSelect.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/FluentSelect.java
index 2ce013c..42a055b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/FluentSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/FluentSelect.java
@@ -84,6 +84,13 @@ public abstract class FluentSelect<T> extends AbstractQuery 
implements Select<T>
         return getBaseMetaData().getStatementFetchSize();
     }
 
+    /**
+     * @since 4.2
+     */
+    public int getQueryTimeout() {
+        return getBaseMetaData().getQueryTimeout();
+    }
+
     public int getPageSize() {
         return getBaseMetaData().getPageSize();
     }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/MappedSelect.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/MappedSelect.java
index e289064..be7a378 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/MappedSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/MappedSelect.java
@@ -18,6 +18,10 @@
  ****************************************************************/
 package org.apache.cayenne.query;
 
+import java.sql.Statement;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.ResultBatchIterator;
@@ -26,10 +30,6 @@ import org.apache.cayenne.ResultIteratorCallback;
 import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.map.QueryDescriptor;
 
-import java.sql.Statement;
-import java.util.List;
-import java.util.Map;
-
 /**
  * A query that represents a named parameterized selecting query stored in the 
mapping. The
  * actual query is resolved during execution.
@@ -62,6 +62,7 @@ public class MappedSelect<T> extends AbstractMappedQuery 
implements Select<T> {
     protected Integer fetchLimit;
     protected Integer fetchOffset;
     protected Integer statementFetchSize;
+    protected Integer queryTimeout;
     protected Integer pageSize;
     protected boolean forceNoCache;
 
@@ -107,6 +108,18 @@ public class MappedSelect<T> extends AbstractMappedQuery 
implements Select<T> {
     }
 
     /**
+     * Sets query timeout for the PreparedStatement generated for this query.
+     *
+     * @see Statement#setQueryTimeout(int)
+     * @since 4.2
+     */
+    public MappedSelect<T> queryTimeout(int timeout) {
+        this.queryTimeout = timeout;
+        this.replacementQuery = null;
+        return this;
+    }
+
+    /**
      * Resets query page size. A non-negative page size enables query result
      * pagination that saves memory and processing time for large lists if only
      * parts of the result are ever going to be accessed.
@@ -214,6 +227,9 @@ public class MappedSelect<T> extends AbstractMappedQuery 
implements Select<T> {
                 if (statementFetchSize != null) {
                     sqlTemplate.setStatementFetchSize(statementFetchSize);
                 }
+                if(queryTimeout != null) {
+                    sqlTemplate.setQueryTimeout(queryTimeout);
+                }
                 if (pageSize != null) {
                     sqlTemplate.setPageSize(pageSize);
                 }
@@ -232,6 +248,9 @@ public class MappedSelect<T> extends AbstractMappedQuery 
implements Select<T> {
                 if (statementFetchSize != null) {
                     ejbqlQuery.setStatementFetchSize(statementFetchSize);
                 }
+                if(queryTimeout != null) {
+                    ejbqlQuery.setQueryTimeout(queryTimeout);
+                }
                 if (pageSize != null) {
                     ejbqlQuery.setPageSize(pageSize);
                 }
@@ -250,6 +269,9 @@ public class MappedSelect<T> extends AbstractMappedQuery 
implements Select<T> {
                 if (statementFetchSize != null) {
                     procedureQuery.setStatementFetchSize(statementFetchSize);
                 }
+                if(queryTimeout != null) {
+                    procedureQuery.setQueryTimeout(queryTimeout);
+                }
                 if (pageSize != null) {
                     procedureQuery.setPageSize(pageSize);
                 }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
index 66e4762..1ce27ac 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
@@ -441,6 +441,17 @@ public class ObjectSelect<T> extends FluentSelect<T> {
         return this;
     }
 
+    /**
+     * Sets query timeout for PreparedStatement generated for this query.
+     *
+     * @see Statement#setQueryTimeout(int)
+     * @since 4.2
+     */
+    public ObjectSelect<T> queryTimeout(int timeout) {
+        this.metaData.setQueryTimeout(timeout);
+        return this;
+    }
+
     public ObjectSelect<T> cacheStrategy(QueryCacheStrategy strategy) {
         setCacheStrategy(strategy);
         setCacheGroup(null);
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/ProcedureQuery.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/ProcedureQuery.java
index 98e302f..7e77d6b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/ProcedureQuery.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ProcedureQuery.java
@@ -19,14 +19,6 @@
 
 package org.apache.cayenne.query;
 
-import org.apache.cayenne.access.jdbc.ColumnDescriptor;
-import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.map.Procedure;
-import org.apache.cayenne.map.QueryDescriptor;
-import org.apache.cayenne.util.XMLEncoder;
-import org.apache.cayenne.util.XMLSerializable;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -34,6 +26,10 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.cayenne.access.jdbc.ColumnDescriptor;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.Procedure;
+
 /**
  * A query based on Procedure. Can be used as a select query, or as a query of 
an
  * arbitrary complexity, performing data modification, selecting data 
(possibly with
@@ -430,4 +426,20 @@ public class ProcedureQuery extends AbstractQuery 
implements ParameterizedQuery
     public int getStatementFetchSize() {
         return metaData.getStatementFetchSize();
     }
+
+    /**
+     * @return query timeout
+     * @since 4.2
+     */
+    public int getQueryTimeout() {
+        return metaData.getQueryTimeout();
+    }
+
+    /**
+     * Set's query timeout
+     * @since 4.2
+     */
+    public void setQueryTimeout(int timeout) {
+        metaData.setQueryTimeout(timeout);
+    }
 }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java
index 40e1793..396cac1 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java
@@ -63,6 +63,8 @@ public interface QueryMetadata {
      */
     int FETCH_OFFSET_DEFAULT = 0;
 
+    int QUERY_TIMEOUT_DEFAULT = -1;
+
     /**
      * Defines the name of the property for the query {@link #getPageSize() 
page size}.
      */
@@ -245,6 +247,12 @@ public interface QueryMetadata {
     int getStatementFetchSize();
 
     /**
+     * @return query timeout
+     * @since 4.2
+     */
+    int getQueryTimeout();
+
+    /**
      * @since 4.0
      */
     boolean isSuppressingDistinct();
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadataProxy.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadataProxy.java
index e1cef43..b39b177 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadataProxy.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadataProxy.java
@@ -133,6 +133,11 @@ public class QueryMetadataProxy implements QueryMetadata {
     }
 
     @Override
+    public int getQueryTimeout() {
+        return mdDelegate.getQueryTimeout();
+    }
+
+    @Override
     public boolean isSuppressingDistinct() {
         return mdDelegate.isSuppressingDistinct();
     }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLExec.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLExec.java
index ee42f67..00c55b2 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLExec.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLExec.java
@@ -18,6 +18,12 @@
  ****************************************************************/
 package org.apache.cayenne.query;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.QueryResponse;
@@ -26,12 +32,6 @@ import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.util.QueryResultBuilder;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 /**
  * A generic query based on raw SQL and featuring fluent API. While
  * {@link SQLExec} can be used to select data (see {@link 
#execute(ObjectContext)}
@@ -65,6 +65,7 @@ public class SQLExec extends IndirectQuery {
     protected Map<String, Object> params;
     protected List<Object> positionalParams;
     protected boolean returnGeneratedKeys;
+    protected int queryTimeout;
 
     public SQLExec(String sql) {
         this.sqlBuffer = sql != null ? new StringBuilder(sql) : new 
StringBuilder();
@@ -220,6 +221,14 @@ public class SQLExec extends IndirectQuery {
         return this;
     }
 
+    /**
+     * @since 4.2
+     */
+    public SQLExec queryTimeout(int queryTimeout) {
+        this.queryTimeout = queryTimeout;
+        return this;
+    }
+
     @Override
     protected Query createReplacementQuery(EntityResolver resolver) {
 
@@ -242,6 +251,7 @@ public class SQLExec extends IndirectQuery {
         template.setDefaultTemplate(getSql());
         template.setFetchingDataRows(true); // in case result set will be 
returned
         template.setReturnGeneratedKeys(returnGeneratedKeys);
+        template.setQueryTimeout(queryTimeout);
 
         if (positionalParams != null) {
             template.setParamsList(positionalParams);
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 96af190..b2f426f 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
@@ -255,6 +255,7 @@ public class SQLSelect<T> extends IndirectQuery implements 
Select<T> {
        protected int offset;
        protected int pageSize;
        protected int statementFetchSize;
+       protected int queryTimeout;
        protected PrefetchTreeNode prefetches;
 
        public SQLSelect(String sql) {
@@ -440,6 +441,7 @@ public class SQLSelect<T> extends IndirectQuery implements 
Select<T> {
                template.setFetchOffset(offset);
                template.setPageSize(pageSize);
                template.setStatementFetchSize(statementFetchSize);
+               template.setQueryTimeout(queryTimeout);
                template.setUseScalar(useScalar);
 
                return template;
@@ -623,12 +625,29 @@ public class SQLSelect<T> extends IndirectQuery 
implements Select<T> {
        }
 
        /**
+        * Sets query timeout
+        * @since 4.2
+        */
+       public SQLSelect<T> queryTimeout(int queryTimeout) {
+               if(this.queryTimeout != queryTimeout) {
+                       this.queryTimeout = queryTimeout;
+                       this.replacementQuery = null;
+               }
+
+               return this;
+       }
+
+       /**
         * @return JBDC statement's fetch size
         */
        public int getStatementFetchSize() {
                return statementFetchSize;
        }
 
+       public int getQueryTimeout() {
+               return queryTimeout;
+       }
+
        /**
         * Merges a prefetch path with specified semantics into the query 
prefetch tree.
         * @param path Path expression
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 3f27484..b2d5e9a 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
@@ -19,14 +19,6 @@
 
 package org.apache.cayenne.query;
 
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.access.QueryEngine;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.map.SQLResult;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -38,6 +30,14 @@ import java.util.Map;
 import java.util.function.Function;
 import java.util.stream.Stream;
 
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.access.QueryEngine;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.SQLResult;
+
 /**
  * A query that executes unchanged (except for template preprocessing) "raw" 
SQL
  * specified by the user. <h3>Template Script</h3>
@@ -625,6 +625,22 @@ public class SQLTemplate extends AbstractQuery implements 
ParameterizedQuery {
        }
 
        /**
+        * Sets query timeout.
+        * @since 4.2
+        */
+       public void setQueryTimeout(int queryTimeout) {
+               metaData.setQueryTimeout(queryTimeout);
+       }
+
+       /**
+        * @return query timeout
+        * @since 4.2
+        */
+       public int getQueryTimeout() {
+               return metaData.getQueryTimeout();
+       }
+
+       /**
         * 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.
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
index 51f225e..55c5a68 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
@@ -19,25 +19,25 @@
 
 package org.apache.cayenne.query;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
-import org.apache.cayenne.exp.property.BaseProperty;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.property.BaseProperty;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.map.ObjEntity;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
 /**
  * A query that selects persistent objects of a certain type or "raw data" (aka
  * DataRows). Supports expression qualifier, multiple orderings and a number of
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/query/MockQueryMetadata.java 
b/cayenne-server/src/test/java/org/apache/cayenne/query/MockQueryMetadata.java
index 6d2e533..aa695a4 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/query/MockQueryMetadata.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/query/MockQueryMetadata.java
@@ -112,6 +112,11 @@ public class MockQueryMetadata implements QueryMetadata {
     }
 
     @Override
+    public int getQueryTimeout() {
+        return QueryMetadata.QUERY_TIMEOUT_DEFAULT;
+    }
+
+    @Override
     public boolean isSuppressingDistinct() {
         return false;
     }
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java 
b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
index 21dad3e..1179ded 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
@@ -18,13 +18,6 @@
  ****************************************************************/
 package org.apache.cayenne.query;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -35,6 +28,8 @@ import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.testdo.testmap.Artist;
 import org.junit.Test;
 
+import static org.junit.Assert.*;
+
 public class ObjectSelectTest {
 
        @Test
@@ -428,4 +423,16 @@ public class ObjectSelectTest {
                assertNull(q.getCacheGroup());
        }
 
+       @Test
+       public void testQueryTimeout() {
+               ObjectSelect<Artist> query = ObjectSelect.query(Artist.class);
+               assertEquals(-1, query.getQueryTimeout());
+
+               query.queryTimeout(10);
+               assertEquals(10, query.getQueryTimeout());
+
+               query.queryTimeout(1).queryTimeout(2);
+               assertEquals(2, query.getQueryTimeout());
+       }
+
 }
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/query/QueryTimeoutIT.java 
b/cayenne-server/src/test/java/org/apache/cayenne/query/QueryTimeoutIT.java
new file mode 100644
index 0000000..d84199f
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/QueryTimeoutIT.java
@@ -0,0 +1,113 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.query;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class QueryTimeoutIT extends ServerCase {
+
+    @Inject
+    private ObjectContext context;
+
+    @Test
+    public void testObjectSelect() {
+        ObjectSelect<Artist> objectSelect = ObjectSelect.query(Artist.class)
+                .queryTimeout(10);
+        assertEquals(10, objectSelect
+                .getMetaData(context.getEntityResolver())
+                .getQueryTimeout());
+        objectSelect.select(context);
+    }
+
+    @Test
+    public void testSQLTemplate() {
+        SQLTemplate sqlTemplate = new SQLTemplate();
+        sqlTemplate.setDefaultTemplate("SELECT * FROM ARTIST");
+        sqlTemplate.setQueryTimeout(10);
+        assertEquals(10, sqlTemplate
+                .getMetaData(context.getEntityResolver())
+                .getQueryTimeout());
+        context.performQuery(sqlTemplate);
+    }
+
+    @Test
+    public void testColumnSelect() {
+        ColumnSelect<String> columnSelect = ObjectSelect
+                .columnQuery(Artist.class, Artist.ARTIST_NAME)
+                .queryTimeout(10);
+        assertEquals(10, columnSelect
+                .getMetaData(context.getEntityResolver())
+                .getQueryTimeout());
+        context.performQuery(columnSelect);
+    }
+
+    @Test
+    public void testEjbql() {
+        EJBQLQuery ejbqlQuery = new EJBQLQuery("select a from Artist a");
+        ejbqlQuery.setQueryTimeout(10);
+        assertEquals(10, ejbqlQuery
+                .getMetaData(context.getEntityResolver())
+                .getQueryTimeout());
+        context.performQuery(ejbqlQuery);
+    }
+
+    @Test
+    public void testSqlSelect() {
+        SQLSelect<Artist> sqlSelect = SQLSelect
+                .query(Artist.class, "SELECT * FROM ARTIST")
+                .queryTimeout(10);
+        assertEquals(10, sqlSelect
+                .getMetaData(context.getEntityResolver())
+                .getQueryTimeout());
+        context.performQuery(sqlSelect);
+    }
+
+    @Test
+    public void testSqlExec() {
+        SQLExec sqlExec = SQLExec
+                .query("SELECT * FROM ARTIST")
+                .queryTimeout(10);
+        assertEquals(10, sqlExec
+                .getMetaData(context.getEntityResolver())
+                .getQueryTimeout());
+        context.performQuery(sqlExec);
+    }
+
+    @Test
+    public void testMappedSelect() {
+        MappedSelect<Artist> mappedSelect = MappedSelect
+                .query("SelectTestUpper", Artist.class)
+                .queryTimeout(10);
+        Query replacementQuery = 
mappedSelect.createReplacementQuery(context.getEntityResolver());
+        assertEquals(10, replacementQuery
+                .getMetaData(context.getEntityResolver())
+                .getQueryTimeout());
+        context.performQuery(replacementQuery);
+    }
+}

Reply via email to