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 2026/03/10 13:55:26 UTC

(cayenne) branch master updated: CAY-2916 SelectById: unify logic for null/empty values

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 <st...@gmail.com>
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();