You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2019/08/26 12:50:54 UTC

[cayenne] branch master updated (b40c722 -> 68eb7b1)

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

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


    from b40c722  CAY-2610 Align methods in ObjectSelect and SQLSelect
     new a405db9  CAY-2527 API to map Object[] result to POJO
     new a8c2c79  CAY-2527 API to map Object[] result to POJO   - simple Object[] to Pojo mapper
     new d3856d2  CAY-2527 API to map Object[] result to POJO   - combine multiple mappers
     new 6ed1a34  Merge PR #401
     new 68eb7b1  Merge PR #401

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../cayenne/access/DataDomainQueryAction.java      | 60 +++++++++++---
 .../org/apache/cayenne/query/ColumnSelect.java     | 14 ++++
 .../apache/cayenne/query/ColumnSelectMetadata.java | 16 ++++
 .../org/apache/cayenne/query/QueryMetadata.java    |  8 ++
 .../java/org/apache/cayenne/query/SQLSelect.java   |  9 +++
 .../java/org/apache/cayenne/query/SQLTemplate.java |  7 ++
 .../apache/cayenne/query/SQLTemplateMetadata.java  | 15 ++++
 .../org/apache/cayenne/reflect/PojoMapper.java     | 93 ++++++++++++++++++++++
 .../org/apache/cayenne/query/ColumnSelectIT.java   | 62 +++++++++++++++
 .../java/org/apache/cayenne/query/SQLSelectIT.java | 78 +++++++++++++++---
 .../org/apache/cayenne/reflect/PojoMapperTest.java | 88 ++++++++++++++++++++
 11 files changed, 428 insertions(+), 22 deletions(-)
 create mode 100644 cayenne-server/src/main/java/org/apache/cayenne/reflect/PojoMapper.java
 create mode 100644 cayenne-server/src/test/java/org/apache/cayenne/reflect/PojoMapperTest.java


[cayenne] 02/05: CAY-2527 API to map Object[] result to POJO - simple Object[] to Pojo mapper

Posted by nt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a8c2c79a01a72fcb56dafbbbd7f4844df4185480
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Mon Aug 5 17:34:21 2019 +0300

    CAY-2527 API to map Object[] result to POJO
      - simple Object[] to Pojo mapper
---
 .../org/apache/cayenne/reflect/PojoMapper.java     | 93 ++++++++++++++++++++++
 .../org/apache/cayenne/reflect/PojoMapperTest.java | 88 ++++++++++++++++++++
 2 files changed, 181 insertions(+)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PojoMapper.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PojoMapper.java
new file mode 100644
index 0000000..c0abf6e
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PojoMapper.java
@@ -0,0 +1,93 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Field;
+import java.util.function.Function;
+
+import org.apache.cayenne.CayenneRuntimeException;
+
+/**
+ * Simple mapper of Object[] to POJO class. This class relies on field order, so use with caution.
+ * @param <T> type of object to produce
+ * @since 4.2
+ */
+public class PojoMapper<T> implements Function<Object[], T> {
+
+    private static MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+    private final Class<T> type;
+    private final MethodHandle constructor;
+    private final MethodHandle[] setters;
+
+    public PojoMapper(Class<T> type) {
+        this.type = type;
+        try {
+            this.constructor = lookup.unreflectConstructor(type.getConstructor());
+        } catch (NoSuchMethodException | IllegalAccessException ex) {
+            throw new CayenneRuntimeException("No default constructor found for class '%s'.", type.getName());
+        }
+
+        Field[] declaredFields = type.getDeclaredFields();
+        this.setters = new MethodHandle[declaredFields.length];
+        int i = 0;
+        for(Field field : declaredFields) {
+            field.setAccessible(true);
+            try {
+                setters[i++] = lookup.unreflectSetter(field);
+            } catch (IllegalAccessException e) {
+                throw new CayenneRuntimeException("Field '%s'.'%s' is inaccessible.", e, type.getName(), field.getName());
+            }
+        }
+    }
+
+    private T newObject() {
+        try {
+            @SuppressWarnings("unchecked")
+            T object = (T)constructor.invoke();
+            return object;
+        } catch (Throwable ex) {
+            throw new CayenneRuntimeException("Unable to instantiate %s.", ex, type.getName());
+        }
+    }
+
+    public T apply(Object[] data) {
+        if(data.length > setters.length) {
+            throw new CayenneRuntimeException("Unable to create '%s'. Values length (%d) > fields count (%d)"
+                    , type.getName(), data.length, setters.length);
+        }
+
+        T object = newObject();
+
+        for (int i = 0; i < data.length; i++) {
+            if (data[i] != null) {
+                try {
+                    setters[i].invoke(object, data[i]);
+                } catch (Throwable ex) {
+                    throw new CayenneRuntimeException("Unable to set field of %s.", ex, type.getName());
+                }
+            }
+        }
+
+        return object;
+    }
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/reflect/PojoMapperTest.java b/cayenne-server/src/test/java/org/apache/cayenne/reflect/PojoMapperTest.java
new file mode 100644
index 0000000..a593a42
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/reflect/PojoMapperTest.java
@@ -0,0 +1,88 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.reflect;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 4.2
+ */
+public class PojoMapperTest {
+
+    @Test
+    public void testObjectCreation() {
+        PojoMapper<C1> descriptor = new PojoMapper<>(C1.class);
+
+        Object o = new Object();
+        Object[] data = {"123", o, 42};
+        C1 object = descriptor.apply(data);
+        assertEquals("123", object.a);
+        assertSame(o, object.b);
+        assertEquals(42, object.c);
+    }
+
+    @Test(expected = CayenneRuntimeException.class)
+    public void testNonPublicClass() {
+        new PojoMapper<>(C2.class);
+    }
+
+    @Test(expected = CayenneRuntimeException.class)
+    public void testNonPublicConstructor() {
+        new PojoMapper<>(C3.class);
+    }
+
+    @Test(expected = CayenneRuntimeException.class)
+    public void testNonDefaultConstructor() {
+        new PojoMapper<>(C4.class);
+    }
+
+    @Test(expected = CayenneRuntimeException.class)
+    public void testWrongArgumentCount() {
+        PojoMapper<C1> descriptor = new PojoMapper<>(C1.class);
+
+        Object[] data = {"123", new Object(), 42, 32};
+        descriptor.apply(data);
+    }
+
+    public static class C1 {
+        String a;
+        Object b;
+        int c;
+    }
+
+    private static class C2 {
+        int a;
+    }
+
+    public static class C3 {
+        int a;
+        private C3() {
+        }
+    }
+
+    public static class C4 {
+        int a;
+        public C4(int a) {
+        }
+    }
+}
\ No newline at end of file


[cayenne] 01/05: CAY-2527 API to map Object[] result to POJO

Posted by nt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a405db945b3cb81d0ecd0f6a445b69d2041cc944
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Mon Aug 5 17:34:02 2019 +0300

    CAY-2527 API to map Object[] result to POJO
---
 .../cayenne/access/DataDomainQueryAction.java      | 60 ++++++++++++++---
 .../org/apache/cayenne/query/ColumnSelect.java     | 14 ++++
 .../apache/cayenne/query/ColumnSelectMetadata.java | 11 +++
 .../org/apache/cayenne/query/QueryMetadata.java    |  8 +++
 .../java/org/apache/cayenne/query/SQLSelect.java   |  9 +++
 .../java/org/apache/cayenne/query/SQLTemplate.java |  7 ++
 .../apache/cayenne/query/SQLTemplateMetadata.java  | 11 +++
 .../org/apache/cayenne/query/ColumnSelectIT.java   | 36 ++++++++++
 .../java/org/apache/cayenne/query/SQLSelectIT.java | 78 +++++++++++++++++++---
 9 files changed, 212 insertions(+), 22 deletions(-)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
index 1128532..dba1041 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
@@ -59,6 +59,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 
 /**
  * Performs query routing and execution. During execution phase intercepts
@@ -466,29 +467,38 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
     @SuppressWarnings("unchecked")
     private void interceptObjectConversion() {
 
-        if (context != null && !metadata.isFetchingDataRows()) {
-
+        if (context != null) {
             List mainRows = response.firstList(); // List<DataRow> or List<Object[]>
             if (mainRows != null && !mainRows.isEmpty()) {
 
                 ObjectConversionStrategy<?> converter;
 
-                List<Object> rsMapping = metadata.getResultSetMapping();
-                if (rsMapping == null) {
-                    converter = new SingleObjectConversionStrategy();
+                if(metadata.isFetchingDataRows()) {
+                    converter = new IdentityConversionStrategy();
                 } else {
-                    if (metadata.isSingleResultSetMapping()) {
-                        if (rsMapping.get(0) instanceof EntityResultSegment) {
-                            converter = new SingleObjectConversionStrategy();
+                    List<Object> rsMapping = metadata.getResultSetMapping();
+                    if (rsMapping == null) {
+                        converter = new SingleObjectConversionStrategy();
+                    } else {
+                        if (metadata.isSingleResultSetMapping()) {
+                            if (rsMapping.get(0) instanceof EntityResultSegment) {
+                                converter = new SingleObjectConversionStrategy();
+                            } else {
+                                converter = new SingleScalarConversionStrategy();
+                            }
                         } else {
-                            converter = new SingleScalarConversionStrategy();
+                            converter = new MixedConversionStrategy();
                         }
-                    } else {
-                        converter = new MixedConversionStrategy();
                     }
                 }
 
+                if(metadata.getResultMapper() != null) {
+                    converter = new MapperConversionStrategy(converter);
+                }
+
                 converter.convert(mainRows);
+                // rewind response after firstList() call
+                response.reset();
             }
         }
     }
@@ -775,4 +785,32 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
             }
         }
     }
+
+    private class IdentityConversionStrategy extends ObjectConversionStrategy<Object> {
+        @Override
+        void convert(List<Object> mainRows) {
+        }
+    }
+
+    /**
+     * Conversion strategy that uses mapper function to map raw result
+     */
+    private class MapperConversionStrategy extends ObjectConversionStrategy<Object> {
+
+        private final Function<Object, ?> mapper;
+        private final ObjectConversionStrategy<Object> parentStrategy;
+
+        @SuppressWarnings("unchecked")
+        MapperConversionStrategy(ObjectConversionStrategy<?> parentStrategy) {
+            this.mapper = (Function)metadata.getResultMapper();
+            this.parentStrategy = (ObjectConversionStrategy)parentStrategy;
+        }
+
+        @Override
+        void convert(List<Object> mainRows) {
+            parentStrategy.convert(mainRows);
+            mainRows.replaceAll(mapper::apply);
+        }
+    }
+
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
index b82c8c8..8e3bca4 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.function.Function;
 
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.exp.property.BaseProperty;
@@ -630,4 +631,17 @@ public class ColumnSelect<T> extends FluentSelect<T> {
     protected BaseQueryMetadata getBaseMetaData() {
         return metaData;
     }
+
+    /**
+     * Wrap result to given class.  Wrapper class should be public and have public constructor with no args.
+     * Columns order in the query should corespond to fields defined in that class.
+     *
+     * @param mapper function that maps result to required form.
+     * @since 4.2
+     */
+    @SuppressWarnings("unchecked")
+    public <E> ColumnSelect<E> map(Function<T, E> mapper) {
+        this.metaData.setResultMapper(mapper);
+        return (ColumnSelect<E>)this;
+    }
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelectMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelectMetadata.java
index 5f5b153..2ab406e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelectMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelectMetadata.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
+import java.util.function.Function;
 
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.property.BaseProperty;
@@ -44,6 +45,7 @@ class ColumnSelectMetadata extends ObjectSelectMetadata {
 
 	private boolean isSingleResultSetMapping;
 	private boolean suppressingDistinct;
+	private Function<?, ?> resultMapper;
 
 	boolean resolve(Object root, EntityResolver resolver, ColumnSelect<?> query) {
 
@@ -133,4 +135,13 @@ class ColumnSelectMetadata extends ObjectSelectMetadata {
 	public void setSuppressingDistinct(boolean suppressingDistinct) {
 		this.suppressingDistinct = suppressingDistinct;
 	}
+
+	void setResultMapper(Function<?, ?> resultMapper) {
+		this.resultMapper = resultMapper;
+	}
+
+	@Override
+	public Function<?, ?> getResultMapper() {
+		return resultMapper;
+	}
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java
index 396cac1..2797f91 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.query;
 
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbEntity;
@@ -256,4 +257,11 @@ public interface QueryMetadata {
      * @since 4.0
      */
     boolean isSuppressingDistinct();
+
+    /**
+     * @since 4.2
+     */
+    default Function<?, ?> getResultMapper() {
+        return null;
+    }
 }
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 b2f426f..e3f6fe8 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
@@ -24,6 +24,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataRow;
@@ -46,6 +47,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> {
 	private List<Class<?>> resultColumnsTypes;
 	private boolean useScalar;
 	private boolean isFetchingDataRows;
+	private Function<T, ?> resultMapper;
 
 	/**
 	 * Creates a query that selects DataRows and uses default routing.
@@ -443,6 +445,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> {
 		template.setStatementFetchSize(statementFetchSize);
 		template.setQueryTimeout(queryTimeout);
 		template.setUseScalar(useScalar);
+		template.setResultMapper(resultMapper);
 
 		return template;
 	}
@@ -684,6 +687,12 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> {
 	}
 
 	@SuppressWarnings("unchecked")
+	public <E> SQLSelect<E> map(Function<T, E> mapper) {
+		this.resultMapper = mapper;
+		return (SQLSelect<E>)this;
+	}
+
+	@SuppressWarnings("unchecked")
 	private <E> SQLSelect<E> fetchingDataRows() {
 		this.isFetchingDataRows = true;
 		return (SQLSelect<E>) 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 b2d5e9a..19b1968 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
@@ -700,4 +700,11 @@ public class SQLTemplate extends AbstractQuery implements ParameterizedQuery {
 	public boolean isUseScalar() {
 		return useScalar;
 	}
+
+	/**
+	 * @since 4.2
+	 */
+    public void setResultMapper(Function<?,?> resultMapper) {
+		this.metaData.setResultMapper(resultMapper);
+    }
 }
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 4fe03c9..6bd92ed 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
@@ -27,6 +27,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 
 /**
  * @since 3.0
@@ -34,6 +35,7 @@ import java.util.Map;
 public class SQLTemplateMetadata extends BaseQueryMetadata {
 
 	private boolean isSingleResultSetMapping;
+	private Function<?, ?> resultMapper;
 
 	@Override
 	public boolean isSingleResultSetMapping() {
@@ -125,4 +127,13 @@ public class SQLTemplateMetadata extends BaseQueryMetadata {
 		}
 		query.setResult(result);
 	}
+
+	void setResultMapper(Function<?,?> resultMapper) {
+		this.resultMapper = resultMapper;
+    }
+
+	@Override
+	public Function<?, ?> getResultMapper() {
+		return resultMapper;
+	}
 }
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
index 3f31925..6ec41fb 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
@@ -41,6 +41,7 @@ import org.apache.cayenne.exp.property.EntityProperty;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
+import org.apache.cayenne.reflect.PojoMapper;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.test.jdbc.TableHelper;
 import org.apache.cayenne.testdo.testmap.Artist;
@@ -1152,4 +1153,39 @@ public class ColumnSelectIT extends ServerCase {
         assertEquals("artist1", ((Artist)results.get(0)[1]).getArtistName());
         assertEquals(1, results.get(0)[2]);
     }
+
+    @Test
+    public void testMapToPojo() {
+        List<TestPojo> result = ObjectSelect.query(Artist.class)
+                .columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.ARTIST_NAME.trim().length())
+                .where(Artist.ARTIST_NAME.like("artist%"))
+                .orderBy(Artist.ARTIST_ID_PK_PROPERTY.asc())
+                .map(TestPojo::new)
+                .select(context);
+
+        assertEquals(20, result.size());
+
+        TestPojo testPojo0 = result.get(0);
+        assertNotNull(testPojo0);
+        assertEquals("artist1", testPojo0.name);
+        assertNotNull(testPojo0.date);
+        assertEquals(7, testPojo0.length);
+
+        TestPojo testPojo19 = result.get(19);
+        assertNotNull(testPojo19);
+        assertEquals("artist20", testPojo19.name);
+        assertEquals(8, testPojo19.length);
+        assertNotNull(testPojo19.date);
+    }
+
+    static class TestPojo {
+        String name;
+        Date date;
+        int length;
+        TestPojo(Object[] data) {
+            name = (String)data[0];
+            date = (Date)data[1];
+            length = (Integer)data[2];
+        }
+    }
 }
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 4185bb7..7d6f276 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
@@ -128,6 +128,18 @@ public class SQLSelectIT extends ServerCase {
 	}
 
 	@Test
+	public void test_DataRowWithTypesMapped() throws Exception {
+		createArtistDataSet();
+
+		List<Object> result = SQLSelect.dataRowQuery("SELECT * FROM ARTIST_CT", Integer.class, String.class, LocalDateTime.class)
+				.columnNameCaps(CapsStrategy.UPPER)
+				.map(dataRow -> dataRow.get("ARTIST_ID"))
+				.select(context);
+		assertEquals(2, result.size());
+		assertTrue(result.get(0) instanceof Integer);
+	}
+
+	@Test
 	public void test_DataRowWithDirectives() throws Exception {
 		createArtistDataSet();
 
@@ -160,6 +172,20 @@ public class SQLSelectIT extends ServerCase {
 		assertTrue(result.get(0)[1] instanceof String);
 	}
 
+	@Test
+	public void testObjectArrayWithDefaultTypesReturnAndDirectivesMappedToPojo() throws Exception {
+		createArtistDataSet();
+
+		List<ArtistDataWrapper> result = SQLSelect
+				.arrayQuery("SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST_CT")
+				.map(ArtistDataWrapper::new)
+				.select(context);
+
+		assertEquals(2, result.size());
+		assertTrue(result.get(0).id > 0);
+		assertNotNull(result.get(0).name);
+	}
+
 	@Test(expected = CayenneRuntimeException.class)
 	public void testObjectArrayReturnAndDirectives() throws Exception {
 		createArtistDataSet();
@@ -235,6 +261,21 @@ public class SQLSelectIT extends ServerCase {
 	}
 
 	@Test
+	public void testObjectArrayWithCustomTypeMappedToPojo() throws SQLException {
+		createArtistDataSet();
+
+		List<ArtistDataWrapper> result = SQLSelect.scalarQuery("SELECT * FROM ARTIST_CT",
+				Integer.class, String.class, LocalDateTime.class)
+				.map(ArtistDataWrapper::new)
+				.select(context);
+
+		assertEquals(2, result.size());
+		assertTrue(result.get(0).id > 0);
+		assertNotNull(result.get(0).name);
+		assertNotNull(result.get(0).date);
+	}
+
+	@Test
 	public void test_DataRows_ClassRoot_Parameters() throws Exception {
 		createPaintingsDataSet();
 
@@ -261,7 +302,7 @@ public class SQLSelectIT extends ServerCase {
 	}
 
 	@Test
-	public void test_DataRows_ColumnNameCaps() throws Exception {
+	public void test_DataRows_ColumnNameCaps() {
 		SQLSelect<DataRow> q1 = SQLSelect.dataRowQuery("SELECT * FROM PAINTING WHERE PAINTING_TITLE = 'painting2'");
 		q1.upperColumnNames();
 
@@ -311,7 +352,9 @@ public class SQLSelectIT extends ServerCase {
 
 		List<Painting> result = SQLSelect
 				.query(Painting.class, "SELECT * FROM PAINTING WHERE PAINTING_TITLE = #bind($a)")
-				.param("a", "painting3").columnNameCaps(CapsStrategy.UPPER).select(context);
+				.param("a", "painting3")
+				.columnNameCaps(CapsStrategy.UPPER)
+				.select(context);
 
 		assertEquals(1, result.size());
 	}
@@ -367,7 +410,7 @@ public class SQLSelectIT extends ServerCase {
 		createPaintingsDataSet();
 
 		try (ResultIterator<Painting> it = SQLSelect.query(Painting.class, "SELECT * FROM PAINTING")
-				.columnNameCaps(CapsStrategy.UPPER).iterator(context);) {
+				.columnNameCaps(CapsStrategy.UPPER).iterator(context)) {
 			int count = 0;
 
 			for (Painting p : it) {
@@ -383,7 +426,7 @@ public class SQLSelectIT extends ServerCase {
 		createPaintingsDataSet();
 
 		try (ResultBatchIterator<Painting> it = SQLSelect.query(Painting.class, "SELECT * FROM PAINTING")
-				.columnNameCaps(CapsStrategy.UPPER).batchIterator(context, 5);) {
+				.columnNameCaps(CapsStrategy.UPPER).batchIterator(context, 5)) {
 			int count = 0;
 
 			for (List<Painting> paintingList : it) {
@@ -404,7 +447,7 @@ public class SQLSelectIT extends ServerCase {
 						Integer.class)
 				.param("a", "painting3").selectOne(context);
 
-		assertEquals(3l, id);
+		assertEquals(3L, id);
 	}
 
 	@Test
@@ -415,7 +458,7 @@ public class SQLSelectIT extends ServerCase {
 				Integer.class).select(context);
 
 		assertEquals(20, ids.size());
-		assertEquals(2l, ids.get(1).intValue());
+		assertEquals(2L, ids.get(1).intValue());
 	}
 
 	@Test
@@ -436,7 +479,7 @@ public class SQLSelectIT extends ServerCase {
 						Integer.class)
 				.paramsArray("painting3").selectOne(context);
 
-		assertEquals(3l, id.intValue());
+		assertEquals(3L, id.intValue());
 	}
 
 	@Test
@@ -449,8 +492,8 @@ public class SQLSelectIT extends ServerCase {
 						Integer.class)
 				.paramsArray("painting3", "painting2").select(context);
 
-		assertEquals(2l, ids.get(0).intValue());
-		assertEquals(3l, ids.get(1).intValue());
+		assertEquals(2L, ids.get(0).intValue());
+		assertEquals(3L, ids.get(1).intValue());
 	}
 
 	@Test
@@ -469,7 +512,7 @@ public class SQLSelectIT extends ServerCase {
 				.paramsArray(null, "painting1").select(context);
 
 		assertEquals(1, ids.size());
-		assertEquals(1l, ids.get(0).longValue());
+		assertEquals(1L, ids.get(0).longValue());
 	}
 
 	@Test
@@ -492,6 +535,19 @@ public class SQLSelectIT extends ServerCase {
 				.params(params).select(context);
 
 		assertEquals(1, ids.size());
-		assertEquals(1l, ids.get(0).longValue());
+		assertEquals(1L, ids.get(0).longValue());
+	}
+
+	static class ArtistDataWrapper {
+		long id;
+		String name;
+		LocalDateTime date;
+		ArtistDataWrapper(Object[] data) {
+			id = ((Number)data[0]).longValue();
+			name = (String)data[1];
+			if(data.length > 2) {
+				date = (LocalDateTime)data[2];
+			}
+		}
 	}
 }


[cayenne] 04/05: Merge PR #401

Posted by nt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 6ed1a34f58a4ebe0ee2dc3655e4b65e061f0dd46
Merge: b40c722 d3856d2
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Mon Aug 26 15:43:22 2019 +0300

    Merge PR #401

 .../cayenne/access/DataDomainQueryAction.java      | 60 +++++++++++---
 .../org/apache/cayenne/query/ColumnSelect.java     | 14 ++++
 .../apache/cayenne/query/ColumnSelectMetadata.java | 16 ++++
 .../org/apache/cayenne/query/QueryMetadata.java    |  8 ++
 .../java/org/apache/cayenne/query/SQLSelect.java   |  9 +++
 .../java/org/apache/cayenne/query/SQLTemplate.java |  7 ++
 .../apache/cayenne/query/SQLTemplateMetadata.java  | 15 ++++
 .../org/apache/cayenne/reflect/PojoMapper.java     | 93 ++++++++++++++++++++++
 .../org/apache/cayenne/query/ColumnSelectIT.java   | 62 +++++++++++++++
 .../java/org/apache/cayenne/query/SQLSelectIT.java | 78 +++++++++++++++---
 .../org/apache/cayenne/reflect/PojoMapperTest.java | 88 ++++++++++++++++++++
 11 files changed, 428 insertions(+), 22 deletions(-)



[cayenne] 03/05: CAY-2527 API to map Object[] result to POJO - combine multiple mappers

Posted by nt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit d3856d212d7c050c4185ede7ad0e7add56923fd8
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Wed Aug 7 17:04:23 2019 +0300

    CAY-2527 API to map Object[] result to POJO
      - combine multiple mappers
---
 .../apache/cayenne/query/ColumnSelectMetadata.java |  7 +++++-
 .../apache/cayenne/query/SQLTemplateMetadata.java  |  6 ++++-
 .../org/apache/cayenne/query/ColumnSelectIT.java   | 26 ++++++++++++++++++++++
 3 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelectMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelectMetadata.java
index 2ab406e..1222346 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelectMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelectMetadata.java
@@ -136,8 +136,13 @@ class ColumnSelectMetadata extends ObjectSelectMetadata {
 		this.suppressingDistinct = suppressingDistinct;
 	}
 
+	@SuppressWarnings("unchecked")
 	void setResultMapper(Function<?, ?> resultMapper) {
-		this.resultMapper = resultMapper;
+		if(this.resultMapper != null) {
+			this.resultMapper = this.resultMapper.andThen((Function)resultMapper);
+		} else {
+			this.resultMapper = resultMapper;
+		}
 	}
 
 	@Override
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 6bd92ed..99910ca 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
@@ -129,7 +129,11 @@ public class SQLTemplateMetadata extends BaseQueryMetadata {
 	}
 
 	void setResultMapper(Function<?,?> resultMapper) {
-		this.resultMapper = resultMapper;
+		if(this.resultMapper != null) {
+			this.resultMapper = this.resultMapper.andThen((Function)resultMapper);
+		} else {
+			this.resultMapper = resultMapper;
+		}
     }
 
 	@Override
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
index 6ec41fb..5220e7b 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
@@ -1178,6 +1178,25 @@ public class ColumnSelectIT extends ServerCase {
         assertNotNull(testPojo19.date);
     }
 
+    @Test
+    public void testDoubleMapToPojo() {
+        List<TestPojo2> result = ObjectSelect.query(Artist.class)
+                .columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.ARTIST_NAME.trim().length())
+                .where(Artist.ARTIST_NAME.like("artist%"))
+                .orderBy(Artist.ARTIST_ID_PK_PROPERTY.asc())
+                .map(TestPojo::new)
+                .map(TestPojo2::new)
+                .select(context);
+        assertEquals(20, result.size());
+
+        TestPojo2 testPojo0 = result.get(0);
+        assertNotNull(testPojo0);
+        assertEquals("artist1", testPojo0.pojo.name);
+        assertNotNull(testPojo0.pojo.date);
+        assertEquals(7, testPojo0.pojo.length);
+
+    }
+
     static class TestPojo {
         String name;
         Date date;
@@ -1188,4 +1207,11 @@ public class ColumnSelectIT extends ServerCase {
             length = (Integer)data[2];
         }
     }
+
+    static class TestPojo2 {
+        TestPojo pojo;
+        TestPojo2(TestPojo pojo) {
+            this.pojo = pojo;
+        }
+    }
 }


[cayenne] 05/05: Merge PR #401

Posted by nt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 68eb7b1f2a2b7ce6917bd46f008cd1f4a9e93929
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Mon Aug 26 15:45:11 2019 +0300

    Merge PR #401
---
 cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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 5e4c5a2..57d4845 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
@@ -177,7 +177,7 @@ public class SQLSelectIT extends ServerCase {
 		createArtistDataSet();
 
 		List<ArtistDataWrapper> result = SQLSelect
-				.arrayQuery("SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST_CT")
+				.columnQuery("SELECT #result('ARTIST_ID' 'java.lang.Long'), #result('ARTIST_NAME' 'java.lang.String') FROM ARTIST_CT")
 				.map(ArtistDataWrapper::new)
 				.select(context);