This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit da205371d57d3259a1399b4354cc8ac1c038d6ba Author: Andrus Adamchik <[email protected]> AuthorDate: Sun Aug 30 16:57:40 2026 -0400 CAY-3007 Deprecate SelectById --- RELEASE-NOTES.txt | 1 + .../cayenne/commitlog/DeletedDiffProcessor.java | 1 - .../org/apache/cayenne/exp/parser/ASTDbPath.java | 7 +- .../cayenne/exp/property/EntityProperty.java | 98 ++++++ .../java/org/apache/cayenne/map/ObjEntity.java | 11 + .../java/org/apache/cayenne/query/SelectById.java | 27 ++ .../org/apache/cayenne/CDOMapRelationshipIT.java | 5 +- .../org/apache/cayenne/CDOSetRelationshipIT.java | 5 +- .../java/org/apache/cayenne/MeaningfulFKIT.java | 5 +- .../access/DataContextFlattenedAttributesIT.java | 7 +- .../apache/cayenne/access/EntityInheritanceIT.java | 5 +- .../cayenne/access/VerticalInheritanceIT.java | 39 +-- .../translator/select/QualifierTranslatorIT.java | 4 - .../cayenne/commitlog/CommitLogFilterIT.java | 36 ++- .../commitlog/CommitLogFilter_All_FlattenedIT.java | 8 +- .../commitlog/CommitLogFilter_FilteredIT.java | 29 +- .../CommitLogFilter_ListenerInducedChangesIT.java | 8 +- .../cayenne/exp/property/EntityPropertyIdIT.java | 161 ++++++++++ .../apache/cayenne/query/ObjectSelect_ByIdIT.java | 356 +++++++++++++++++++++ .../cayenne/query/SelectByIdIteratedQueryIT.java | 1 + .../org/apache/cayenne/query/SelectByIdTest.java | 1 + .../org/apache/cayenne/query/SelectById_RunIT.java | 1 + .../org/apache/cayenne/value/json/JsonTypeIT.java | 8 +- 23 files changed, 750 insertions(+), 74 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index d383ab82c..1c473c4f6 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -16,6 +16,7 @@ Changes/New Features: CAY-2998 Modeler: clickabe "Target" column to navigate relationships CAY-3000 Modeler: unifying the main views of all model objects CAY-3004 Modeler: Rework DbEntity PK generation UI to match the XML model +CAY-3007 Deprecate SelectById Bug Fixes: diff --git a/cayenne/src/main/java/org/apache/cayenne/commitlog/DeletedDiffProcessor.java b/cayenne/src/main/java/org/apache/cayenne/commitlog/DeletedDiffProcessor.java index edd5f329b..4ffa7ddcb 100644 --- a/cayenne/src/main/java/org/apache/cayenne/commitlog/DeletedDiffProcessor.java +++ b/cayenne/src/main/java/org/apache/cayenne/commitlog/DeletedDiffProcessor.java @@ -56,7 +56,6 @@ class DeletedDiffProcessor implements GraphChangeHandler { final MutableObjectChange objectChangeSet = changeSet.getOrCreate(id, ObjectChangeType.DELETE); - // TODO: rewrite with SelectById query after Cayenne upgrade ObjectIdQuery query = new ObjectIdQuery(id, true, ObjectIdQuery.CACHE); QueryResponse result = channel.onQuery(null, query); diff --git a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java index 553314621..c397648e5 100644 --- a/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java +++ b/cayenne/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java @@ -30,6 +30,7 @@ import org.apache.cayenne.ObjectContext; import org.apache.cayenne.ObjectId; import org.apache.cayenne.Persistent; import org.apache.cayenne.access.DataContext; +import org.apache.cayenne.DataRow; import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.exp.path.CayennePath; @@ -38,7 +39,6 @@ import org.apache.cayenne.map.DbRelationship; import org.apache.cayenne.map.Entity; import org.apache.cayenne.map.ObjEntity; import org.apache.cayenne.query.ObjectSelect; -import org.apache.cayenne.query.SelectById; import org.apache.cayenne.util.CayenneMapEntry; /** @@ -149,7 +149,10 @@ public class ASTDbPath extends ASTPath { } if (oid != null) { - return SelectById.dataRowQuery(persistent.getObjectId()).selectOne(context); + return ObjectSelect.query(DataRow.class, oid.getEntityName()) + .fetchDataRows() + .where(ExpressionFactory.matchExp(ExpressionFactory.fullObjectExp(), oid)) + .selectOne(context); } // fallback to ID snapshot as a last resort diff --git a/cayenne/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java b/cayenne/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java index 59dba8ca2..8465d84a8 100644 --- a/cayenne/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java +++ b/cayenne/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java @@ -19,8 +19,14 @@ package org.apache.cayenne.exp.property; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.List; +import java.util.Map; +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.ObjectId; import org.apache.cayenne.Persistent; import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.ExpressionFactory; @@ -143,6 +149,98 @@ public class EntityProperty<E extends Persistent> extends BaseProperty<E> implem return ExpressionFactory.notInExp(getExpression(), ids); } + + /** + * Matches an object by a possibly compound id, expressed as a map of DB attribute names to values. + * Unlike {@link #eqId(Object)} this form supports entities with a compound PK. + * + * @param id a map of the target entity PK attribute names to values + * @return an expression matching an object with the given id + * @since 5.0 + */ + public Expression eqIdMap(Map<String, ?> id) { + return idExp(id); + } + + /** + * @param id a map of the target entity PK attribute names to values + * @return an expression excluding an object with the given id + * @since 5.0 + * @see #eqIdMap(Map) + */ + public Expression neqIdMap(Map<String, ?> id) { + return idExp(id).notExp(); + } + + /** + * Matches any of the objects identified by the provided ids. Unlike {@link #idsIn(Object...)} this form + * supports entities with a compound PK, expanding to an {@code OR} of per-id matches rather than an + * {@code IN}, as SQL {@code IN} can't be applied to a multi-column key. + * + * @param ids maps of the target entity PK attribute names to values + * @since 5.0 + * @see #eqIdMap(Map) + */ + @SafeVarargs + public final Expression idMapsIn(Map<String, ?>... ids) { + return idMapsInCollection(Arrays.asList(ids)); + } + + /** + * @param ids maps of the target entity PK attribute names to values + * @since 5.0 + * @see #idMapsIn(Map[]) + */ + public Expression idMapsInCollection(Collection<Map<String, ?>> ids) { + List<Expression> expressions = new ArrayList<>(ids.size()); + for (Map<String, ?> id : ids) { + expressions.add(idExp(id)); + } + return expressions.isEmpty() ? ExpressionFactory.expFalse() : ExpressionFactory.joinExp(Expression.OR, expressions); + } + + /** + * Matches any of the objects identified by the provided {@link ObjectId}s. Unlike + * {@link #idsIn(Object...)} this form supports entities with a compound PK. + * + * @since 5.0 + * @see #idMapsIn(Map[]) + */ + public Expression objectIdsIn(ObjectId... ids) { + return objectIdsInCollection(Arrays.asList(ids)); + } + + /** + * @since 5.0 + * @see #objectIdsIn(ObjectId...) + */ + public Expression objectIdsInCollection(Collection<ObjectId> ids) { + List<Map<String, ?>> snapshots = new ArrayList<>(ids.size()); + for (ObjectId id : ids) { + snapshots.add(id.getIdSnapshot()); + } + return idMapsInCollection(snapshots); + } + + /** + * Builds an {@code AND} of per-PK-attribute matches. Paths are built as "dbid:" paths relative to this + * property, so they resolve through the Obj layer and work for both the query root (an empty path, see + * {@link SelfProperty}) and a to-one relationship. + */ + private Expression idExp(Map<String, ?> id) { + if (id == null || id.isEmpty()) { + throw new CayenneRuntimeException("Null or empty id map"); + } + + Expression result = null; + for (Map.Entry<String, ?> entry : id.entrySet()) { + Expression next = ExpressionFactory + .matchExp(ExpressionFactory.dbIdPathExp(getPath().dot(entry.getKey())), entry.getValue()); + result = result == null ? next : result.andExp(next); + } + return result; + } + /** * {@inheritDoc} */ diff --git a/cayenne/src/main/java/org/apache/cayenne/map/ObjEntity.java b/cayenne/src/main/java/org/apache/cayenne/map/ObjEntity.java index 0f792cb18..ef962bf6b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/map/ObjEntity.java +++ b/cayenne/src/main/java/org/apache/cayenne/map/ObjEntity.java @@ -928,6 +928,17 @@ public class ObjEntity extends Entity<ObjEntity, ObjAttribute, ObjRelationship> Expression expression = (Expression) input; + if (expression.getType() == Expression.FULL_OBJECT && expression.getOperandCount() == 0) { + // A bare "self" reference carries no path, so there is nothing to rebase on the related + // entity. Resolve it to this entity's PK, which is what it means in the first place. + Collection<DbAttribute> pks = getDbEntity().getPrimaryKeys(); + if (pks.size() > 1) { + throw new CayenneRuntimeException("Can't transform a 'self' expression rooted in '%s': " + + "entity has more than one PK. Match by id map or ObjectId instead.", getName()); + } + return ExpressionFactory.dbPathExp(pks.iterator().next().getName()); + } + if (expression.getType() != Expression.OBJ_PATH) { return input; } 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 532942134..c09f27be5 100644 --- a/cayenne/src/main/java/org/apache/cayenne/query/SelectById.java +++ b/cayenne/src/main/java/org/apache/cayenne/query/SelectById.java @@ -40,7 +40,34 @@ import static org.apache.cayenne.exp.ExpressionFactory.*; * A query to select objects by id. * * @since 4.0 + * @deprecated this query is a thin wrapper around an {@link ObjectSelect} with a PK qualifier - use + * {@link ObjectSelect} directly with one of the "id" expressions of the entity "self" property: + * <table> + * <caption>Migration</caption> + * <tr><th>SelectById</th><th>ObjectSelect</th></tr> + * <tr><td>{@code queryId(Artist.class, id)}</td> + * <td>{@code query(Artist.class).where(Artist.SELF.eqId(id))}</td></tr> + * <tr><td>{@code queryIds(Artist.class, ids)}</td> + * <td>{@code query(Artist.class).where(Artist.SELF.idsIn(ids))}</td></tr> + * <tr><td>{@code queryIdsCollection(Artist.class, ids)}</td> + * <td>{@code query(Artist.class).where(Artist.SELF.idsInCollection(ids))}</td></tr> + * <tr><td>{@code queryMap(Artist.class, id)}</td> + * <td>{@code query(Artist.class).where(Artist.SELF.eqId(id))}</td></tr> + * <tr><td>{@code queryMaps(Artist.class, ids)}</td> + * <td>{@code query(Artist.class).where(Artist.SELF.idMapsIn(ids))}</td></tr> + * <tr><td>{@code queryMapsCollection(Artist.class, ids)}</td> + * <td>{@code query(Artist.class).where(Artist.SELF.idMapsInCollection(ids))}</td></tr> + * <tr><td>{@code queryObjectId(Artist.class, id)}</td> + * <td>{@code query(Artist.class).where(Artist.SELF.eqId(id))}</td></tr> + * <tr><td>{@code queryObjectIds(Artist.class, ids)}</td> + * <td>{@code query(Artist.class).where(Artist.SELF.objectIdsIn(ids))}</td></tr> + * <tr><td>{@code queryObjectIdsCollection(Artist.class, ids)}</td> + * <td>{@code query(Artist.class).where(Artist.SELF.objectIdsInCollection(ids))}</td></tr> + * <tr><td>{@code dataRowQuery*(..)}</td> + * <td>any of the above plus {@link ObjectSelect#fetchDataRows()}</td></tr> + * </table> */ +@Deprecated(since = "5.0", forRemoval = true) public class SelectById<T> extends IndirectQuery implements Select<T> { private static final long serialVersionUID = -6589464349051607583L; diff --git a/cayenne/src/test/java/org/apache/cayenne/CDOMapRelationshipIT.java b/cayenne/src/test/java/org/apache/cayenne/CDOMapRelationshipIT.java index 9668f1653..2e6c65946 100644 --- a/cayenne/src/test/java/org/apache/cayenne/CDOMapRelationshipIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/CDOMapRelationshipIT.java @@ -21,7 +21,7 @@ package org.apache.cayenne; import java.util.Map; import org.apache.cayenne.query.RefreshQuery; -import org.apache.cayenne.query.SelectById; +import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.test.jdbc.TableHelper; import org.apache.cayenne.testdo.map_to_many.IdMapToMany; import org.apache.cayenne.testdo.map_to_many.MapToMany; @@ -124,7 +124,8 @@ public class CDOMapRelationshipIT { public void readToManyPrefetching() throws Exception { createTestDataSet(); - MapToMany o1 = SelectById.query(MapToMany.class, 1).prefetch(MapToMany.TARGETS.disjoint()).selectOne(env.context()); + MapToMany o1 = ObjectSelect.query(MapToMany.class) + .where(MapToMany.SELF.eqId(1)).prefetch(MapToMany.TARGETS.disjoint()).selectOne(env.context()); Map targets = o1.getTargets(); assertFalse(((ValueHolder) targets).isFault()); diff --git a/cayenne/src/test/java/org/apache/cayenne/CDOSetRelationshipIT.java b/cayenne/src/test/java/org/apache/cayenne/CDOSetRelationshipIT.java index 27193339b..18766e6ef 100644 --- a/cayenne/src/test/java/org/apache/cayenne/CDOSetRelationshipIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/CDOSetRelationshipIT.java @@ -19,7 +19,7 @@ package org.apache.cayenne; import org.apache.cayenne.query.RefreshQuery; -import org.apache.cayenne.query.SelectById; +import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.test.jdbc.TableHelper; import org.apache.cayenne.testdo.relationships_set_to_many.SetToMany; import org.apache.cayenne.testdo.relationships_set_to_many.SetToManyTarget; @@ -93,7 +93,8 @@ public class CDOSetRelationshipIT { public void readToManyPrefetching() throws Exception { createTestDataSet(); - SetToMany o1 = SelectById.query(SetToMany.class, 1).prefetch(SetToMany.TARGETS.disjoint()).selectOne(env.context()); + SetToMany o1 = ObjectSelect.query(SetToMany.class) + .where(SetToMany.SELF.eqId(1)).prefetch(SetToMany.TARGETS.disjoint()).selectOne(env.context()); Set targets = o1.getTargets(); diff --git a/cayenne/src/test/java/org/apache/cayenne/MeaningfulFKIT.java b/cayenne/src/test/java/org/apache/cayenne/MeaningfulFKIT.java index 520bac43c..4a5aa95f8 100644 --- a/cayenne/src/test/java/org/apache/cayenne/MeaningfulFKIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/MeaningfulFKIT.java @@ -19,7 +19,7 @@ package org.apache.cayenne; -import org.apache.cayenne.query.SelectById; +import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.testdo.relationships.MeaningfulFK; import org.apache.cayenne.testdo.relationships.RelationshipHelper; import org.apache.cayenne.unit.CayenneProjects; @@ -71,7 +71,8 @@ public class MeaningfulFKIT { env.context().commitChanges(); - MeaningfulFK testObject2 = SelectById.query(MeaningfulFK.class, testObject.getObjectId()).selectOne(env.context()); + MeaningfulFK testObject2 = ObjectSelect.query(MeaningfulFK.class) + .where(MeaningfulFK.SELF.eqId(testObject.getObjectId())).selectOne(env.context()); assertNotEquals(0, testObject2.getRelationshipHelperID()); } } diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextFlattenedAttributesIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextFlattenedAttributesIT.java index 324ad6a4a..0863c1b72 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextFlattenedAttributesIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextFlattenedAttributesIT.java @@ -28,7 +28,6 @@ import org.apache.cayenne.map.DefaultEntityResultSegment; import org.apache.cayenne.query.ColumnSelect; import org.apache.cayenne.query.EJBQLQuery; import org.apache.cayenne.query.ObjectSelect; -import org.apache.cayenne.query.SelectById; import org.apache.cayenne.reflect.PersistentDescriptor; import org.apache.cayenne.runtime.CayenneRuntime; import org.apache.cayenne.test.jdbc.TableHelper; @@ -558,7 +557,8 @@ public class DataContextFlattenedAttributesIT { { // read and update ObjectContext context2 = runtime.newContext(); - CompoundPainting o2 = SelectById.query(CompoundPainting.class, id).selectFirst(context2); + CompoundPainting o2 = ObjectSelect.query(CompoundPainting.class) + .where(CompoundPainting.SELF.eqId(id)).selectFirst(context2); o2.setArtistName("AX1"); o2.setEstimatedPrice(BigDecimal.valueOf(2)); @@ -572,7 +572,8 @@ public class DataContextFlattenedAttributesIT { { // read and check ObjectContext context3 = runtime.newContext(); - CompoundPainting o3 = SelectById.query(CompoundPainting.class, id).selectFirst(context3); + CompoundPainting o3 = ObjectSelect.query(CompoundPainting.class) + .where(CompoundPainting.SELF.eqId(id)).selectFirst(context3); assertEquals("AX1", o3.getArtistName()); assertEquals(0, BigDecimal.valueOf(2).compareTo(o3.getEstimatedPrice())); diff --git a/cayenne/src/test/java/org/apache/cayenne/access/EntityInheritanceIT.java b/cayenne/src/test/java/org/apache/cayenne/access/EntityInheritanceIT.java index 58a9486f5..7e42c940e 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/EntityInheritanceIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/EntityInheritanceIT.java @@ -20,7 +20,7 @@ package org.apache.cayenne.access; import org.apache.cayenne.Cayenne; -import org.apache.cayenne.query.SelectById; +import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.testdo.inheritance.BaseEntity; import org.apache.cayenne.testdo.inheritance.DirectToSubEntity; import org.apache.cayenne.testdo.inheritance.RelatedEntity; @@ -97,7 +97,8 @@ public class EntityInheritanceIT { BaseEntity forPkLoadedEntity = Cayenne.objectForPK(env.context(), BaseEntity.class, subEntityId); assertEquals(forPkLoadedEntity.getClass(), SubEntity.class); - BaseEntity selectLoadedEntity = SelectById.query(BaseEntity.class, subEntityId).selectOne(env.context()); + BaseEntity selectLoadedEntity = ObjectSelect.query(BaseEntity.class) + .where(BaseEntity.SELF.eqId(subEntityId)).selectOne(env.context()); assertEquals(selectLoadedEntity.getClass(), SubEntity.class); } } diff --git a/cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java b/cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java index f6ae0e2a2..a0216d5fb 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java @@ -27,7 +27,6 @@ import org.apache.cayenne.query.ColumnSelect; import org.apache.cayenne.query.EJBQLQuery; import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.query.PrefetchTreeNode; -import org.apache.cayenne.query.SelectById; import org.apache.cayenne.runtime.CayenneRuntime; import org.apache.cayenne.test.jdbc.TableHelper; import org.apache.cayenne.testdo.inheritance_vertical.*; @@ -244,8 +243,8 @@ public class VerticalInheritanceIT { TableHelper ivSub3Table = env.table("IV_SUB3", "ID", "IV_ROOT_ID"); ivSub3Table.insert(3, 1); - IvRoot root = SelectById.query(IvRoot.class, 2).selectOne(env.context()); - IvSub3 sub3 = SelectById.query(IvSub3.class, 3).selectOne(env.context()); + IvRoot root = ObjectSelect.query(IvRoot.class).where(IvRoot.SELF.eqId(2)).selectOne(env.context()); + IvSub3 sub3 = ObjectSelect.query(IvSub3.class).where(IvSub3.SELF.eqId(3)).selectOne(env.context()); sub3.setName("new name"); sub3.setIvRoot(root); @@ -254,7 +253,7 @@ public class VerticalInheritanceIT { env.context().commitChanges(); ObjectContext cleanContext = runtime.newContext(); - IvSub3 sub3Clean = SelectById.query(IvSub3.class, 3).selectOne(cleanContext); + IvSub3 sub3Clean = ObjectSelect.query(IvSub3.class).where(IvSub3.SELF.eqId(3)).selectOne(cleanContext); assertNotNull(sub3Clean); assertNotSame(sub3, sub3Clean); @@ -623,7 +622,8 @@ public class VerticalInheritanceIT { long id = Cayenne.longPKForObject(concrete); { ObjectContext cleanContext = runtime.newContext(); - IvConcrete concreteFetched = SelectById.query(IvConcrete.class, id).selectOne(cleanContext); + IvConcrete concreteFetched = ObjectSelect.query(IvConcrete.class) + .where(IvConcrete.SELF.eqId(id)).selectOne(cleanContext); assertNull(concreteFetched.getName()); } } @@ -646,7 +646,7 @@ public class VerticalInheritanceIT { long id = Cayenne.longPKForObject(impl); { ObjectContext cleanContext = runtime.newContext(); - IvImpl implFetched = SelectById.query(IvImpl.class, id).selectOne(cleanContext); + IvImpl implFetched = ObjectSelect.query(IvImpl.class).where(IvImpl.SELF.eqId(id)).selectOne(cleanContext); assertEquals("Impl 1", implFetched.getName()); assertNull(implFetched.getOther1()); } @@ -665,8 +665,8 @@ public class VerticalInheritanceIT { ivBaseTable.insert(1, "Impl 1", "I"); ivImplTable.insert(1, "attr1", 1); - IvImpl impl = SelectById.query(IvImpl.class, 1).selectOne(env.context()); - IvOther other = SelectById.query(IvOther.class, 2).selectOne(env.context()); + IvImpl impl = ObjectSelect.query(IvImpl.class).where(IvImpl.SELF.eqId(1)).selectOne(env.context()); + IvOther other = ObjectSelect.query(IvOther.class).where(IvOther.SELF.eqId(2)).selectOne(env.context()); impl.setOther3(other); env.context().commitChanges(); @@ -676,8 +676,8 @@ public class VerticalInheritanceIT { { ObjectContext cleanContext = runtime.newContext(); - IvImpl implFetched = SelectById.query(IvImpl.class, 1).selectOne(cleanContext); - IvOther otherFetched = SelectById.query(IvOther.class, 2).selectOne(cleanContext); + IvImpl implFetched = ObjectSelect.query(IvImpl.class).where(IvImpl.SELF.eqId(1)).selectOne(cleanContext); + IvOther otherFetched = ObjectSelect.query(IvOther.class).where(IvOther.SELF.eqId(2)).selectOne(cleanContext); assertEquals("Impl 1", implFetched.getName()); assertEquals("attr1", implFetched.getAttr1()); assertEquals(implFetched.getOther3(), otherFetched); @@ -688,7 +688,7 @@ public class VerticalInheritanceIT { public void deleteFlattenedNoValues() throws SQLException { ivAbstractTable.insert(1, null, "S"); - IvConcrete concrete = SelectById.query(IvConcrete.class, 1).selectOne(env.context()); + IvConcrete concrete = ObjectSelect.query(IvConcrete.class).where(IvConcrete.SELF.eqId(1)).selectOne(env.context()); assertNotNull(concrete); assertNull(concrete.getName()); @@ -704,7 +704,7 @@ public class VerticalInheritanceIT { ivAbstractTable.insert(1, null, "S"); ivConcreteTable.insert(1, null, null); - IvConcrete concrete = SelectById.query(IvConcrete.class, 1).selectOne(env.context()); + IvConcrete concrete = ObjectSelect.query(IvConcrete.class).where(IvConcrete.SELF.eqId(1)).selectOne(env.context()); assertNotNull(concrete); assertNull(concrete.getName()); @@ -720,7 +720,7 @@ public class VerticalInheritanceIT { ivAbstractTable.insert(1, null, "S"); ivConcreteTable.insert(1, "test", null); - IvConcrete concrete = SelectById.query(IvConcrete.class, 1).selectOne(env.context()); + IvConcrete concrete = ObjectSelect.query(IvConcrete.class).where(IvConcrete.SELF.eqId(1)).selectOne(env.context()); assertNotNull(concrete); assertEquals("test", concrete.getName()); @@ -745,7 +745,7 @@ public class VerticalInheritanceIT { ivAbstractTable.insert(2, null, "S"); ivConcreteTable.insert(2, "Two", 1); - IvConcrete concrete = SelectById.query(IvConcrete.class, 2).selectOne(env.context()); + IvConcrete concrete = ObjectSelect.query(IvConcrete.class).where(IvConcrete.SELF.eqId(2)).selectOne(env.context()); concrete.setRelatedAbstract(null); env.context().commitChanges(); @@ -753,7 +753,8 @@ public class VerticalInheritanceIT { { ObjectContext cleanContext = runtime.newContext(); - IvConcrete concreteFetched = SelectById.query(IvConcrete.class, 2).selectOne(cleanContext); + IvConcrete concreteFetched = ObjectSelect.query(IvConcrete.class) + .where(IvConcrete.SELF.eqId(2)).selectOne(cleanContext); assertEquals("Two", concreteFetched.getName()); assertNull(concreteFetched.getRelatedAbstract()); } @@ -1217,7 +1218,7 @@ public class VerticalInheritanceIT { assertEquals(1, ivImplTable.getRowCount()); ObjectContext cleanContext = runtime.newContext(); - IvImpl reread = SelectById.queryId(IvImpl.class, 1).selectOne(cleanContext); + IvImpl reread = ObjectSelect.query(IvImpl.class).where(IvImpl.SELF.eqId(1)).selectOne(cleanContext); assertEquals("attr1-updated", reread.getAttr1()); } @@ -1264,7 +1265,7 @@ public class VerticalInheritanceIT { assertEquals(1, ivImplTable.getRowCount()); ObjectContext cleanContext = runtime.newContext(); - IvImpl reread = SelectById.queryId(IvImpl.class, 1).selectOne(cleanContext); + IvImpl reread = ObjectSelect.query(IvImpl.class).where(IvImpl.SELF.eqId(1)).selectOne(cleanContext); assertEquals("attr1-second", reread.getAttr1()); } @@ -1278,7 +1279,7 @@ public class VerticalInheritanceIT { ivSub1Table.insert(1); ivSub1Sub1Table.insert(1, "sub1sub1name"); - IvSub1Sub1 sub1Sub1 = SelectById.queryId(IvSub1Sub1.class, 1).selectOne(env.context()); + IvSub1Sub1 sub1Sub1 = ObjectSelect.query(IvSub1Sub1.class).where(IvSub1Sub1.SELF.eqId(1)).selectOne(env.context()); assertEquals("sub1sub1name", sub1Sub1.getSub1Sub1Name()); sub1Sub1.setSub1Sub1Name("sub1sub1name-updated"); @@ -1286,7 +1287,7 @@ public class VerticalInheritanceIT { assertEquals(1, ivSub1Sub1Table.getRowCount()); ObjectContext cleanContext = runtime.newContext(); - IvSub1Sub1 reread = SelectById.queryId(IvSub1Sub1.class, 1).selectOne(cleanContext); + IvSub1Sub1 reread = ObjectSelect.query(IvSub1Sub1.class).where(IvSub1Sub1.SELF.eqId(1)).selectOne(cleanContext); assertEquals("sub1sub1name-updated", reread.getSub1Sub1Name()); } } diff --git a/cayenne/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java b/cayenne/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java index 668fbd6ae..c489ba39d 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java @@ -25,7 +25,6 @@ import org.apache.cayenne.ObjectId; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.query.ObjectSelect; -import org.apache.cayenne.query.SelectById; import org.apache.cayenne.runtime.CayenneRuntime; import org.apache.cayenne.test.jdbc.TableHelper; import org.apache.cayenne.testdo.compound.CompoundFkTestEntity; @@ -161,9 +160,6 @@ public class QualifierTranslatorIT { .where(CompoundPkTestEntity.SELF.eqId(id)).selectOne(env.context()); assertNotNull(viaSelf); assertEquals("CCC", viaSelf.getName()); - - // must agree with the dedicated by-id query - assertSame(SelectById.queryObjectId(CompoundPkTestEntity.class, id).selectOne(env.context()), viaSelf); } /** diff --git a/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilterIT.java b/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilterIT.java index 552173312..cde6eddc4 100644 --- a/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilterIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilterIT.java @@ -25,7 +25,7 @@ import org.apache.cayenne.commitlog.db.AuditableChild1; import org.apache.cayenne.commitlog.db.AuditableChild1x; import org.apache.cayenne.commitlog.model.*; import org.apache.cayenne.commitlog.unit.AuditableRuntimeCase; -import org.apache.cayenne.query.SelectById; +import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.configuration.runtime.CoreModule; import org.apache.cayenne.runtime.CayenneRuntimeBuilder; import org.junit.jupiter.api.BeforeEach; @@ -87,7 +87,7 @@ public class CommitLogFilterIT extends AuditableRuntimeCase { auditable1.insert(1, "xx"); - Auditable1 a1 = SelectById.query(Auditable1.class, 1).selectOne(context); + Auditable1 a1 = ObjectSelect.query(Auditable1.class).where(Auditable1.SELF.eqId(1)).selectOne(context); a1.setCharProperty1("yy"); ObjectId preCommitId = a1.getObjectId(); @@ -120,7 +120,7 @@ public class CommitLogFilterIT extends AuditableRuntimeCase { auditableChild1.insert(1, 1, "cc1"); auditableChild1.insert(2, 1, "cc2"); - Auditable1 a1 = SelectById.query(Auditable1.class, 1).selectOne(context); + Auditable1 a1 = ObjectSelect.query(Auditable1.class).where(Auditable1.SELF.eqId(1)).selectOne(context); context.deleteObjects(a1.getChildren1()); context.deleteObject(a1); context.commitChanges(); @@ -164,7 +164,8 @@ public class CommitLogFilterIT extends AuditableRuntimeCase { auditableChild1.insert(1, 1, "cc1"); auditableChild1.insert(2, 1, "cc2"); - AuditableChild1 ac1 = SelectById.query(AuditableChild1.class, 2).selectOne(context); + AuditableChild1 ac1 = ObjectSelect.query(AuditableChild1.class) + .where(AuditableChild1.SELF.eqId(2)).selectOne(context); context.deleteObject(ac1); context.commitChanges(); @@ -194,7 +195,8 @@ public class CommitLogFilterIT extends AuditableRuntimeCase { auditableChild1x.insert(1, 1, "cc1"); auditableChild1x.insert(2, 1, "cc2"); - AuditableChild1x ac1 = SelectById.query(AuditableChild1x.class, 2).selectOne(context); + AuditableChild1x ac1 = ObjectSelect.query(AuditableChild1x.class) + .where(AuditableChild1x.SELF.eqId(2)).selectOne(context); context.deleteObject(ac1); context.commitChanges(); @@ -228,12 +230,15 @@ public class CommitLogFilterIT extends AuditableRuntimeCase { auditableChild1.insert(2, 2, "cc2"); auditableChild1.insert(3, null, "cc3"); - AuditableChild1 ac1 = SelectById.query(AuditableChild1.class, 1).selectOne(context); - AuditableChild1 ac2 = SelectById.query(AuditableChild1.class, 2).selectOne(context); - AuditableChild1 ac3 = SelectById.query(AuditableChild1.class, 3).selectOne(context); + AuditableChild1 ac1 = ObjectSelect.query(AuditableChild1.class) + .where(AuditableChild1.SELF.eqId(1)).selectOne(context); + AuditableChild1 ac2 = ObjectSelect.query(AuditableChild1.class) + .where(AuditableChild1.SELF.eqId(2)).selectOne(context); + AuditableChild1 ac3 = ObjectSelect.query(AuditableChild1.class) + .where(AuditableChild1.SELF.eqId(3)).selectOne(context); - Auditable1 a1 = SelectById.query(Auditable1.class, 1).selectOne(context); - Auditable1 a2 = SelectById.query(Auditable1.class, 2).selectOne(context); + Auditable1 a1 = ObjectSelect.query(Auditable1.class).where(Auditable1.SELF.eqId(1)).selectOne(context); + Auditable1 a2 = ObjectSelect.query(Auditable1.class).where(Auditable1.SELF.eqId(2)).selectOne(context); a1.removeFromChildren1(ac1); a1.addToChildren1(ac2); @@ -297,11 +302,14 @@ public class CommitLogFilterIT extends AuditableRuntimeCase { auditableChild1.insert(2, null, "cc2"); auditableChild1.insert(3, null, "cc3"); - AuditableChild1 ac1 = SelectById.query(AuditableChild1.class, 1).selectOne(context); - AuditableChild1 ac2 = SelectById.query(AuditableChild1.class, 2).selectOne(context); - AuditableChild1 ac3 = SelectById.query(AuditableChild1.class, 3).selectOne(context); + AuditableChild1 ac1 = ObjectSelect.query(AuditableChild1.class) + .where(AuditableChild1.SELF.eqId(1)).selectOne(context); + AuditableChild1 ac2 = ObjectSelect.query(AuditableChild1.class) + .where(AuditableChild1.SELF.eqId(2)).selectOne(context); + AuditableChild1 ac3 = ObjectSelect.query(AuditableChild1.class) + .where(AuditableChild1.SELF.eqId(3)).selectOne(context); - Auditable1 a1 = SelectById.query(Auditable1.class, 1).selectOne(context); + Auditable1 a1 = ObjectSelect.query(Auditable1.class).where(Auditable1.SELF.eqId(1)).selectOne(context); a1.removeFromChildren1(ac1); a1.addToChildren1(ac2); diff --git a/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_All_FlattenedIT.java b/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_All_FlattenedIT.java index 13eaac30c..1da500630 100644 --- a/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_All_FlattenedIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_All_FlattenedIT.java @@ -27,7 +27,7 @@ import org.apache.cayenne.commitlog.model.ObjectChange; import org.apache.cayenne.commitlog.model.ObjectChangeType; import org.apache.cayenne.commitlog.model.ToManyRelationshipChange; import org.apache.cayenne.commitlog.unit.FlattenedRuntimeCase; -import org.apache.cayenne.query.SelectById; +import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.configuration.runtime.CoreModule; import org.apache.cayenne.runtime.CayenneRuntimeBuilder; import org.junit.jupiter.api.BeforeEach; @@ -64,9 +64,9 @@ public class CommitLogFilter_All_FlattenedIT extends FlattenedRuntimeCase { e4.insert(12); e34.insert(1, 11); - E3 e3 = SelectById.query(E3.class, 1).selectOne(context); - E4 e4_1 = SelectById.query(E4.class, 11).selectOne(context); - E4 e4_2 = SelectById.query(E4.class, 12).selectOne(context); + E3 e3 = ObjectSelect.query(E3.class).where(E3.SELF.eqId(1)).selectOne(context); + E4 e4_1 = ObjectSelect.query(E4.class).where(E4.SELF.eqId(11)).selectOne(context); + E4 e4_2 = ObjectSelect.query(E4.class).where(E4.SELF.eqId(12)).selectOne(context); doAnswer((Answer<Object>) invocation -> { diff --git a/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_FilteredIT.java b/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_FilteredIT.java index 4c624a749..efc53f8f8 100644 --- a/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_FilteredIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_FilteredIT.java @@ -31,7 +31,7 @@ import org.apache.cayenne.commitlog.model.ObjectChange; import org.apache.cayenne.commitlog.model.ObjectChangeType; import org.apache.cayenne.commitlog.model.ToManyRelationshipChange; import org.apache.cayenne.commitlog.unit.AuditableRuntimeCase; -import org.apache.cayenne.query.SelectById; +import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.configuration.runtime.CoreModule; import org.apache.cayenne.runtime.CayenneRuntimeBuilder; import org.junit.jupiter.api.BeforeEach; @@ -116,7 +116,7 @@ public class CommitLogFilter_FilteredIT extends AuditableRuntimeCase { return null; }).when(mockListener).onPostCommit(any(ObjectContext.class), any(ChangeMap.class)); - Auditable2 a1 = SelectById.query(Auditable2.class, 1).selectOne(context); + Auditable2 a1 = ObjectSelect.query(Auditable2.class).where(Auditable2.SELF.eqId(1)).selectOne(context); a1.setCharProperty1("P1_2"); a1.setCharProperty2("P2_2"); context.commitChanges(); @@ -146,7 +146,7 @@ public class CommitLogFilter_FilteredIT extends AuditableRuntimeCase { return null; }).when(mockListener).onPostCommit(any(ObjectContext.class), any(ChangeMap.class)); - Auditable2 a1 = SelectById.query(Auditable2.class, 1).selectOne(context); + Auditable2 a1 = ObjectSelect.query(Auditable2.class).where(Auditable2.SELF.eqId(1)).selectOne(context); context.deleteObject(a1); context.commitChanges(); @@ -160,11 +160,14 @@ public class CommitLogFilter_FilteredIT extends AuditableRuntimeCase { auditableChild1.insert(2, null, "cc2"); auditableChild1.insert(3, null, "cc3"); - final AuditableChild1 ac1 = SelectById.query(AuditableChild1.class, 1).selectOne(context); - final AuditableChild1 ac2 = SelectById.query(AuditableChild1.class, 2).selectOne(context); - final AuditableChild1 ac3 = SelectById.query(AuditableChild1.class, 3).selectOne(context); + final AuditableChild1 ac1 = ObjectSelect.query(AuditableChild1.class) + .where(AuditableChild1.SELF.eqId(1)).selectOne(context); + final AuditableChild1 ac2 = ObjectSelect.query(AuditableChild1.class) + .where(AuditableChild1.SELF.eqId(2)).selectOne(context); + final AuditableChild1 ac3 = ObjectSelect.query(AuditableChild1.class) + .where(AuditableChild1.SELF.eqId(3)).selectOne(context); - final Auditable1 a1 = SelectById.query(Auditable1.class, 1).selectOne(context); + final Auditable1 a1 = ObjectSelect.query(Auditable1.class).where(Auditable1.SELF.eqId(1)).selectOne(context); doAnswer((Answer<Object>) invocation -> { @@ -208,7 +211,7 @@ public class CommitLogFilter_FilteredIT extends AuditableRuntimeCase { auditable3.insert(1, "31", "32"); - final Auditable3 a3 = SelectById.query(Auditable3.class, 1).selectOne(context); + final Auditable3 a3 = ObjectSelect.query(Auditable3.class).where(Auditable3.SELF.eqId(1)).selectOne(context); doAnswer((Answer<Object>) invocation -> { @@ -235,9 +238,9 @@ public class CommitLogFilter_FilteredIT extends AuditableRuntimeCase { auditable4.insert(11, "41", "42", 1); auditable4.insert(12, "43", "44", 1); - final Auditable3 a3 = SelectById.query(Auditable3.class, 1).selectOne(context); - final Auditable4 a41 = SelectById.query(Auditable4.class, 11).selectOne(context); - final Auditable4 a42 = SelectById.query(Auditable4.class, 12).selectOne(context); + final Auditable3 a3 = ObjectSelect.query(Auditable3.class).where(Auditable3.SELF.eqId(1)).selectOne(context); + final Auditable4 a41 = ObjectSelect.query(Auditable4.class).where(Auditable4.SELF.eqId(11)).selectOne(context); + final Auditable4 a42 = ObjectSelect.query(Auditable4.class).where(Auditable4.SELF.eqId(12)).selectOne(context); doAnswer((Answer<Object>) invocation -> { @@ -264,9 +267,9 @@ public class CommitLogFilter_FilteredIT extends AuditableRuntimeCase { auditable3.insert(2, "33", "34"); auditable4.insert(11, "41", "41", 1); - final Auditable3 a32 = SelectById.query(Auditable3.class, 2).selectOne(context); + final Auditable3 a32 = ObjectSelect.query(Auditable3.class).where(Auditable3.SELF.eqId(2)).selectOne(context); - final Auditable4 a4 = SelectById.query(Auditable4.class, 11).selectOne(context); + final Auditable4 a4 = ObjectSelect.query(Auditable4.class).where(Auditable4.SELF.eqId(11)).selectOne(context); doAnswer((Answer<Object>) invocation -> { diff --git a/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_ListenerInducedChangesIT.java b/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_ListenerInducedChangesIT.java index 7fcf8a232..00eb3d9a3 100644 --- a/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_ListenerInducedChangesIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_ListenerInducedChangesIT.java @@ -29,7 +29,7 @@ import org.apache.cayenne.commitlog.model.ChangeMap; import org.apache.cayenne.commitlog.model.ObjectChange; import org.apache.cayenne.commitlog.model.ObjectChangeType; import org.apache.cayenne.commitlog.unit.AuditableRuntimeCase; -import org.apache.cayenne.query.SelectById; +import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.configuration.runtime.CoreModule; import org.apache.cayenne.runtime.CayenneRuntimeBuilder; import org.junit.jupiter.api.BeforeEach; @@ -111,7 +111,8 @@ public class CommitLogFilter_ListenerInducedChangesIT extends AuditableRuntimeCa final DeleteListener listener = new DeleteListener(); runtime.getDataDomain().addListener(listener); - final Auditable1 a1 = SelectById.query(Auditable1.class, 1).prefetch(Auditable1.CHILDREN1.joint()) + final Auditable1 a1 = ObjectSelect.query(Auditable1.class) + .where(Auditable1.SELF.eqId(1)).prefetch(Auditable1.CHILDREN1.joint()) .selectFirst(context); a1.setCharProperty1("zz"); @@ -152,7 +153,8 @@ public class CommitLogFilter_ListenerInducedChangesIT extends AuditableRuntimeCa final UpdateListener listener = new UpdateListener(); runtime.getDataDomain().addListener(listener); - final Auditable1 a1 = SelectById.query(Auditable1.class, 1).prefetch(Auditable1.CHILDREN1.joint()) + final Auditable1 a1 = ObjectSelect.query(Auditable1.class) + .where(Auditable1.SELF.eqId(1)).prefetch(Auditable1.CHILDREN1.joint()) .selectFirst(context); a1.setCharProperty1("zz"); diff --git a/cayenne/src/test/java/org/apache/cayenne/exp/property/EntityPropertyIdIT.java b/cayenne/src/test/java/org/apache/cayenne/exp/property/EntityPropertyIdIT.java new file mode 100644 index 000000000..0b3d15be7 --- /dev/null +++ b/cayenne/src/test/java/org/apache/cayenne/exp/property/EntityPropertyIdIT.java @@ -0,0 +1,161 @@ +/***************************************************************** + * 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 + * + * https://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.exp.property; + +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.ObjectId; +import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.ExpressionFactory; +import org.apache.cayenne.query.ObjectSelect; +import org.apache.cayenne.test.jdbc.TableHelper; +import org.apache.cayenne.testdo.compound.CompoundFkTestEntity; +import org.apache.cayenne.testdo.compound.CompoundPkTestEntity; +import org.apache.cayenne.unit.CayenneProjects; +import org.apache.cayenne.unit.CayenneTestsEnv; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class EntityPropertyIdIT { + + @RegisterExtension + static final CayenneTestsEnv env = CayenneTestsEnv.forProject(CayenneProjects.COMPOUND_PROJECT); + + private static final Map<String, ?> PK_1 = Map.of("KEY1", "PK1", "KEY2", "PK2"); + private static final Map<String, ?> PK_2 = Map.of("KEY1", "PK3", "KEY2", "PK4"); + + @BeforeEach + public void seed() throws SQLException { + TableHelper pk = env.table("COMPOUND_PK_TEST", "KEY1", "KEY2", "NAME"); + pk.insert("PK1", "PK2", "BBB"); + pk.insert("PK3", "PK4", "CCC"); + + TableHelper fk = env.table("COMPOUND_FK_TEST", "PKEY", "F_KEY1", "F_KEY2", "NAME"); + fk.insert(1, "PK1", "PK2", "FK1"); + fk.insert(2, "PK3", "PK4", "FK2"); + } + + // --- "self" property: an empty path --- + + @Test + public void selfEqIdMap() { + CompoundPkTestEntity o = ObjectSelect.query(CompoundPkTestEntity.class) + .where(CompoundPkTestEntity.SELF.eqIdMap(PK_2)).selectOne(env.context()); + assertNotNull(o); + assertEquals("CCC", o.getName()); + } + + @Test + public void selfEqIdMapMatchesExplicitDbExpression() { + CompoundPkTestEntity viaProperty = ObjectSelect.query(CompoundPkTestEntity.class) + .where(CompoundPkTestEntity.SELF.eqIdMap(PK_2)).selectOne(env.context()); + + // the same match, spelled out as raw db paths + CompoundPkTestEntity viaExpression = ObjectSelect.query(CompoundPkTestEntity.class) + .where(ExpressionFactory.matchAllDbExp(PK_2, Expression.EQUAL_TO)).selectOne(env.context()); + + assertSame(viaExpression, viaProperty); + } + + @Test + public void selfNeqIdMap() { + List<CompoundPkTestEntity> list = ObjectSelect.query(CompoundPkTestEntity.class) + .where(CompoundPkTestEntity.SELF.neqIdMap(PK_1)).select(env.context()); + assertEquals(1, list.size()); + assertEquals("CCC", list.get(0).getName()); + } + + @Test + public void selfIdMapsIn() { + List<CompoundPkTestEntity> list = ObjectSelect.query(CompoundPkTestEntity.class) + .where(CompoundPkTestEntity.SELF.idMapsIn(PK_1, PK_2)) + .orderBy(CompoundPkTestEntity.NAME.asc()).select(env.context()); + assertEquals(2, list.size()); + assertEquals("BBB", list.get(0).getName()); + assertEquals("CCC", list.get(1).getName()); + } + + @Test + public void selfObjectIdsIn() { + ObjectId id1 = ObjectId.of("CompoundPkTestEntity", PK_1); + ObjectId id2 = ObjectId.of("CompoundPkTestEntity", PK_2); + + List<CompoundPkTestEntity> list = ObjectSelect.query(CompoundPkTestEntity.class) + .where(CompoundPkTestEntity.SELF.objectIdsIn(id1, id2)) + .orderBy(CompoundPkTestEntity.NAME.asc()).select(env.context()); + assertEquals(2, list.size()); + assertEquals("BBB", list.get(0).getName()); + assertEquals("CCC", list.get(1).getName()); + } + + // --- to-one relationship property: a non-empty path --- + + @Test + public void toOneEqIdMap() { + CompoundFkTestEntity o = ObjectSelect.query(CompoundFkTestEntity.class) + .where(CompoundFkTestEntity.TO_COMPOUND_PK.eqIdMap(PK_2)).selectOne(env.context()); + assertNotNull(o); + assertEquals("FK2", o.getName()); + } + + @Test + public void toOneIdMapsIn() { + List<CompoundFkTestEntity> list = ObjectSelect.query(CompoundFkTestEntity.class) + .where(CompoundFkTestEntity.TO_COMPOUND_PK.idMapsIn(PK_1, PK_2)) + .orderBy(CompoundFkTestEntity.NAME.asc()).select(env.context()); + assertEquals(2, list.size()); + assertEquals("FK1", list.get(0).getName()); + } + + @Test + public void toOneObjectIdsIn() { + ObjectId id1 = ObjectId.of("CompoundPkTestEntity", PK_1); + List<CompoundFkTestEntity> list = ObjectSelect.query(CompoundFkTestEntity.class) + .where(CompoundFkTestEntity.TO_COMPOUND_PK.objectIdsIn(id1)).select(env.context()); + assertEquals(1, list.size()); + assertEquals("FK1", list.get(0).getName()); + } + + // --- edge cases --- + + @Test + public void emptyIdCollectionMatchesNothing() { + List<CompoundPkTestEntity> list = ObjectSelect.query(CompoundPkTestEntity.class) + .where(CompoundPkTestEntity.SELF.idMapsInCollection(List.of())).select(env.context()); + assertTrue(list.isEmpty()); + } + + @Test + public void emptyIdMapFails() { + CayenneRuntimeException e = assertThrows(CayenneRuntimeException.class, + () -> CompoundPkTestEntity.SELF.eqIdMap(Map.of())); + assertTrue(e.getMessage().contains("Null or empty id map"), e.getMessage()); + } +} diff --git a/cayenne/src/test/java/org/apache/cayenne/query/ObjectSelect_ByIdIT.java b/cayenne/src/test/java/org/apache/cayenne/query/ObjectSelect_ByIdIT.java new file mode 100644 index 000000000..ab3509a11 --- /dev/null +++ b/cayenne/src/test/java/org/apache/cayenne/query/ObjectSelect_ByIdIT.java @@ -0,0 +1,356 @@ +/***************************************************************** + * 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 + * + * https://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.query; + +import org.apache.cayenne.Cayenne; +import org.apache.cayenne.DataRow; +import org.apache.cayenne.ObjectId; +import org.apache.cayenne.ResultBatchIterator; +import org.apache.cayenne.ResultIterator; +import org.apache.cayenne.test.jdbc.TableHelper; +import org.apache.cayenne.testdo.testmap.Artist; +import org.apache.cayenne.testdo.testmap.Painting; +import org.apache.cayenne.unit.CayenneProjects; +import org.apache.cayenne.unit.CayenneTestsEnv; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.sql.Types; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.singletonMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +public class ObjectSelect_ByIdIT { + + @RegisterExtension + static final CayenneTestsEnv env = CayenneTestsEnv.forProject(CayenneProjects.TESTMAP_PROJECT); + + private TableHelper tArtist; + private TableHelper tPainting; + + @BeforeEach + public void setUp() { + tArtist = env.table("ARTIST").setColumns("ARTIST_ID", "ARTIST_NAME"); + tPainting = env.table("PAINTING").setColumns("PAINTING_ID", "ARTIST_ID", "PAINTING_TITLE") + .setColumnTypes(Types.INTEGER, Types.BIGINT, Types.VARCHAR); + } + + private void createTwoArtists() throws Exception { + tArtist.insert(2, "artist2"); + tArtist.insert(3, "artist3"); + } + + // --- scalar ids --- + + @Test + public void intPk() throws Exception { + createTwoArtists(); + + Artist a3 = ObjectSelect.query(Artist.class).where(Artist.SELF.eqId(3)).selectOne(env.context()); + assertNotNull(a3); + assertEquals("artist3", a3.getArtistName()); + + Artist a2 = ObjectSelect.query(Artist.class).where(Artist.SELF.eqId(2)).selectOne(env.context()); + assertNotNull(a2); + assertEquals("artist2", a2.getArtistName()); + } + + @Test + public void intPkSelectFirst() throws Exception { + createTwoArtists(); + + Artist a3 = ObjectSelect.query(Artist.class).where(Artist.SELF.eqId(3)).selectFirst(env.context()); + assertNotNull(a3); + assertEquals("artist3", a3.getArtistName()); + } + + @Test + public void nullPk() { + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(Artist.SELF.eqId(null)).select(env.context()); + assertEquals(0, artists.size()); + } + + @Test + public void intPkMulti() throws Exception { + createTwoArtists(); + + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(Artist.SELF.idsIn(2, 3)).orderBy(Artist.ARTIST_NAME.asc()).select(env.context()); + assertEquals(2, artists.size()); + assertEquals("artist2", artists.get(0).getArtistName()); + assertEquals("artist3", artists.get(1).getArtistName()); + } + + @Test + public void intPkCollection() throws Exception { + createTwoArtists(); + + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(Artist.SELF.idsInCollection(Arrays.asList(2, 3))) + .orderBy(Artist.ARTIST_NAME.asc()).select(env.context()); + assertEquals(2, artists.size()); + } + + @Test + public void emptyPkCollection() { + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(Artist.SELF.idsInCollection(Collections.emptyList())).select(env.context()); + assertEquals(0, artists.size()); + } + + // --- map ids --- + + @Test + public void mapPk() throws Exception { + createTwoArtists(); + + Artist a3 = ObjectSelect.query(Artist.class) + .where(Artist.SELF.eqIdMap(singletonMap(Artist.ARTIST_ID_PK_COLUMN, 3))) + .selectOne(env.context()); + assertNotNull(a3); + assertEquals("artist3", a3.getArtistName()); + } + + @Test + public void mapPkMulti() throws Exception { + createTwoArtists(); + + Map<String, ?> id2 = singletonMap(Artist.ARTIST_ID_PK_COLUMN, 2); + Map<String, ?> id3 = singletonMap(Artist.ARTIST_ID_PK_COLUMN, 3); + + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(Artist.SELF.idMapsIn(id2, id3)) + .orderBy(Artist.ARTIST_NAME.asc()).select(env.context()); + assertEquals(2, artists.size()); + assertEquals("artist2", artists.get(0).getArtistName()); + } + + @Test + public void mapPkCollection() throws Exception { + createTwoArtists(); + + List<Map<String, ?>> ids = Arrays.asList( + singletonMap(Artist.ARTIST_ID_PK_COLUMN, 2), + singletonMap(Artist.ARTIST_ID_PK_COLUMN, 3)); + + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(Artist.SELF.idMapsInCollection(ids)).select(env.context()); + assertEquals(2, artists.size()); + } + + @Test + public void emptyMapPkCollection() { + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(Artist.SELF.idMapsInCollection(Collections.emptyList())).select(env.context()); + assertEquals(0, artists.size()); + } + + // --- ObjectId ids --- + + @Test + public void objectIdPk() throws Exception { + createTwoArtists(); + + ObjectId id = ObjectId.of("Artist", Artist.ARTIST_ID_PK_COLUMN, 3); + Artist a3 = ObjectSelect.query(Artist.class).where(Artist.SELF.eqId(id)).selectOne(env.context()); + assertNotNull(a3); + assertEquals("artist3", a3.getArtistName()); + } + + @Test + public void objectIdPkMulti() throws Exception { + createTwoArtists(); + + ObjectId id2 = ObjectId.of("Artist", Artist.ARTIST_ID_PK_COLUMN, 2); + ObjectId id3 = ObjectId.of("Artist", Artist.ARTIST_ID_PK_COLUMN, 3); + + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(Artist.SELF.objectIdsIn(id2, id3)) + .orderBy(Artist.ARTIST_NAME.asc()).select(env.context()); + assertEquals(2, artists.size()); + assertEquals("artist2", artists.get(0).getArtistName()); + } + + @Test + public void objectIdPkCollection() throws Exception { + createTwoArtists(); + + List<ObjectId> ids = Arrays.asList( + ObjectId.of("Artist", Artist.ARTIST_ID_PK_COLUMN, 2), + ObjectId.of("Artist", Artist.ARTIST_ID_PK_COLUMN, 3)); + + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(Artist.SELF.objectIdsInCollection(ids)).select(env.context()); + assertEquals(2, artists.size()); + } + + // --- DataRows: the "dataRowQuery*" equivalent is chaining fetchDataRows() --- + + @Test + public void dataRowIntPk() throws Exception { + createTwoArtists(); + + DataRow row = ObjectSelect.query(Artist.class).where(Artist.SELF.eqId(3)) + .fetchDataRows().selectOne(env.context()); + assertNotNull(row); + assertEquals("artist3", row.get("ARTIST_NAME")); + } + + @Test + public void dataRowIntPkMulti() throws Exception { + createTwoArtists(); + + List<DataRow> rows = ObjectSelect.query(Artist.class).where(Artist.SELF.idsIn(2, 3)) + .fetchDataRows().select(env.context()); + assertEquals(2, rows.size()); + } + + @Test + public void dataRowObjectIdPk() throws Exception { + createTwoArtists(); + + ObjectId id = ObjectId.of("Artist", Artist.ARTIST_ID_PK_COLUMN, 3); + DataRow row = ObjectSelect.query(Artist.class).where(Artist.SELF.eqId(id)) + .fetchDataRows().selectOne(env.context()); + assertEquals("artist3", row.get("ARTIST_NAME")); + } + + // --- caching and prefetching still work off a by-id qualifier --- + + @Test + public void localCache() throws Exception { + createTwoArtists(); + + final Artist[] a3 = new Artist[1]; + + assertEquals(1, env.runWithQueryCounter(() -> { + a3[0] = ObjectSelect.query(Artist.class).where(Artist.SELF.eqId(3)) + .localCache("g1").selectOne(env.context()); + assertNotNull(a3[0]); + assertEquals("artist3", a3[0].getArtistName()); + })); + + env.runWithQueriesBlocked(() -> { + Artist a3cached = ObjectSelect.query(Artist.class).where(Artist.SELF.eqId(3)) + .localCache("g1").selectOne(env.context()); + assertSame(a3[0], a3cached); + }); + + env.context().performGenericQuery(new RefreshQuery("g1")); + + assertEquals(1, env.runWithQueryCounter(() -> ObjectSelect.query(Artist.class) + .where(Artist.SELF.eqId(3)).localCache("g1").selectOne(env.context()))); + } + + @Test + public void prefetch() throws Exception { + createTwoArtists(); + tPainting.insert(45, 3, "One"); + tPainting.insert(48, 3, "Two"); + + Artist a3 = ObjectSelect.query(Artist.class).where(Artist.SELF.eqId(3)) + .prefetch(Artist.PAINTING_ARRAY.joint()) + .selectOne(env.context()); + + env.runWithQueriesBlocked(() -> { + assertNotNull(a3); + assertEquals("artist3", a3.getArtistName()); + assertEquals(2, a3.getPaintingArray().size()); + + a3.getPaintingArray().get(0).getPaintingTitle(); + a3.getPaintingArray().get(1).getPaintingTitle(); + }); + } + + /** + * A disjoint prefetch rebases the query qualifier onto the prefetched entity. A bare "self" + * reference has no path to rebase, so it must be resolved to the root PK first - otherwise it + * silently reads as the *target* entity's PK. + */ + @Test + public void prefetchDisjoint() throws Exception { + createTwoArtists(); + tPainting.insert(45, 3, "One"); + tPainting.insert(48, 3, "Two"); + + Artist a3 = ObjectSelect.query(Artist.class).where(Artist.SELF.eqId(3)) + .prefetch(Artist.PAINTING_ARRAY.disjoint()) + .selectOne(env.context()); + + env.runWithQueriesBlocked(() -> { + assertNotNull(a3); + assertEquals(2, a3.getPaintingArray().size()); + }); + } + + // --- iterated results, mirroring SelectByIdIteratedQueryIT --- + + @Test + public void queryWithBatchIterator() throws Exception { + createSixPaintings(); + + try (ResultBatchIterator<Painting> iterator = ObjectSelect.query(Painting.class) + .where(Painting.SELF.idsInCollection(Arrays.asList(1, 2, 3, 4, 5, 6))) + .batchIterator(env.context(), 4)) { + + int count = 0; + while (iterator.hasNext()) { + count++; + for (Painting painting : iterator.next()) { + assertEquals("painting" + Cayenne.longPKForObject(painting), painting.getPaintingTitle()); + } + } + assertEquals(2, count); + } + } + + @Test + public void queryWithIterator() throws Exception { + createSixPaintings(); + + try (ResultIterator<Painting> iterator = ObjectSelect.query(Painting.class) + .where(Painting.SELF.idsInCollection(Arrays.asList(1, 2, 3, 4, 5, 6))) + .iterator(env.context())) { + + int count = 0; + while (iterator.hasNextRow()) { + count++; + Painting painting = iterator.nextRow(); + assertEquals("painting" + Cayenne.longPKForObject(painting), painting.getPaintingTitle()); + } + assertEquals(6, count); + } + } + + private void createSixPaintings() throws Exception { + tArtist.insert(1, "artist1"); + for (int i = 1; i <= 6; i++) { + tPainting.insert(i, 1, "painting" + i); + } + } +} diff --git a/cayenne/src/test/java/org/apache/cayenne/query/SelectByIdIteratedQueryIT.java b/cayenne/src/test/java/org/apache/cayenne/query/SelectByIdIteratedQueryIT.java index 8f00eeccf..ab9dbbf25 100644 --- a/cayenne/src/test/java/org/apache/cayenne/query/SelectByIdIteratedQueryIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/query/SelectByIdIteratedQueryIT.java @@ -39,6 +39,7 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +@SuppressWarnings("removal") // dedicated coverage for the deprecated SelectById public class SelectByIdIteratedQueryIT { @RegisterExtension 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 98c27e287..157c41525 100644 --- a/cayenne/src/test/java/org/apache/cayenne/query/SelectByIdTest.java +++ b/cayenne/src/test/java/org/apache/cayenne/query/SelectByIdTest.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; +@SuppressWarnings("removal") // dedicated coverage for the deprecated SelectById public class SelectByIdTest { @Test 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 b63ff2694..5a7605861 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 @@ -39,6 +39,7 @@ import java.util.Map; import static java.util.Collections.singletonMap; import static org.junit.jupiter.api.Assertions.*; +@SuppressWarnings("removal") // dedicated coverage for the deprecated SelectById public class SelectById_RunIT { @RegisterExtension diff --git a/cayenne/src/test/java/org/apache/cayenne/value/json/JsonTypeIT.java b/cayenne/src/test/java/org/apache/cayenne/value/json/JsonTypeIT.java index 1b7eb272b..0f7e6ab5f 100644 --- a/cayenne/src/test/java/org/apache/cayenne/value/json/JsonTypeIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/value/json/JsonTypeIT.java @@ -19,7 +19,7 @@ package org.apache.cayenne.value.json; -import org.apache.cayenne.query.SelectById; +import org.apache.cayenne.query.ObjectSelect; import org.apache.cayenne.testdo.json.JsonOther; import org.apache.cayenne.testdo.json.JsonVarchar; import org.apache.cayenne.unit.CayenneProjects; @@ -660,7 +660,8 @@ public class JsonTypeIT { jsonInsert.setData(new Json(jsonString)); env.context().commitChanges(); - JsonOther jsonSelect = env.context().selectOne(SelectById.query(JsonOther.class, jsonInsert.getObjectId())); + JsonOther jsonSelect = env.context().selectOne(ObjectSelect.query(JsonOther.class) + .where(JsonOther.SELF.eqId(jsonInsert.getObjectId()))); assertEquals(jsonInsert.getData(), jsonSelect.getData()); } @@ -669,7 +670,8 @@ public class JsonTypeIT { jsonInsert.setData(new Json(jsonString)); env.context().commitChanges(); - JsonVarchar jsonSelect = env.context().selectOne(SelectById.query(JsonVarchar.class, jsonInsert.getObjectId())); + JsonVarchar jsonSelect = env.context().selectOne(ObjectSelect.query(JsonVarchar.class) + .where(JsonVarchar.SELF.eqId(jsonInsert.getObjectId()))); assertEquals(jsonInsert.getData(), jsonSelect.getData()); } }
