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 2017/08/31 09:28:22 UTC

[1/3] cayenne git commit: CAY-2357 Generic select queries silently convert result to nulls

Repository: cayenne
Updated Branches:
  refs/heads/master e44f2fab5 -> 66e2727e8


CAY-2357 Generic select queries silently convert result to nulls


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/8452d41d
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/8452d41d
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/8452d41d

Branch: refs/heads/master
Commit: 8452d41def3796502d297456043d97822665e3b3
Parents: e44f2fa
Author: Nikita Timofeev <st...@gmail.com>
Authored: Thu Aug 31 12:19:33 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Thu Aug 31 12:19:33 2017 +0300

----------------------------------------------------------------------
 .../apache/cayenne/access/ObjectResolver.java   |  9 +++++-
 .../ejbql/EJBQLOrderByTranslator.java           |  5 ++++
 .../org/apache/cayenne/query/EJBQLQueryIT.java  | 30 ++++++++++++++++++++
 .../org/apache/cayenne/query/SQLTemplateIT.java | 29 +++++++++++++++++++
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |  1 +
 5 files changed, 73 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/8452d41d/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectResolver.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectResolver.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectResolver.java
index efcac6e..af5949b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectResolver.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectResolver.java
@@ -113,7 +113,8 @@ class ObjectResolver {
 
 		List<Persistent> results = new ArrayList<>(rows.size());
 		for (DataRow row : rows) {
-			// nulls are possible here since 3.0 for soem varieties of EJBQL
+			// nulls are possible here since 3.0 for some varieties of EJBQL,
+			// simple example of this: "select p.toGallery+ from Painting p" where toGallery is null.
 			results.add(objectFromDataRow(row));
 		}
 
@@ -212,6 +213,9 @@ class ObjectResolver {
 
 			// this is possible when processing left outer joint prefetches
 			if (val == null) {
+				if(!dataRow.containsKey(key)) {
+					throw new CayenneRuntimeException("No PK column '%s' found in data row.", key);
+				}
 				return null;
 			}
 
@@ -230,6 +234,9 @@ class ObjectResolver {
 
 			// this is possible when processing left outer joint prefetches
 			if (val == null) {
+				if(!dataRow.containsKey(key)) {
+					throw new CayenneRuntimeException("No PK column '%s' found in data row.", key);
+				}
 				return null;
 			}
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8452d41d/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ejbql/EJBQLOrderByTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ejbql/EJBQLOrderByTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ejbql/EJBQLOrderByTranslator.java
index 41ca358..6b004f9 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ejbql/EJBQLOrderByTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ejbql/EJBQLOrderByTranslator.java
@@ -69,4 +69,9 @@ class EJBQLOrderByTranslator extends EJBQLBaseVisitor {
         expression.visit(childVisitor);
         return false;
     }
+
+    @Override
+    public boolean visitDbPath(EJBQLExpression expression, int finishedChildIndex) {
+        return super.visitDbPath(expression, finishedChildIndex);
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8452d41d/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
index d2ae8f8..2e148ef 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
@@ -615,4 +615,34 @@ public class EJBQLQueryIT extends ServerCase {
         assertEquals(1L, artistAsc.get(1)[1]);
         assertEquals(0L, artistAsc.get(2)[1]);
     }
+
+	@Test
+	public void testNullObjects() throws Exception {
+        tArtist.insert(1, "a1");
+        tArtist.insert(2, "a2");
+        tArtist.insert(3, "a3");
+
+        tPainting.insert(1, 2, "title1");
+        tPainting.insert(2, 1, "title2");
+        tPainting.insert(3, 1, "title3");
+
+        EJBQLQuery queryFullProduct = new EJBQLQuery("select a, p from Artist a, Painting p");
+        List<Object[]> result1 = context.performQuery(queryFullProduct);
+        assertEquals(9, result1.size());
+        for(Object[] next : result1) {
+            assertEquals(2, next.length);
+            assertNotNull(next[0]);
+            assertNotNull(next[1]);
+        }
+
+        EJBQLQuery queryToOneRel = new EJBQLQuery("select p.toGallery+, p.toArtist+, p from Painting p");
+        List<Object[]> result2 = context.performQuery(queryToOneRel);
+        assertEquals(3, result2.size());
+        for(Object[] next : result2) {
+            assertNull(next[0]); // Gallery
+            assertTrue(next[1] instanceof Artist);
+            assertTrue(next[2] instanceof Painting);
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8452d41d/cayenne-server/src/test/java/org/apache/cayenne/query/SQLTemplateIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SQLTemplateIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SQLTemplateIT.java
index 902ff04..020364a 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
@@ -26,6 +26,7 @@ import org.apache.cayenne.di.Inject;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.testdo.testmap.Gallery;
 import org.apache.cayenne.testdo.testmap.Painting;
 import org.apache.cayenne.unit.di.server.CayenneProjects;
 import org.apache.cayenne.unit.di.server.ServerCase;
@@ -33,11 +34,13 @@ import org.apache.cayenne.unit.di.server.UseServerRuntime;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.math.BigInteger;
 import java.sql.SQLException;
 import java.sql.Types;
 import java.util.List;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -155,4 +158,30 @@ public class SQLTemplateIT extends ServerCase {
 			// expected
 		}
 	}
+
+	@Test
+	public void testSQLTemplateSelectNullObjects() throws Exception {
+		tPainting.insert(1, null, "p1", BigInteger.valueOf(10L));
+
+
+		String sql = "SELECT p.GALLERY_ID FROM PAINTING p";
+		SQLTemplate q1 = new SQLTemplate(Gallery.class, sql);
+		q1.setColumnNamesCapitalization(CapsStrategy.UPPER);
+		List<Gallery> galleries = context.performQuery(q1);
+
+		assertEquals(1, galleries.size());
+		assertNull(galleries.get(0));
+	}
+
+	@Test(expected = CayenneRuntimeException.class)
+	public void testSQLTemplateSelectInvalid() throws Exception {
+		tPainting.insert(1, null, "p1", BigInteger.valueOf(10L));
+
+		String sql = "SELECT p.PAINTING_TITLE FROM PAINTING p";
+		SQLTemplate q1 = new SQLTemplate(Gallery.class, sql);
+		q1.setColumnNamesCapitalization(CapsStrategy.UPPER);
+
+		// this should fail as result can't be converted to Gallery class
+		context.performQuery(q1);
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8452d41d/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 06b5ffb..77e14a3 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -37,6 +37,7 @@ CAY-2350 Expression: NotIn with empty collection returns empty result
 CAY-2353 Broken paginated column select with only one entity in the result
 CAY-2354 DbGenerator.runGenerator must commit its connection
 CAY-2356 EJBQL: Incorrect COUNT() on outer joined table
+CAY-2357 Generic select queries silently convert result to nulls if no PK column found
 
 ----------------------------------
 Release: 4.0.B1


[2/3] cayenne git commit: CAY-2358 NPE when callbacks invoked on null objects

Posted by nt...@apache.org.
CAY-2358 NPE when callbacks invoked on null objects


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/65ebda99
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/65ebda99
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/65ebda99

Branch: refs/heads/master
Commit: 65ebda996b5dff7b9887a45fc1d5c5759192f8e3
Parents: 8452d41
Author: Nikita Timofeev <st...@gmail.com>
Authored: Thu Aug 31 12:20:49 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Thu Aug 31 12:20:49 2017 +0300

----------------------------------------------------------------------
 .../reflect/LifecycleCallbackEventHandler.java  | 16 +++++------
 .../org/apache/cayenne/query/EJBQLQueryIT.java  | 29 ++++++++++++++++++++
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |  1 +
 3 files changed, 37 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/65ebda99/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
index db596bb..fd044a6 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
@@ -20,7 +20,6 @@ package org.apache.cayenne.reflect;
 
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.map.ObjEntity;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -111,14 +110,8 @@ class LifecycleCallbackEventHandler {
      * Registers a callback object to be invoked when a lifecycle event occurs.
      */
     private void addCallback(Class<?> entityClass, AbstractCallback callback) {
-        Collection<AbstractCallback> entityListeners = listeners.get(entityClass
-                .getName());
-
-        if (entityListeners == null) {
-            entityListeners = new ArrayList<>(3);
-            listeners.put(entityClass.getName(), entityListeners);
-        }
-
+        Collection<AbstractCallback> entityListeners = listeners
+                .computeIfAbsent(entityClass.getName(), k -> new ArrayList<>(3));
         entityListeners.add(callback);
     }
 
@@ -126,6 +119,11 @@ class LifecycleCallbackEventHandler {
      * Invokes callbacks for a given entity object.
      */
     void performCallbacks(Persistent object) {
+        if(object == null) {
+            // this can happen if object resolved to null from some query with outer join
+            // (e.g. in EJBQL or SQLTemplate)
+            return;
+        }
 
         // default listeners are invoked first
         if (!defaultListeners.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/65ebda99/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
index 2e148ef..a760f3c 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
@@ -28,6 +28,8 @@ import org.apache.cayenne.ejbql.EJBQLException;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.map.EJBQLQueryDescriptor;
 import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.LifecycleEvent;
+import org.apache.cayenne.reflect.LifecycleCallbackRegistry;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.test.jdbc.TableHelper;
 import org.apache.cayenne.testdo.testmap.Artist;
@@ -645,4 +647,31 @@ public class EJBQLQueryIT extends ServerCase {
         }
     }
 
+    @Test
+    public void testNullObjectsCallback() throws Exception {
+        tArtist.insert(1, "a1");
+        tArtist.insert(2, "a2");
+        tArtist.insert(3, "a3");
+
+        tPainting.insert(1, 2, "title1");
+        tPainting.insert(2, 1, "title2");
+        tPainting.insert(3, 1, "title3");
+
+        // set callback to be called
+        LifecycleCallbackRegistry registry = runtime
+                .getDataDomain()
+                .getEntityResolver()
+                .getCallbackRegistry();
+        registry.addCallback(LifecycleEvent.POST_LOAD, Painting.class, "postAddCallback");
+
+        // select Paintings, where one of it will be null
+        EJBQLQuery queryFullProduct = new EJBQLQuery("select a.paintingArray+ from Artist a order by a.artistName");
+        List<Painting> result1 = context.performQuery(queryFullProduct);
+        assertEquals(4, result1.size());
+        assertNull(result1.get(3));
+        for(int i=0; i<3; i++) {
+            assertNotNull(result1.get(i));
+            assertTrue(result1.get(i).isPostAdded());
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/65ebda99/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 77e14a3..d80050a 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -38,6 +38,7 @@ CAY-2353 Broken paginated column select with only one entity in the result
 CAY-2354 DbGenerator.runGenerator must commit its connection
 CAY-2356 EJBQL: Incorrect COUNT() on outer joined table
 CAY-2357 Generic select queries silently convert result to nulls if no PK column found
+CAY-2358 NPE when callbacks invoked on null objects
 
 ----------------------------------
 Release: 4.0.B1


[3/3] cayenne git commit: Change superclass to BaseDataObject

Posted by nt...@apache.org.
Change superclass to BaseDataObject


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/66e2727e
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/66e2727e
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/66e2727e

Branch: refs/heads/master
Commit: 66e2727e8b6c3481518d6fe7450530924d566ade
Parents: 65ebda9
Author: Nikita Timofeev <st...@gmail.com>
Authored: Thu Aug 31 12:21:16 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Thu Aug 31 12:21:16 2017 +0300

----------------------------------------------------------------------
 .../java/org/apache/cayenne/testdo/testmap/ArtDataObject.java    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/66e2727e/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/ArtDataObject.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/ArtDataObject.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/ArtDataObject.java
index 0923d7c..2658ecc 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/ArtDataObject.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/ArtDataObject.java
@@ -18,13 +18,13 @@
  ****************************************************************/
 package org.apache.cayenne.testdo.testmap;
 
-import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.BaseDataObject;
 
 /**
  * This class exists solely to enable testing of arbitrary superclasses for
  * ObjEntities. It has no functionality at this stage (nor is any likely)
  */
-public class ArtDataObject extends CayenneDataObject {
+public class ArtDataObject extends BaseDataObject {
 
     private static final long serialVersionUID = -1350981440925237535L;