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();