From 18c72d34710646bed3c51a6b64285a597a56c2b6 Mon Sep 17 00:00:00 2001 From: Arseni Bulatski Date: Wed, 24 Oct 2018 09:26:36 +0300 Subject: [PATCH 1/3] CAY-2481 Methods to return Object[] after SQLTemplate and SQLExec perform --- .../access/jdbc/RowDescriptorBuilder.java | 2 +- .../access/jdbc/SQLTemplateAction.java | 26 +- .../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 +++++- .../apache/cayenne/query/SQLTemplateIT.java | 241 +++++++++++++++++- 8 files changed, 452 insertions(+), 35 deletions(-) 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 6c92c128ef..7ab51b7ec5 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 @@ protected ColumnDescriptor[] mergeResultSetAndPresetColumns() throws SQLExceptio 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; 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 cc6ac560ed..2f27eb0c28 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.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 @@ protected void processSelectResult(SQLStatement compiled, Connection connection, 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 @@ protected void doClose() { } } + 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 @@ protected RowDescriptorBuilder configureRowDescriptorBuilder(SQLStatement compil } 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 @@ protected RowDescriptorBuilder configureRowDescriptorBuilder(SQLStatement compil // 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 @@ protected RowDescriptorBuilder configureRowDescriptorBuilder(SQLStatement compil 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. 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 1b25684499..b0933dd66f 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 @@ /** * @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; } 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 3d46f6ad5e..c276a783d4 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 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 extends IndirectQuery implements Select { private static final long serialVersionUID = -7074293371883740872L; private List> resultColumnsTypes; + private boolean useScalar; + private boolean isFetchingDataRows; /** * Creates a query that selects DataRows and uses default routing. */ public static SQLSelect 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 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 dataRowQuery(String dataMapName, String sql, Class... types) { + return dataRowQuery(dataMapName, sql).resultColumnsTypes(types); } /** @@ -60,7 +81,7 @@ public static SQLSelect dataRowQuery(String sql) { public static SQLSelect dataRowQuery(String dataMapName, String sql) { SQLSelect query = new SQLSelect<>(sql); query.dataMapName = dataMapName; - return query; + return query.fetchingDataRows(); } /** @@ -84,7 +105,7 @@ public static SQLSelect scalarQuery(Class type, String sql) { public static SQLSelect scalarQuery(Class type, String dataMapName, String sql) { SQLSelect query = new SQLSelect<>(sql); query.dataMapName = dataMapName; - return query.resultColumnsTypes(type); + return query.resultColumnsTypes(type).useScalar(); } /** @@ -94,7 +115,39 @@ public static SQLSelect scalarQuery(Class type, String dataMapName, St */ public static SQLSelect scalarQuery(String sql, Class type) { SQLSelect 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 SQLSelect scalarQuery(String sql, String dataMapName, Class type) { + SQLSelect 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 scalarQuery(String sql) { + SQLSelect 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 scalarQuery(String sql, String dataMapName) { + SQLSelect query = new SQLSelect<>(sql); + query.dataMapName = dataMapName; + return query.useScalar(); } /** @@ -104,7 +157,7 @@ public static SQLSelect scalarQuery(String sql, Class type) { */ public static SQLSelect scalarQuery(String sql, Class firstType, Class... types) { SQLSelect query = new SQLSelect<>(sql); - return query.resultColumnsTypes(firstType).resultColumnsTypes(types); + return query.resultColumnsTypes(firstType).resultColumnsTypes(types).useScalar(); } /** @@ -116,7 +169,7 @@ public static SQLSelect scalarQuery(String sql, Class firstType, Cl public static SQLSelect scalarQuery(String sql, String dataMapName, Class firstType, Class... types) { SQLSelect 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 ResultBatchIterator batchIterator(ObjectContext context, int size) { } public boolean isFetchingDataRows() { - return persistentType == null; + return isFetchingDataRows; } public String getSql() { @@ -291,7 +344,7 @@ protected Query createReplacementQuery(EntityResolver resolver) { } SQLTemplate template = new SQLTemplate(); - template.setFetchingDataRows(isFetchingDataRows()); + template.setFetchingDataRows(isFetchingDataRows); template.setRoot(root); template.setDefaultTemplate(getSql()); template.setCacheGroup(cacheGroup); @@ -312,6 +365,7 @@ protected Query createReplacementQuery(EntityResolver resolver) { template.setFetchOffset(offset); template.setPageSize(pageSize); template.setStatementFetchSize(statementFetchSize); + template.setUseScalar(useScalar); return template; } @@ -534,4 +588,16 @@ public SQLSelect addPrefetch(PrefetchTreeNode node) { prefetches.merge(node); return this; } + + @SuppressWarnings("unchecked") + private SQLSelect fetchingDataRows() { + this.isFetchingDataRows = true; + return (SQLSelect) this; + } + + @SuppressWarnings("unchecked") + private SQLSelect useScalar() { + this.useScalar = true; + return (SQLSelect) this; + } } 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 fa863c3b1d..55a590d025 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> resultColumnsTypes; + private boolean useScalar; SQLTemplateMetadata metaData = new SQLTemplateMetadata(); @@ -108,12 +109,11 @@ public SQLTemplate(String defaultTemplate, boolean isFetchingDataRows) { 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 List> getResultColumnsTypes() { public void setResultColumnsTypes(List> 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; + } } 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 1537f8a010..3cd99a429e 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 @@ /** * @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 @@ boolean resolve(Object root, EntityResolver resolver, SQLTemplate query) { } 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(); 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 7774c8cf3d..cbc7c83abd 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.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 void test_DataRows_ClassRoot() throws Exception { } @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 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 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 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 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 result = SQLSelect.scalarQuery("SELECT PAINTING_ID, PAINTING_TITLE, ESTIMATED_PRICE FROM PAINTING", Integer.class, String.class, Double.class) + List 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 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 void testObjectArrayWithCustomType() throws SQLException { 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 void test_SelectCount() throws Exception { 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); } 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 9931f8cca6..da4fff4658 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.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.time.LocalDateTime; import java.util.List; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.*; @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) @@ -133,19 +136,233 @@ public void testSQLTemplateForDataMapWithInsertException() { gotRuntimeException); } + @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 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 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 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 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 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 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 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 void testObjectArrayReturn() throws SQLException { 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 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 void testObjectArrayReturnWithCustomType() throws SQLException { 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); From b1f9ad5b333442e6e51286fa1a3379f83919411c Mon Sep 17 00:00:00 2001 From: Arseni Bulatski Date: Wed, 24 Oct 2018 12:57:26 +0300 Subject: [PATCH 2/3] Update docs --- .../_cayenne-guide/part2/queries.adoc | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries.adoc index c93076f5e8..0230a54ea6 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries.adoc @@ -296,8 +296,9 @@ Artist artistWithId1 = SelectById.query(Artist.class, 1) `SQLSelect` and `SQLExec` are essentially a "fluent" versions of older `SQLTemplate` query. `SQLSelect` can be used (as name suggests) to select custom data in form of entities, -separate columns or collection of `DataRow`. `SQLExec` is designed to just execute any raw SQL code -(e.g. updates, deletes, DDLs, etc.) This queries support all directives described in <> section. +separate columns, collection of `DataRow` or Object[]. `SQLExec` is designed to just execute any raw SQL code +(e.g. updates, deletes, DDLs, etc.) This queries support all directives described in <> section. Also you can predefine +result type of columns. Here is example of how to use SQLSelect: @@ -317,6 +318,16 @@ List paintingNames = SQLSelect .scalarQuery(String.class, "SELECT PAINTING_TITLE FROM PAINTING WHERE ESTIMATED_PRICE > #bind($price)") .params("price", 100000) .select(context); + +// Selecting DataRow with predefined types +List result = SQLSelect + .dataRowQuery("SELECT * FROM ARTIST_CT", Integer.class, String.class, LocalDateTime.class) + .select(context); + +// Selecting Object[] with predefined types +List result = SQLSelect + .scalarQuery("SELECT * FROM ARTIST_CT", Integer.class, String.class, LocalDateTime.class) + .select(context); ---- @@ -873,6 +884,22 @@ query.setResult(resultDescriptor); List data = context.performQuery(query); ---- +You can fetch list of scalars, list of Object[] or list of DataRow with predefined result column types or using default types. +[source, Java] +---- +// Selecting Object[] with predefined types +SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); + q3.setResultColumnsTypes(Double.class, String.class); + q3.setUseScalar(true); +List result = context.performQuery(q3); + +// Selecting DataRow with predefined types +SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); + q3.setResultColumnsTypes(Double.class, String.class); + q3.setFetchingDataRows(true); +List result = context.performQuery(q3); +---- + Another trick related to mapping result sets is making Cayenne recognize prefetched entities in the result set. This emulates "joint" prefetching of `SelectQuery`, and is achieved by special column naming. Columns belonging to the "root" entity of the query should use unqualified names corresponding to the root `DbEntity` columns. From 5ae8ffd1f600a67edd35ff8800f9a53528984ec5 Mon Sep 17 00:00:00 2001 From: Arseni Bulatski Date: Wed, 31 Oct 2018 12:06:53 +0300 Subject: [PATCH 3/3] Tests cleanup --- .../org/apache/cayenne/query/SQLSelectIT.java | 55 +++--- .../apache/cayenne/query/SQLTemplateIT.java | 183 +++++++----------- 2 files changed, 88 insertions(+), 150 deletions(-) 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 cbc7c83abd..51dbc486bc 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 @@ -69,15 +69,19 @@ public void before() { tArtistCt.setColumns("ARTIST_ID", "ARTIST_NAME", "DATE_OF_BIRTH"); } - protected void createPaintingsDataSet() throws Exception { + private void createPaintingsDataSet() throws Exception { for (int i = 1; i <= 20; i++) { tPainting.insert(i, "painting" + i, 10000. * i); } } + private void createArtistDataSet() throws SQLException { + tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); + tArtistCt.insert(2, "Test1", new Date(System.currentTimeMillis())); + } + @Test public void test_DataRows_DataMapNameRoot() throws Exception { - createPaintingsDataSet(); SQLSelect q1 = SQLSelect.dataRowQuery("testmap", "SELECT * FROM PAINTING"); @@ -90,7 +94,6 @@ public void test_DataRows_DataMapNameRoot() throws Exception { @Test public void test_DataRows_DefaultRoot() throws Exception { - createPaintingsDataSet(); SQLSelect q1 = SQLSelect.dataRowQuery("SELECT * FROM PAINTING"); @@ -103,7 +106,6 @@ public void test_DataRows_DefaultRoot() throws Exception { @Test public void test_DataRows_ClassRoot() throws Exception { - createPaintingsDataSet(); SQLSelect q1 = SQLSelect.query(Painting.class, "SELECT * FROM PAINTING").columnNameCaps( @@ -116,8 +118,8 @@ public void test_DataRows_ClassRoot() throws Exception { @Test public void test_DataRowWithTypes() throws Exception { - tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); - tArtistCt.insert(2, "Test1", new Date(System.currentTimeMillis())); + createArtistDataSet(); + List result = SQLSelect.dataRowQuery("SELECT * FROM ARTIST_CT", Integer.class, String.class, LocalDateTime.class) .columnNameCaps(CapsStrategy.UPPER) .select(context); @@ -128,8 +130,8 @@ public void test_DataRowWithTypes() throws Exception { @Test public void test_DataRowWithDirectives() throws Exception { - tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); - tArtistCt.insert(2, "Test1", new Date(System.currentTimeMillis())); + createArtistDataSet(); + List 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()); @@ -139,15 +141,16 @@ public void test_DataRowWithDirectives() throws Exception { @Test(expected = CayenneRuntimeException.class) public void test_DataRowWithTypesException() throws Exception { - tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); + createArtistDataSet(); + 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())); + createArtistDataSet(); + List result = SQLSelect.scalarQuery("SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST_CT") .select(context); @@ -160,16 +163,16 @@ public void testObjectArrayWithDefaultTypesReturnAndDirectives() throws Exceptio @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())); + createArtistDataSet(); + 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())); + createArtistDataSet(); + List result = SQLSelect.scalarQuery("SELECT #result('ARTIST_ID' 'java.lang.Long') FROM ARTIST_CT") .select(context); @@ -182,6 +185,7 @@ public void testObjectArrayWithOneObjectDefaultTypesReturnAndDirectives() throws @Test public void test_ObjectArrayQueryWithDefaultTypes() throws Exception { createPaintingsDataSet(); + List result = SQLSelect.scalarQuery("SELECT PAINTING_ID, PAINTING_TITLE, ESTIMATED_PRICE FROM PAINTING") .select(context); @@ -192,6 +196,7 @@ public void test_ObjectArrayQueryWithDefaultTypes() throws Exception { @Test public void test_ObjectQueryWithDefaultType() throws Exception { createPaintingsDataSet(); + List result = SQLSelect.scalarQuery("SELECT PAINTING_ID FROM PAINTING") .select(context); assertEquals(20, result.size()); @@ -202,6 +207,7 @@ public void test_ObjectQueryWithDefaultType() throws Exception { @Test(expected = CayenneRuntimeException.class) public void test_ObjectArrayQueryException() throws Exception { createPaintingsDataSet(); + SQLSelect query = SQLSelect.scalarQuery("SELECT PAINTING_ID, PAINTING_TITLE, ESTIMATED_PRICE FROM PAINTING", Integer.class, String.class); context.performQuery(query); } @@ -209,6 +215,7 @@ public void test_ObjectArrayQueryException() throws Exception { @Test public void test_SingleObjectQuery() throws Exception { createPaintingsDataSet(); + List result = SQLSelect.scalarQuery("SELECT PAINTING_ID FROM PAINTING", Integer.class) .select(context); assertEquals(20, result.size()); @@ -217,8 +224,7 @@ public void test_SingleObjectQuery() throws Exception { @Test public void testObjectArrayWithCustomType() throws SQLException { - tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); - tArtistCt.insert(2, "Test1", new Date(System.currentTimeMillis())); + createArtistDataSet(); List results = SQLSelect.scalarQuery("SELECT * FROM ARTIST_CT", Integer.class, String.class, LocalDateTime.class).select(context); @@ -231,7 +237,6 @@ public void testObjectArrayWithCustomType() throws SQLException { @Test public void test_DataRows_ClassRoot_Parameters() throws Exception { - createPaintingsDataSet(); SQLSelect q1 = SQLSelect.query(Painting.class, @@ -245,7 +250,6 @@ public void test_DataRows_ClassRoot_Parameters() throws Exception { @Test public void test_DataRows_ClassRoot_Bind() throws Exception { - createPaintingsDataSet(); SQLSelect q1 = SQLSelect.query(Painting.class, @@ -259,7 +263,6 @@ public void test_DataRows_ClassRoot_Bind() throws Exception { @Test public void test_DataRows_ColumnNameCaps() throws Exception { - SQLSelect q1 = SQLSelect.dataRowQuery("SELECT * FROM PAINTING WHERE PAINTING_TITLE = 'painting2'"); q1.upperColumnNames(); @@ -273,7 +276,6 @@ public void test_DataRows_ColumnNameCaps() throws Exception { @Test public void test_DataRows_FetchLimit() throws Exception { - createPaintingsDataSet(); SQLSelect q1 = SQLSelect.dataRowQuery("SELECT * FROM PAINTING"); @@ -284,7 +286,6 @@ public void test_DataRows_FetchLimit() throws Exception { @Test public void test_DataRows_FetchOffset() throws Exception { - createPaintingsDataSet(); SQLSelect q1 = SQLSelect.dataRowQuery("SELECT * FROM PAINTING"); @@ -295,7 +296,6 @@ public void test_DataRows_FetchOffset() throws Exception { @Test public void test_Append() throws Exception { - createPaintingsDataSet(); SQLSelect q1 = SQLSelect.query(Painting.class, "SELECT * FROM PAINTING") @@ -308,7 +308,6 @@ public void test_Append() throws Exception { @Test public void test_Select() throws Exception { - createPaintingsDataSet(); List result = SQLSelect @@ -320,7 +319,6 @@ public void test_Select() throws Exception { @Test public void test_SelectOne() throws Exception { - createPaintingsDataSet(); Painting a = SQLSelect.query(Painting.class, "SELECT * FROM PAINTING WHERE PAINTING_TITLE = #bind($a)") @@ -403,7 +401,6 @@ public void test_BatchIterator() throws Exception { @Test public void test_SelectLong() throws Exception { - createPaintingsDataSet(); long id = SQLSelect @@ -415,7 +412,6 @@ public void test_SelectLong() throws Exception { @Test public void test_SelectLongArray() throws Exception { - createPaintingsDataSet(); List ids = SQLSelect.scalarQuery(Integer.class, @@ -427,7 +423,6 @@ public void test_SelectLongArray() throws Exception { @Test public void test_SelectCount() throws Exception { - createPaintingsDataSet(); int c = SQLSelect.scalarQuery("SELECT COUNT(*) FROM PAINTING", Integer.class).selectOne(context); @@ -437,7 +432,6 @@ public void test_SelectCount() throws Exception { @Test public void test_ParamsArray_Single() throws Exception { - createPaintingsDataSet(); Integer id = SQLSelect @@ -449,7 +443,6 @@ public void test_ParamsArray_Single() throws Exception { @Test public void test_ParamsArray_Multiple() throws Exception { - createPaintingsDataSet(); List ids = SQLSelect @@ -465,7 +458,6 @@ public void test_ParamsArray_Multiple() throws Exception { @Ignore("This is supported by Velocity only") // TODO: move this test to new cayenne-velocity module public void test_ParamsArray_Multiple_OptionalChunks() throws Exception { - tPainting.insert(1, "painting1", 1.0); tPainting.insert(2, "painting2", null); @@ -485,7 +477,6 @@ public void test_ParamsArray_Multiple_OptionalChunks() throws Exception { @Ignore("This is supported by Velocity only") // TODO: move this test to new cayenne-velocity module public void test_Params_Multiple_OptionalChunks() throws Exception { - tPainting.insert(1, "painting1", 1.0); tPainting.insert(2, "painting2", null); 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 da4fff4658..09a425a4b8 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 @@ -71,7 +71,7 @@ public class SQLTemplateIT extends ServerCase { @Before public void setUp() throws Exception { tArtist = new TableHelper(dbHelper, "ARTIST"); - tArtist.setColumns("ARTIST_ID", "ARTIST_NAME"); + tArtist.setColumns("ARTIST_ID", "ARTIST_NAME", "DATE_OF_BIRTH"); tPainting = new TableHelper(dbHelper, "PAINTING"); tPainting.setColumns("PAINTING_ID", "ARTIST_ID", "PAINTING_TITLE", "ESTIMATED_PRICE"); @@ -80,6 +80,11 @@ public void setUp() throws Exception { tArtistCt.setColumns("ARTIST_ID", "ARTIST_NAME", "DATE_OF_BIRTH"); } + private void createArtistDataSet() throws SQLException { + tArtist.insert(15, "Surikov", new Date(System.currentTimeMillis())); + tArtist.insert(16, "Ivanov", new Date(System.currentTimeMillis())); + } + @Test public void testSQLTemplateForDataMap() { DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); @@ -89,15 +94,13 @@ public void testSQLTemplateForDataMap() { } @Test - public void testSQLTemplateForDataMapWithInsert() { - 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); + public void testSQLTemplateForDataMapWithInsert() throws SQLException { + createArtistDataSet(); + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); SQLTemplate q2 = new SQLTemplate(testDataMap, "SELECT * FROM ARTIST", true); List result = context.performQuery(q2); - assertEquals(1, result.size()); + assertEquals(2, result.size()); } @Test @@ -119,12 +122,10 @@ public void testReturnGeneratedKeys() { } @Test - public void testSQLTemplateForDataMapWithInsertException() { - 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); + public void testSQLTemplateForDataMapWithInsertException() throws SQLException { + createArtistDataSet(); + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); SQLTemplate q2 = new SQLTemplate(testDataMap, "SELECT * FROM ARTIST", false); boolean gotRuntimeException = false; try { @@ -137,11 +138,9 @@ public void testSQLTemplateForDataMapWithInsertException() { } @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); + public void testExceptionWhenUsingColumnsTypesAndSQLResult() throws SQLException { + createArtistDataSet(); + SQLTemplate query = new SQLTemplate("SELECT ARTIST_ID P FROM ARTIST", true); query.setResultColumnsTypes(Float.class); SQLResult resultDescriptor = new SQLResult(); @@ -151,11 +150,10 @@ public void testExceptionWhenUsingColumnsTypesAndSQLResult(){ } @Test(expected = CayenneRuntimeException.class) - public void testExceptionWhenUsingColumnsTypesAndSQLResultUsingScalar() { + public void testExceptionWhenUsingColumnsTypesAndSQLResultUsingScalar() throws SQLException { + createArtistDataSet(); + 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); @@ -167,14 +165,9 @@ public void testExceptionWhenUsingColumnsTypesAndSQLResultUsingScalar() { } @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); + public void testWithRootUsingScalar() throws SQLException { + createArtistDataSet(); + SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); q3.setResultColumnsTypes(Double.class, String.class); q3.setUseScalar(true); @@ -185,14 +178,9 @@ public void testWithRootUsingScalar() { } @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); + public void testWithRootUsingDataRow() throws SQLException { + createArtistDataSet(); + SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); q3.setResultColumnsTypes(Double.class, String.class); q3.setFetchingDataRows(true); @@ -204,22 +192,18 @@ public void testWithRootUsingDataRow() { } @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); + public void testWithRootException() throws SQLException { + createArtistDataSet(); + 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); + public void testUsingScalarAndDataRow() throws SQLException { + createArtistDataSet(); + SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); q3.setUseScalar(true); q3.setFetchingDataRows(true); @@ -227,15 +211,10 @@ public void testUsingScalarAndDataRow() { } @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); + public void testDataRowWithTypes() throws SQLException { + createArtistDataSet(); + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST", true); q3.setResultColumnsTypes(Double.class, String.class); q3.setColumnNamesCapitalization(CapsStrategy.UPPER); @@ -246,15 +225,10 @@ public void testDataRowWithTypes() { } @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); + public void testDataRowReturnAndDirectives() throws SQLException { + createArtistDataSet(); + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST", true); List result = context.performQuery(q3); assertEquals(2, result.size()); @@ -265,26 +239,20 @@ public void testDataRowReturnAndDirectives() { } @Test(expected = CayenneRuntimeException.class) - public void testDataRowReturnAndDirectivesExc() { + public void testDataRowReturnAndDirectivesExc() throws SQLException { + createArtistDataSet(); + 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); + public void testObjectArrayReturnAndDirectives() throws SQLException { + createArtistDataSet(); + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); 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 result = context.performQuery(q3); @@ -296,15 +264,10 @@ public void testObjectArrayReturnAndDirectives() { } @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); + public void testObjectArrayReturnAndDirectivesException() throws SQLException { + createArtistDataSet(); + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); 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); @@ -312,15 +275,10 @@ public void testObjectArrayReturnAndDirectivesException() { } @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); + public void testObjectArrayWithSingleObjectReturnAndDirectives() throws SQLException { + createArtistDataSet(); + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT #result('ARTIST_ID' 'java.lang.Long') FROM ARTIST", false); q3.setUseScalar(true); List result = context.performQuery(q3); @@ -331,11 +289,10 @@ public void testObjectArrayWithSingleObjectReturnAndDirectives() { } @Test(expected = CayenneRuntimeException.class) - public void testObjectArrayReturnWithException() { + public void testObjectArrayReturnWithException() throws SQLException { + createArtistDataSet(); + 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", false); q3.setResultColumnsTypes(Integer.class); q3.setUseScalar(true); @@ -343,15 +300,10 @@ public void testObjectArrayReturnWithException() { } @Test - 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); + public void testObjectArrayWithSingleObjectReturn() throws SQLException { + createArtistDataSet(); + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT ARTIST_ID FROM ARTIST", false); q3.setUseScalar(true); List artists = context.performQuery(q3); @@ -362,15 +314,10 @@ public void testObjectArrayWithSingleObjectReturn() { } @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)"; - SQLTemplate q1 = new SQLTemplate(testDataMap, sql, true); - context.performNonSelectingQuery(q1); - SQLTemplate q2 = new SQLTemplate(testDataMap, sql1, true); - context.performNonSelectingQuery(q2); + public void testObjectArrayReturnWithDefaultTypes() throws SQLException { + createArtistDataSet(); + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); SQLTemplate q3 = new SQLTemplate(testDataMap, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST", false); q3.setUseScalar(true); List artists = context.performQuery(q3); @@ -382,10 +329,10 @@ public void testObjectArrayReturnWithDefaultTypes() { @Test public void testObjectArrayReturn() throws SQLException { + createArtistDataSet(); + 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", false); + SQLTemplate q5 = new SQLTemplate(testDataMap, "SELECT * FROM ARTIST", false); q5.setResultColumnsTypes(Float.class, String.class, LocalDateTime.class); q5.setUseScalar(true); List dates = context.performQuery(q5); @@ -397,8 +344,9 @@ public void testObjectArrayReturn() throws SQLException { @Test public void testSingleObjectReturn() throws SQLException { - DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); tArtistCt.insert(1, "Test", new Date(System.currentTimeMillis())); + + DataMap testDataMap = context.getEntityResolver().getDataMap("testmap"); SQLTemplate q5 = new SQLTemplate(testDataMap, "SELECT ARTIST_NAME FROM ARTIST_CT", false); q5.setResultColumnsTypes(String.class); q5.setUseScalar(true); @@ -471,7 +419,6 @@ public void testSQLTemplate_PositionalParams_ToManyParams() throws SQLException public void testSQLTemplateSelectNullObjects() throws Exception { tPainting.insert(1, null, "p1", 10); - String sql = "SELECT p.GALLERY_ID FROM PAINTING p"; SQLTemplate q1 = new SQLTemplate(Gallery.class, sql); q1.setColumnNamesCapitalization(CapsStrategy.UPPER); @@ -495,8 +442,8 @@ public void testSQLTemplateSelectInvalid() throws Exception { @Test public void testSQLTemplateWithDisjointByIdPrefetch() throws Exception { - tArtist.insert(1, "artist1"); - tArtist.insert(2, "artist2"); + tArtist.insert(1, "artist1", null); + tArtist.insert(2, "artist2", null); tPainting.insert(1, 1, "p1", 10); tPainting.insert(2, 2, "p2", 20);