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


The following commit(s) were added to refs/heads/master by this push:
     new f4b6e0344 CAY-2916 SelectById: unify logic for null/empty values
f4b6e0344 is described below

commit f4b6e0344bf048fbc91dd44bb99b87aeb9dbd63e
Author: Nikita Timofeev <[email protected]>
AuthorDate: Tue Mar 10 17:55:16 2026 +0400

    CAY-2916 SelectById: unify logic for null/empty values
---
 RELEASE-NOTES.txt                                  |  1 +
 .../java/org/apache/cayenne/query/SelectById.java  | 77 ++++++++++++++++++----
 .../org/apache/cayenne/query/SelectByIdTest.java   | 44 ++++++++++++-
 .../org/apache/cayenne/query/SelectById_RunIT.java | 60 +++++++++++++++++
 4 files changed, 166 insertions(+), 16 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 72bfb1335..906257457 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -20,6 +20,7 @@ CAY-2893 Update velocity-engine-core dependency
 CAY-2897 Add no-op default implementations to the GraphChangeHandler interface
 CAY-2905 Upgrade Gradle to 8.14
 CAY-2908 Review and upgrade dependencies
+CAY-2916 SelectById: unify logic for null/empty values
 
 Bug Fixes:
 
diff --git a/cayenne/src/main/java/org/apache/cayenne/query/SelectById.java 
b/cayenne/src/main/java/org/apache/cayenne/query/SelectById.java
index ebffd4434..82cc38f5b 100644
--- a/cayenne/src/main/java/org/apache/cayenne/query/SelectById.java
+++ b/cayenne/src/main/java/org/apache/cayenne/query/SelectById.java
@@ -72,7 +72,7 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
         */
        public static <T> SelectById<T> queryMap(Class<T> entityType, 
Map<String, ?> id) {
                QueryRoot root = new ByEntityTypeResolver(entityType);
-               IdSpec idSpec = new SingleMapIdSpec(id);
+               IdSpec idSpec = new SingleMapIdSpec(checkIdMap(id));
                return new SelectById<>(root, idSpec);
        }
 
@@ -90,6 +90,9 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
         * @since 5.0
         */
        public static <T> SelectById<T> queryIds(Class<T> entityType, Object... 
ids) {
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
+               }
                QueryRoot root = new ByEntityTypeResolver(entityType);
                IdSpec idSpec = new MultiScalarIdSpec(Arrays.asList(ids));
                return new SelectById<>(root, idSpec);
@@ -99,6 +102,9 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
         * @since 5.0
         */
        public static <T> SelectById<T> queryIdsCollection(Class<T> entityType, 
Collection<Object> ids) {
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
+               }
                QueryRoot root = new ByEntityTypeResolver(entityType);
                IdSpec idSpec = new MultiScalarIdSpec(ids);
                return new SelectById<>(root, idSpec);
@@ -109,6 +115,14 @@ public class SelectById<T> extends IndirectQuery 
implements Select<T> {
         */
        @SafeVarargs
        public static <T> SelectById<T> queryMaps(Class<T> entityType, 
Map<String, ?>... ids) {
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
+               }
+
+               for(Map<String, ?> id : ids) {
+                       checkIdMap(id);
+               }
+
                QueryRoot root = new ByEntityTypeResolver(entityType);
                IdSpec idSpec = MultiMapIdSpec.ofMap(ids);
                return new SelectById<>(root, idSpec);
@@ -118,6 +132,11 @@ public class SelectById<T> extends IndirectQuery 
implements Select<T> {
         * @since 5.0
         */
        public static <T> SelectById<T> queryMapsCollection(Class<T> 
entityType, Collection<Map<String, ?>> ids) {
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
+               }
+
+               ids.forEach(SelectById::checkIdMap);
                QueryRoot root = new ByEntityTypeResolver(entityType);
                IdSpec idSpec = MultiMapIdSpec.ofMapCollection(ids);
                return new SelectById<>(root, idSpec);
@@ -127,8 +146,8 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
         * @since 5.0
         */
        public static <T> SelectById<T> queryObjectIds(Class<T> entityType, 
ObjectId... ids) {
-               if(ids == null || ids.length == 0) {
-                       throw new CayenneRuntimeException("Null or empty ids");
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
                }
                String entityName = ids[0].getEntityName();
                for(ObjectId id : ids) {
@@ -144,8 +163,8 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
         * @since 5.0
         */
        public static <T> SelectById<T> queryObjectIdsCollection(Class<T> 
entityType, Collection<ObjectId> ids) {
-               if(ids == null || ids.isEmpty()) {
-                       throw new CayenneRuntimeException("Null or empty ids");
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
                }
                String entityName = ids.iterator().next().getEntityName();
                for(ObjectId id : ids) {
@@ -253,7 +272,7 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
         */
        public static SelectById<DataRow> dataRowQueryMap(Class<?> entityType, 
Map<String, ?> id) {
                QueryRoot root = new ByEntityTypeResolver(entityType);
-               IdSpec idSpec = new SingleMapIdSpec(id);
+               IdSpec idSpec = new SingleMapIdSpec(checkIdMap(id));
                return new SelectById<>(root, idSpec, true);
        }
 
@@ -271,6 +290,9 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
         * @since 5.0
         */
        public static SelectById<DataRow> dataRowQueryIds(Class<?> entityType, 
Object... ids) {
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
+               }
                QueryRoot root = new ByEntityTypeResolver(entityType);
                IdSpec idSpec = new MultiScalarIdSpec(Arrays.asList(ids));
                return new SelectById<>(root, idSpec, true);
@@ -280,6 +302,9 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
         * @since 5.0
         */
        public static SelectById<DataRow> dataRowQueryIdsCollection(Class<?> 
entityType, Collection<Object> ids) {
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
+               }
                QueryRoot root = new ByEntityTypeResolver(entityType);
                IdSpec idSpec = new MultiScalarIdSpec(ids);
                return new SelectById<>(root, idSpec, true);
@@ -290,6 +315,14 @@ public class SelectById<T> extends IndirectQuery 
implements Select<T> {
         */
        @SafeVarargs
        public static SelectById<DataRow> dataRowQueryMaps(Class<?> entityType, 
Map<String, ?>... ids) {
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
+               }
+
+               for(Map<String, ?> id : ids) {
+                       checkIdMap(id);
+               }
+
                QueryRoot root = new ByEntityTypeResolver(entityType);
                IdSpec idSpec = MultiMapIdSpec.ofMap(ids);
                return new SelectById<>(root, idSpec, true);
@@ -299,6 +332,12 @@ public class SelectById<T> extends IndirectQuery 
implements Select<T> {
         * @since 5.0
         */
        public static SelectById<DataRow> dataRowQueryMapsCollection(Class<?> 
entityType, Collection<Map<String, ?>> ids) {
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
+               }
+
+               ids.forEach(SelectById::checkIdMap);
+
                QueryRoot root = new ByEntityTypeResolver(entityType);
                IdSpec idSpec = MultiMapIdSpec.ofMapCollection(ids);
                return new SelectById<>(root, idSpec, true);
@@ -308,8 +347,8 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
         * @since 5.0
         */
        public static SelectById<DataRow> dataRowQueryObjectIds(ObjectId... 
ids) {
-               if(ids == null || ids.length == 0) {
-                       throw new CayenneRuntimeException("Null or empty ids");
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
                }
                String entityName = ids[0].getEntityName();
                for(ObjectId id : ids) {
@@ -325,8 +364,8 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
         * @since 5.0
         */
        public static SelectById<DataRow> 
dataRowQueryObjectIdsCollection(Collection<ObjectId> ids) {
-               if(ids == null || ids.isEmpty()) {
-                       throw new CayenneRuntimeException("Null or empty ids");
+               if(ids == null) {
+                       throw new CayenneRuntimeException("Null ids");
                }
                String entityName = ids.iterator().next().getEntityName();
                for(ObjectId id : ids) {
@@ -603,6 +642,14 @@ public class SelectById<T> extends IndirectQuery 
implements Select<T> {
                }
        }
 
+       private static Map<String, ?> checkIdMap(Map<String, ?> id) {
+               if(id == null || id.isEmpty()) {
+                       throw new CayenneRuntimeException("Null or empty id 
map");
+               }
+
+               return id;
+       }
+
        @SafeVarargs
        private static <E, R> Collection<R> foldArguments(Function<E, R> 
mapper, E first, E... other) {
                List<R> result = new ArrayList<>(1 + other.length);
@@ -680,7 +727,8 @@ public class SelectById<T> extends IndirectQuery implements 
Select<T> {
 
                @Override
                public Expression getQualifier(ObjEntity entity) {
-                       return matchAllDbExp(id, Expression.EQUAL_TO);
+                       Expression expression = matchAllDbExp(id, 
Expression.EQUAL_TO);
+                       return expression == null ? expFalse() : expression;
                }
        }
 
@@ -722,10 +770,13 @@ public class SelectById<T> extends IndirectQuery 
implements Select<T> {
                public Expression getQualifier(ObjEntity entity) {
                        List<Expression> expressions = new ArrayList<>();
                        for(Map<String, ?> id : ids) {
-                               expressions.add(matchAllDbExp(id, 
Expression.EQUAL_TO));
+                               Expression expression = matchAllDbExp(id, 
Expression.EQUAL_TO);
+                               if(expression != null) {
+                                       expressions.add(expression);
+                               }
                        }
 
-                       return or(expressions);
+                       return expressions.isEmpty() ? expFalse() : 
or(expressions);
                }
        }
 
diff --git a/cayenne/src/test/java/org/apache/cayenne/query/SelectByIdTest.java 
b/cayenne/src/test/java/org/apache/cayenne/query/SelectByIdTest.java
index 503b3cf5a..977f9b0ab 100644
--- a/cayenne/src/test/java/org/apache/cayenne/query/SelectByIdTest.java
+++ b/cayenne/src/test/java/org/apache/cayenne/query/SelectByIdTest.java
@@ -20,6 +20,9 @@ package org.apache.cayenne.query;
 
 import static org.junit.Assert.assertNotNull;
 
+import java.util.Collections;
+
+import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.testdo.testmap.Artist;
 import org.junit.Test;
 
@@ -30,7 +33,7 @@ public class SelectByIdTest {
 
                PrefetchTreeNode root = PrefetchTreeNode.withPath("a.b", 
PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
 
-               SelectById<Artist> q = SelectById.query(Artist.class, 6);
+               SelectById<Artist> q = SelectById.queryId(Artist.class, 6);
                q.prefetch(root);
 
                PrefetchTreeNode prefetch = q.getPrefetches();
@@ -42,7 +45,7 @@ public class SelectByIdTest {
        @Test
        public void testPrefetch_Path() {
 
-               SelectById<Artist> q = SelectById.query(Artist.class, 7);
+               SelectById<Artist> q = SelectById.queryId(Artist.class, 7);
                q.prefetch("a.b", PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
                PrefetchTreeNode prefetch = q.getPrefetches();
 
@@ -62,7 +65,7 @@ public class SelectByIdTest {
 
                PrefetchTreeNode root = PrefetchTreeNode.withPath("a.b", 
PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
 
-               SelectById<Artist> q = SelectById.query(Artist.class, 8);
+               SelectById<Artist> q = SelectById.queryId(Artist.class, 8);
                q.prefetch(root);
 
                PrefetchTreeNode prefetch = q.getPrefetches();
@@ -77,4 +80,39 @@ public class SelectByIdTest {
                assertNotNull(prefetch.getNode("a.b"));
                assertNotNull(prefetch.getNode("a.b.c"));
        }
+
+       @Test
+       public void testQueryId_NullId() {
+               assertNotNull(SelectById.queryId(Artist.class, null));
+       }
+
+       @Test
+       public void testDataRowQueryId_NullId() {
+               assertNotNull(SelectById.dataRowQueryId(Artist.class, null));
+       }
+
+       @Test
+       public void testDataRowQueryIds_EmptyVarargs() {
+               assertNotNull(SelectById.dataRowQueryIds(Artist.class));
+       }
+
+       @Test
+       public void testDataRowQueryIdsCollection_EmptyCollection() {
+               
assertNotNull(SelectById.dataRowQueryIdsCollection(Artist.class, 
Collections.emptyList()));
+       }
+
+       @Test(expected = CayenneRuntimeException.class)
+       public void testDataRowQueryMap_EmptyMap() {
+               SelectById.dataRowQueryMap(Artist.class, 
Collections.emptyMap());
+       }
+
+       @Test
+       public void testDataRowQueryMaps_EmptyVarargs() {
+               assertNotNull(SelectById.dataRowQueryMaps(Artist.class));
+       }
+
+       @Test
+       public void testDataRowQueryMapsCollection_EmptyCollection() {
+               
assertNotNull(SelectById.dataRowQueryMapsCollection(Artist.class, 
Collections.emptyList()));
+       }
 }
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/query/SelectById_RunIT.java 
b/cayenne/src/test/java/org/apache/cayenne/query/SelectById_RunIT.java
index 05f234cce..adbd5c799 100644
--- a/cayenne/src/test/java/org/apache/cayenne/query/SelectById_RunIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/query/SelectById_RunIT.java
@@ -88,6 +88,66 @@ public class SelectById_RunIT extends RuntimeCase {
                assertEquals("artist2", a2.getArtistName());
        }
 
+       @Test
+       public void testNullPk() {
+               List<Artist> artists = SelectById.queryId(Artist.class, 
null).select(context);
+               assertEquals(0, artists.size());
+       }
+
+       @Test
+       public void testDataRowNullPk() {
+               List<DataRow> artists = SelectById.dataRowQueryId(Artist.class, 
null).select(context);
+               assertEquals(0, artists.size());
+       }
+
+       @Test
+       public void testEmptyPkMulti() {
+               List<Artist> artists = 
SelectById.queryIds(Artist.class).select(context);
+               assertEquals(0, artists.size());
+       }
+
+       @Test
+       public void testEmptyPkCollection() {
+               List<Artist> artists = 
SelectById.queryIdsCollection(Artist.class, 
Collections.emptyList()).select(context);
+               assertEquals(0, artists.size());
+       }
+
+       @Test
+       public void testEmptyMapPkMulti() {
+               List<Artist> artists = 
SelectById.queryMaps(Artist.class).select(context);
+               assertEquals(0, artists.size());
+       }
+
+       @Test
+       public void testEmptyMapPkCollection() {
+               List<Artist> artists = 
SelectById.queryMapsCollection(Artist.class, 
Collections.emptyList()).select(context);
+               assertEquals(0, artists.size());
+       }
+
+       @Test
+       public void testDataRowEmptyPkMulti() {
+               List<DataRow> artists = 
SelectById.dataRowQueryIds(Artist.class).select(context);
+               assertEquals(0, artists.size());
+       }
+
+       @Test
+       public void testDataRowEmptyPkCollection() {
+               List<DataRow> artists = 
SelectById.dataRowQueryIdsCollection(Artist.class, 
Collections.emptyList()).select(context);
+               assertEquals(0, artists.size());
+       }
+
+       @Test
+       public void testDataRowEmptyMapPkMulti() {
+               List<DataRow> artists = 
SelectById.dataRowQueryMaps(Artist.class).select(context);
+               assertEquals(0, artists.size());
+       }
+
+       @Test
+       public void testDataRowEmptyMapPkCollection() {
+               List<DataRow> artists = 
SelectById.dataRowQueryMapsCollection(Artist.class, 
Collections.emptyList()).select(context);
+               assertEquals(0, artists.size());
+       }
+
        @Test
        public void testIntPkMulti() throws Exception {
                createTwoArtists();

Reply via email to