Repository: cayenne Updated Branches: refs/heads/master 831442cb6 -> fb7f004a8
CAY-2481 Methods to return Object[] after SQLTemplate and SQLExec perform Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/18c72d34 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/18c72d34 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/18c72d34 Branch: refs/heads/master Commit: 18c72d34710646bed3c51a6b64285a597a56c2b6 Parents: 553de26 Author: Arseni Bulatski <ancars...@gmail.com> Authored: Wed Oct 24 09:26:36 2018 +0300 Committer: Arseni Bulatski <ancars...@gmail.com> Committed: Wed Oct 24 12:04:14 2018 +0300 ---------------------------------------------------------------------- .../access/jdbc/RowDescriptorBuilder.java | 2 +- .../cayenne/access/jdbc/SQLTemplateAction.java | 26 +- .../cayenne/map/DefaultScalarResultSegment.java | 4 +- .../org/apache/cayenne/query/SQLSelect.java | 84 ++++++- .../org/apache/cayenne/query/SQLTemplate.java | 17 +- .../cayenne/query/SQLTemplateMetadata.java | 29 ++- .../org/apache/cayenne/query/SQLSelectIT.java | 84 ++++++- .../org/apache/cayenne/query/SQLTemplateIT.java | 241 ++++++++++++++++++- 8 files changed, 452 insertions(+), 35 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/18c72d34/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/RowDescriptorBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/RowDescriptorBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/RowDescriptorBuilder.java index 6c92c12..7ab51b7 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/RowDescriptorBuilder.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/RowDescriptorBuilder.java @@ -106,7 +106,7 @@ public class RowDescriptorBuilder { throw new CayenneRuntimeException("Size of 'ResultSetMetadata' not equals to size of 'columns'."); } else if (rsLen < columnLen) { throw new CayenneRuntimeException("'ResultSetMetadata' has less elements then 'columns'."); - } else if (rsLen == columnLen && !mergeColumnsWithRsMetadata) { + } else if(rsLen == columnLen && !mergeColumnsWithRsMetadata) { // 'columns' contains ColumnDescriptor for every column // in resultSetMetadata. This return is for optimization. return columns; http://git-wip-us.apache.org/repos/asf/cayenne/blob/18c72d34/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java ---------------------------------------------------------------------- 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 cc6ac56..2f27eb0 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 @@ -20,6 +20,7 @@ package org.apache.cayenne.access.jdbc; import org.apache.cayenne.CayenneException; +import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.ResultIterator; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.OperationObserver; @@ -31,6 +32,7 @@ import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dba.TypesMapping; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.DefaultScalarResultSegment; import org.apache.cayenne.map.ObjAttribute; import org.apache.cayenne.map.ObjEntity; import org.apache.cayenne.query.QueryMetadata; @@ -250,8 +252,8 @@ public class SQLTemplateAction implements SQLAction { boolean iteratedResult = callback.isIteratedResult(); ExtendedTypeMap types = dataNode.getAdapter().getExtendedTypes(); RowDescriptorBuilder builder = configureRowDescriptorBuilder(compiled, resultSet); + recreateQueryMetadata(resultSet); RowReader<?> rowReader = dataNode.rowReader(builder.getDescriptor(types), queryMetadata); - ResultIterator<?> it = new JDBCResultIterator<>(statement, resultSet, rowReader); if (iteratedResult) { @@ -286,13 +288,25 @@ public class SQLTemplateAction implements SQLAction { } } + private void recreateQueryMetadata(ResultSet resultSet) throws SQLException { + if(query.isUseScalar() && queryMetadata.getResultSetMapping() != null && queryMetadata.getResultSetMapping().isEmpty()){ + for(int i = 0; i < resultSet.getMetaData().getColumnCount(); i++) { + queryMetadata.getResultSetMapping().add(new DefaultScalarResultSegment(String.valueOf(i), i)); + } + } + } + /** * Creates column descriptors based on compiled statement and query metadata */ private ColumnDescriptor[] createColumnDescriptors(SQLStatement compiled) { // SQLTemplate #result columns take precedence over other ways to determine the type if (compiled.getResultColumns().length > 0) { - return compiled.getResultColumns(); + if(query.getResultColumnsTypes() != null) { + throw new CayenneRuntimeException("Caused by setting return types by directives and by parameters in query."); + } else { + return compiled.getResultColumns(); + } } // check explicitly set column types @@ -325,7 +339,7 @@ public class SQLTemplateAction implements SQLAction { } ObjEntity entity = queryMetadata.getObjEntity(); - if (entity != null) { + if (entity != null && isResultColumnTypesEmpty()) { // TODO: andrus 2008/03/28 support flattened attributes with aliases... for (ObjAttribute attribute : entity.getAttributes()) { String column = attribute.getDbAttributePath(); @@ -339,7 +353,7 @@ public class SQLTemplateAction implements SQLAction { // override numeric Java types based on JDBC defaults for DbAttributes, as Oracle // ResultSetMetadata is not very precise about NUMERIC distinctions... // (BigDecimal vs Long vs. Integer) - if (dbEntity != null) { + if (dbEntity != null && isResultColumnTypesEmpty()) { for (DbAttribute attribute : dbEntity.getAttributes()) { if (!builder.isOverriden(attribute.getName()) && TypesMapping.isNumeric(attribute.getType())) { builder.overrideColumnType(attribute.getName(), TypesMapping.getJavaBySqlType(attribute.getType())); @@ -359,6 +373,10 @@ public class SQLTemplateAction implements SQLAction { return builder; } + private boolean isResultColumnTypesEmpty(){ + return query.getResultColumnsTypes() == null || query.getResultColumnsTypes().isEmpty(); + } + /** * Extracts a template string from a SQLTemplate query. Exists mainly for * the benefit of subclasses that can customize returned template. http://git-wip-us.apache.org/repos/asf/cayenne/blob/18c72d34/cayenne-server/src/main/java/org/apache/cayenne/map/DefaultScalarResultSegment.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/DefaultScalarResultSegment.java b/cayenne-server/src/main/java/org/apache/cayenne/map/DefaultScalarResultSegment.java index 1b25684..b0933dd 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/map/DefaultScalarResultSegment.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/map/DefaultScalarResultSegment.java @@ -23,12 +23,12 @@ import org.apache.cayenne.query.ScalarResultSegment; /** * @since 3.0 */ -class DefaultScalarResultSegment implements ScalarResultSegment { +public class DefaultScalarResultSegment implements ScalarResultSegment { private String column; private int offset; - DefaultScalarResultSegment(String column, int offset) { + public DefaultScalarResultSegment(String column, int offset) { this.column = column; this.offset = offset; } http://git-wip-us.apache.org/repos/asf/cayenne/blob/18c72d34/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 3d46f6a..c276a78 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 @@ -29,7 +29,6 @@ import org.apache.cayenne.map.EntityResolver; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -45,12 +44,34 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { private static final long serialVersionUID = -7074293371883740872L; private List<Class<?>> resultColumnsTypes; + private boolean useScalar; + private boolean isFetchingDataRows; /** * Creates a query that selects DataRows and uses default routing. */ public static SQLSelect<DataRow> dataRowQuery(String sql) { - return new SQLSelect<>(sql); + return new SQLSelect<>(sql) + .fetchingDataRows(); + } + + /** + * Creates a query that selects DataRows and uses default routing. + * @since 4.1 + */ + public static SQLSelect<DataRow> dataRowQuery(String sql, Class<?>... types) { + return new SQLSelect<>(sql) + .resultColumnsTypes(types) + .fetchingDataRows(); + } + + /** + * Creates a query that selects DataRows and uses routing based on the + * provided DataMap name. + * @since 4.1 + */ + public static SQLSelect<DataRow> dataRowQuery(String dataMapName, String sql, Class<?>... types) { + return dataRowQuery(dataMapName, sql).resultColumnsTypes(types); } /** @@ -60,7 +81,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { public static SQLSelect<DataRow> dataRowQuery(String dataMapName, String sql) { SQLSelect<DataRow> query = new SQLSelect<>(sql); query.dataMapName = dataMapName; - return query; + return query.fetchingDataRows(); } /** @@ -84,7 +105,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { public static <T> SQLSelect<T> scalarQuery(Class<T> type, String dataMapName, String sql) { SQLSelect<T> query = new SQLSelect<>(sql); query.dataMapName = dataMapName; - return query.resultColumnsTypes(type); + return query.resultColumnsTypes(type).useScalar(); } /** @@ -94,7 +115,39 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { */ public static <T> SQLSelect<T> scalarQuery(String sql, Class<T> type) { SQLSelect<T> query = new SQLSelect<>(sql); - return query.resultColumnsTypes(type); + return query.resultColumnsTypes(type).useScalar(); + } + + /** + * Creates query that selects scalar value and uses default routing + * + * @since 4.1 + */ + public static <T> SQLSelect<T> scalarQuery(String sql, String dataMapName, Class<T> type) { + SQLSelect<T> query = new SQLSelect<>(sql); + query.dataMapName = dataMapName; + return query.resultColumnsTypes(type).useScalar(); + } + + /** + * Creates query that selects scalar value and uses default routing + * + * @since 4.1 + */ + public static SQLSelect<Object[]> scalarQuery(String sql) { + SQLSelect<Object[]> query = new SQLSelect<>(sql); + return query.useScalar(); + } + + /** + * Creates query that selects scalar values (as Object[]) and uses routing based on the + * provided DataMap name. + * @since 4.1 + */ + public static SQLSelect<Object[]> scalarQuery(String sql, String dataMapName) { + SQLSelect<Object[]> query = new SQLSelect<>(sql); + query.dataMapName = dataMapName; + return query.useScalar(); } /** @@ -104,7 +157,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { */ public static SQLSelect<Object[]> scalarQuery(String sql, Class<?> firstType, Class<?>... types) { SQLSelect<Object[]> query = new SQLSelect<>(sql); - return query.resultColumnsTypes(firstType).resultColumnsTypes(types); + return query.resultColumnsTypes(firstType).resultColumnsTypes(types).useScalar(); } /** @@ -116,7 +169,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { public static SQLSelect<Object[]> scalarQuery(String sql, String dataMapName, Class<?> firstType, Class<?>... types) { SQLSelect<Object[]> query = new SQLSelect<>(sql); query.dataMapName = dataMapName; - return query.resultColumnsTypes(firstType).resultColumnsTypes(types); + return query.resultColumnsTypes(firstType).resultColumnsTypes(types).useScalar(); } @SuppressWarnings("unchecked") @@ -185,7 +238,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { } public boolean isFetchingDataRows() { - return persistentType == null; + return isFetchingDataRows; } public String getSql() { @@ -291,7 +344,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { } SQLTemplate template = new SQLTemplate(); - template.setFetchingDataRows(isFetchingDataRows()); + template.setFetchingDataRows(isFetchingDataRows); template.setRoot(root); template.setDefaultTemplate(getSql()); template.setCacheGroup(cacheGroup); @@ -312,6 +365,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { template.setFetchOffset(offset); template.setPageSize(pageSize); template.setStatementFetchSize(statementFetchSize); + template.setUseScalar(useScalar); return template; } @@ -534,4 +588,16 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> { prefetches.merge(node); return this; } + + @SuppressWarnings("unchecked") + private <E> SQLSelect<E> fetchingDataRows() { + this.isFetchingDataRows = true; + return (SQLSelect<E>) this; + } + + @SuppressWarnings("unchecked") + private <E> SQLSelect<E> useScalar() { + this.useScalar = true; + return (SQLSelect<E>) this; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/18c72d34/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 fa863c3..55a590d 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 @@ -84,6 +84,7 @@ public class SQLTemplate extends AbstractQuery implements ParameterizedQuery { protected boolean returnGeneratedKeys; private List<Class<?>> resultColumnsTypes; + private boolean useScalar; SQLTemplateMetadata metaData = new SQLTemplateMetadata(); @@ -108,12 +109,11 @@ public class SQLTemplate extends AbstractQuery implements ParameterizedQuery { setFetchingDataRows(isFetchingDataRows); } - public SQLTemplate resultColumnsTypes(Class<?> ...types) { + public void setResultColumnsTypes(Class<?> ...types) { if(resultColumnsTypes == null) { resultColumnsTypes = new ArrayList<>(types.length); } Collections.addAll(resultColumnsTypes, types); - return this; } @Override @@ -671,4 +671,17 @@ public class SQLTemplate extends AbstractQuery implements ParameterizedQuery { public void setResultColumnsTypes(List<Class<?>> resultColumnsTypes) { this.resultColumnsTypes = resultColumnsTypes; } + + /** + * Sets flag to use scalars. + * + * @since 4.1 + */ + public void setUseScalar(boolean useScalar) { + this.useScalar = useScalar; + } + + public boolean isUseScalar() { + return useScalar; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/18c72d34/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplateMetadata.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplateMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplateMetadata.java index 1537f8a..3cd99a4 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplateMetadata.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplateMetadata.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.query; +import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.map.EntityResolver; import org.apache.cayenne.map.ObjEntity; import org.apache.cayenne.map.SQLResult; @@ -30,14 +31,36 @@ import java.util.Map; /** * @since 3.0 */ -class SQLTemplateMetadata extends BaseQueryMetadata { +public class SQLTemplateMetadata extends BaseQueryMetadata { + + private boolean isSingleResultSetMapping; + + @Override + public boolean isSingleResultSetMapping() { + return isSingleResultSetMapping; + } boolean resolve(Object root, EntityResolver resolver, SQLTemplate query) { if (super.resolve(root, resolver)) { + if((!query.isUseScalar() && !query.isFetchingDataRows()) && (query.getResultColumnsTypes() != null && !query.getResultColumnsTypes().isEmpty())) { + throw new CayenneRuntimeException("Error caused by using root in query with resultColumnTypes without scalar or dataRow."); + } + + if(query.getResult() != null && query.getResultColumnsTypes() != null) { + throw new CayenneRuntimeException("Caused by trying to override result column types of query."); + } + + if(query.isFetchingDataRows() && query.isUseScalar()) { + throw new CayenneRuntimeException("Can't set both use scalar and fetching data rows."); + } + buildResultSetMappingForColumns(query); - resultSetMapping = query.getResult() != null ? query.getResult().getResolvedComponents(resolver) : null; + resultSetMapping = query.getResult() != null ? + query.getResult().getResolvedComponents(resolver) : + query.isUseScalar() ? new ArrayList<>() : null; + isSingleResultSetMapping = resultSetMapping != null && resultSetMapping.size() == 1; // generate unique cache key... if (QueryCacheStrategy.NO_CACHE == getCacheStrategy()) { @@ -93,7 +116,7 @@ class SQLTemplateMetadata extends BaseQueryMetadata { } private void buildResultSetMappingForColumns(SQLTemplate query) { - if(query.getResultColumnsTypes() == null || query.getResultColumnsTypes().isEmpty()) { + if(query.getResultColumnsTypes() == null || query.getResultColumnsTypes().isEmpty() || !query.isUseScalar()) { return; } SQLResult result = new SQLResult(); http://git-wip-us.apache.org/repos/asf/cayenne/blob/18c72d34/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java index 7774c8c..cbc7c83 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java @@ -43,6 +43,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.*; @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) @@ -114,15 +115,90 @@ public class SQLSelectIT extends ServerCase { } @Test - public void test_ObjectArrayQuery() throws Exception { + public void test_DataRowWithTypes() throws Exception { + tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); + tArtistCt.insert(2, "Test1", new Date(System.currentTimeMillis())); + List<DataRow> result = SQLSelect.dataRowQuery("SELECT * FROM ARTIST_CT", Integer.class, String.class, LocalDateTime.class) + .columnNameCaps(CapsStrategy.UPPER) + .select(context); + assertEquals(2, result.size()); + assertTrue(result.get(0) instanceof DataRow); + assertThat(result.get(0).get("DATE_OF_BIRTH"), instanceOf(LocalDateTime.class)); + } + + @Test + public void test_DataRowWithDirectives() throws Exception { + tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); + tArtistCt.insert(2, "Test1", new Date(System.currentTimeMillis())); + List<DataRow> result = SQLSelect.dataRowQuery("SELECT #result('ARTIST_ID' 'java.lang.Double'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST_CT") + .select(context); + assertEquals(2, result.size()); + assertTrue(result.get(0) instanceof DataRow); + assertTrue(result.get(0).get("ARTIST_ID") instanceof Double); + } + + @Test(expected = CayenneRuntimeException.class) + public void test_DataRowWithTypesException() throws Exception { + tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); + SQLSelect.dataRowQuery("SELECT * FROM ARTIST_CT", Integer.class, String.class) + .select(context); + } + + @Test + public void testObjectArrayWithDefaultTypesReturnAndDirectives() throws Exception { + tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); + tArtistCt.insert(2, "Test1", new Date(System.currentTimeMillis())); + List<Object[]> result = SQLSelect.scalarQuery("SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST_CT") + .select(context); + + assertEquals(2, result.size()); + assertTrue(result.get(0) instanceof Object[]); + assertEquals(2, result.get(0).length); + assertTrue(result.get(0)[0] instanceof Long); + assertTrue(result.get(0)[1] instanceof String); + } + + @Test(expected = CayenneRuntimeException.class) + public void testObjectArrayReturnAndDirectives() throws Exception { + tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); + tArtistCt.insert(2, "Test1", new Date(System.currentTimeMillis())); + SQLSelect.scalarQuery("SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST_CT", + Integer.class, String.class).select(context); + } + + @Test + public void testObjectArrayWithOneObjectDefaultTypesReturnAndDirectives() throws Exception { + tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); + tArtistCt.insert(2, "Test1", new Date(System.currentTimeMillis())); + List<Object[]> result = SQLSelect.scalarQuery("SELECT #result('ARTIST_ID' 'java.lang.Long') FROM ARTIST_CT") + .select(context); + + assertEquals(2, result.size()); + assertTrue(result.get(0) instanceof Object[]); + assertEquals(1, result.get(0).length); + assertTrue(result.get(0)[0] instanceof Long); + } + + @Test + public void test_ObjectArrayQueryWithDefaultTypes() throws Exception { createPaintingsDataSet(); - List<Object[]> result = SQLSelect.scalarQuery("SELECT PAINTING_ID, PAINTING_TITLE, ESTIMATED_PRICE FROM PAINTING", Integer.class, String.class, Double.class) + List<Object[]> result = SQLSelect.scalarQuery("SELECT PAINTING_ID, PAINTING_TITLE, ESTIMATED_PRICE FROM PAINTING") .select(context); assertEquals(20, result.size()); assertEquals(3, result.get(0).length); } + @Test + public void test_ObjectQueryWithDefaultType() throws Exception { + createPaintingsDataSet(); + List<Object[]> result = SQLSelect.scalarQuery("SELECT PAINTING_ID FROM PAINTING") + .select(context); + assertEquals(20, result.size()); + assertTrue(result.get(0) instanceof Object[]); + assertTrue(result.get(0)[0] instanceof Integer); + } + @Test(expected = CayenneRuntimeException.class) public void test_ObjectArrayQueryException() throws Exception { createPaintingsDataSet(); @@ -148,6 +224,7 @@ public class SQLSelectIT extends ServerCase { Integer.class, String.class, LocalDateTime.class).select(context); assertEquals(2, results.size()); + assertTrue(results.get(0) instanceof Object[]); assertEquals(3, results.get(0).length); assertTrue(results.get(0)[2] instanceof LocalDateTime); } @@ -353,8 +430,7 @@ public class SQLSelectIT extends ServerCase { createPaintingsDataSet(); - int c = SQLSelect.scalarQuery(Integer.class, "SELECT #result('COUNT(*)' 'int') FROM PAINTING").selectOne( - context); + int c = SQLSelect.scalarQuery("SELECT COUNT(*) FROM PAINTING", Integer.class).selectOne(context); assertEquals(20, c); } http://git-wip-us.apache.org/repos/asf/cayenne/blob/18c72d34/cayenne-server/src/test/java/org/apache/cayenne/query/SQLTemplateIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SQLTemplateIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SQLTemplateIT.java index 9931f8c..da4fff4 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/SQLTemplateIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SQLTemplateIT.java @@ -25,8 +25,10 @@ import org.apache.cayenne.PersistenceState; import org.apache.cayenne.access.DataContext; import org.apache.cayenne.di.Inject; import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.map.SQLResult; import org.apache.cayenne.test.jdbc.DBHelper; import org.apache.cayenne.test.jdbc.TableHelper; +import org.apache.cayenne.testdo.testmap.Artist; import org.apache.cayenne.testdo.testmap.Gallery; import org.apache.cayenne.testdo.testmap.Painting; import org.apache.cayenne.unit.UnitDbAdapter; @@ -42,6 +44,7 @@ import java.sql.SQLException; import java.time.LocalDateTime; import java.util.List; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.*; @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) @@ -134,18 +137,232 @@ public class SQLTemplateIT extends ServerCase { } @Test(expected = CayenneRuntimeException.class) + public void testExceptionWhenUsingColumnsTypesAndSQLResult(){ + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate query = new SQLTemplate("SELECT ARTIST_ID P FROM ARTIST", true); + query.setResultColumnsTypes(Float.class); + SQLResult resultDescriptor = new SQLResult(); + resultDescriptor.addColumnResult("P"); + query.setResult(resultDescriptor); + context.performQuery(query); + } + + @Test(expected = CayenneRuntimeException.class) + public void testExceptionWhenUsingColumnsTypesAndSQLResultUsingScalar() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate query = new SQLTemplate(testDataMap, "SELECT ARTIST_ID, ARTIST_NAME P FROM ARTIST", false); + query.setResultColumnsTypes(Float.class, String.class); + query.setUseScalar(true); + SQLResult resultDescriptor = new SQLResult(); + resultDescriptor.addColumnResult("P"); + resultDescriptor.addColumnResult("N"); + query.setResult(resultDescriptor); + context.performQuery(query); + } + + @Test + public void testWithRootUsingScalar() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + String sql1 = "INSERT INTO ARTIST VALUES (16, 'Ivanov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q2 = new SQLTemplate(testDataMap, sql1, true); + context.performNonSelectingQuery(q2); + SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); + q3.setResultColumnsTypes(Double.class, String.class); + q3.setUseScalar(true); + List<Object[]> result = context.performQuery(q3); + assertEquals(2, result.size()); + assertTrue(result.get(0) instanceof Object[]); + assertTrue(result.get(0)[0] instanceof Double); + } + + @Test + public void testWithRootUsingDataRow() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + String sql1 = "INSERT INTO ARTIST VALUES (16, 'Ivanov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q2 = new SQLTemplate(testDataMap, sql1, true); + context.performNonSelectingQuery(q2); + SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); + q3.setResultColumnsTypes(Double.class, String.class); + q3.setFetchingDataRows(true); + q3.setColumnNamesCapitalization(CapsStrategy.UPPER); + List<DataRow> result = context.performQuery(q3); + assertEquals(2, result.size()); + assertTrue(result.get(0) instanceof DataRow); + assertThat(result.get(0).get("ARTIST_ID"), instanceOf(Double.class)); + } + + @Test(expected = CayenneRuntimeException.class) + public void testWithRootException() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); + q3.setResultColumnsTypes(Double.class, String.class); + context.performQuery(q3); + } + + @Test(expected = CayenneRuntimeException.class) + public void testUsingScalarAndDataRow() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); + q3.setUseScalar(true); + q3.setFetchingDataRows(true); + context.performQuery(q3); + } + + @Test + public void testDataRowWithTypes() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + String sql1 = "INSERT INTO ARTIST VALUES (16, 'Ivanov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q2 = new SQLTemplate(testDataMap, sql1, true); + context.performNonSelectingQuery(q2); + + SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST", true); + q3.setResultColumnsTypes(Double.class, String.class); + q3.setColumnNamesCapitalization(CapsStrategy.UPPER); + List<DataRow> artists = context.performQuery(q3); + assertEquals(2, artists.size()); + assertTrue(artists.get(0) instanceof DataRow); + assertThat(artists.get(0).get("ARTIST_ID"), instanceOf(Double.class)); + } + + @Test + public void testDataRowReturnAndDirectives() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + String sql1 = "INSERT INTO ARTIST VALUES (16, 'Ivanov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q2 = new SQLTemplate(testDataMap, sql1, true); + context.performNonSelectingQuery(q2); + + SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST", true); + List<DataRow> result = context.performQuery(q3); + assertEquals(2, result.size()); + assertTrue(result.get(0) instanceof DataRow); + assertEquals(2, result.get(0).size()); + assertTrue(result.get(0).get("ARTIST_ID") instanceof Long); + assertTrue(result.get(0).get("ARTIST_NAME") instanceof String); + } + + @Test(expected = CayenneRuntimeException.class) + public void testDataRowReturnAndDirectivesExc() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST", true); + q3.setResultColumnsTypes(Integer.class, String.class); + context.performQuery(q3); + } + + @Test + public void testObjectArrayReturnAndDirectives() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + String sql1 = "INSERT INTO ARTIST VALUES (16, 'Ivanov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q2 = new SQLTemplate(testDataMap, sql1, true); + context.performNonSelectingQuery(q2); + + SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST", false); + q3.setUseScalar(true); + List<Object[]> result = context.performQuery(q3); + assertEquals(2, result.size()); + assertTrue(result.get(0) instanceof Object[]); + assertEquals(2, result.get(0).length); + assertTrue(result.get(0)[0] instanceof Long); + assertTrue(result.get(0)[1] instanceof String); + } + + @Test(expected = CayenneRuntimeException.class) + public void testObjectArrayReturnAndDirectivesException() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + String sql1 = "INSERT INTO ARTIST VALUES (16, 'Ivanov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q2 = new SQLTemplate(testDataMap, sql1, true); + context.performNonSelectingQuery(q2); + + SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST", false); + q3.setResultColumnsTypes(Integer.class, String.class); + q3.setUseScalar(true); + context.performQuery(q3); + } + + @Test + public void testObjectArrayWithSingleObjectReturnAndDirectives() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + String sql1 = "INSERT INTO ARTIST VALUES (16, 'Ivanov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q2 = new SQLTemplate(testDataMap, sql1, true); + context.performNonSelectingQuery(q2); + + SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT #result('ARTIST_ID' 'java.lang.Long') FROM ARTIST", false); + q3.setUseScalar(true); + List<Object[]> result = context.performQuery(q3); + assertEquals(2, result.size()); + assertTrue(result.get(0) instanceof Object[]); + assertEquals(1, result.get(0).length); + assertTrue(result.get(0)[0] instanceof Long); + } + + @Test(expected = CayenneRuntimeException.class) public void testObjectArrayReturnWithException() { DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); context.performNonSelectingQuery(q1); - SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST", true) - .resultColumnsTypes(Integer.class); + SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST", false); + q3.setResultColumnsTypes(Integer.class); + q3.setUseScalar(true); context.performQuery(q3); } @Test - public void testObjectArrayReturn() throws SQLException { + public void testObjectArrayWithSingleObjectReturn() { + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); + String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; + String sql1 = "INSERT INTO ARTIST VALUES (16, 'Ivanov', null)"; + SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); + context.performNonSelectingQuery(q1); + SQLTemplate q2 = new SQLTemplate(testDataMap, sql1, true); + context.performNonSelectingQuery(q2); + + SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT ARTIST_ID FROM ARTIST", false); + q3.setUseScalar(true); + List<Object[]> artists = context.performQuery(q3); + assertEquals(2, artists.size()); + assertEquals(1, artists.get(0).length); + assertTrue(artists.get(0) instanceof Object[]); + assertTrue(artists.get(0)[0] instanceof Long); + } + + @Test + public void testObjectArrayReturnWithDefaultTypes() { DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); String sql = "INSERT INTO ARTIST VALUES (15, 'Surikov', null)"; String sql1 = "INSERT INTO ARTIST VALUES (16, 'Ivanov', null)"; @@ -154,20 +371,23 @@ public class SQLTemplateIT extends ServerCase { SQLTemplate q2 = new SQLTemplate(testDataMap, sql1, true); context.performNonSelectingQuery(q2); - SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST", true) - .resultColumnsTypes(Integer.class, String.class); + SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST", false); + q3.setUseScalar(true); List<Object[]> artists = context.performQuery(q3); assertEquals(2, artists.size()); assertEquals(2, artists.get(0).length); + assertTrue(artists.get(0) instanceof Object[]); + assertTrue(artists.get(0)[0] instanceof Long); } @Test - public void testObjectArrayReturnWithCustomType() throws SQLException { + public void testObjectArrayReturn() throws SQLException { DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); tArtistCt.insert(2, "Test1", new Date(System.currentTimeMillis())); - SQLTemplate q5 = new SQLTemplate(testDataMap, "SELECT * FROM ARTIST_CT", true) - .resultColumnsTypes(Integer.class, String.class, LocalDateTime.class); + SQLTemplate q5 = new SQLTemplate(testDataMap, "SELECT * FROM ARTIST_CT", false); + q5.setResultColumnsTypes(Float.class, String.class, LocalDateTime.class); + q5.setUseScalar(true); List dates = context.performQuery(q5); assertEquals(2, dates.size()); assertTrue(dates.get(0) instanceof Object[]); @@ -179,8 +399,9 @@ public class SQLTemplateIT extends ServerCase { public void testSingleObjectReturn() throws SQLException { DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); - SQLTemplate q5 = new SQLTemplate(testDataMap, "SELECT ARTIST_NAME FROM ARTIST_CT", true) - .resultColumnsTypes(String.class); + SQLTemplate q5 = new SQLTemplate(testDataMap, "SELECT ARTIST_NAME FROM ARTIST_CT", false); + q5.setResultColumnsTypes(String.class); + q5.setUseScalar(true); List dates = context.performQuery(q5); assertEquals(1, dates.size()); assertTrue(dates.get(0) instanceof String);