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 bfd1a0b50cc407c5bf5d5cb6d84d900c453daafa Author: Andrus Adamchik <[email protected]> AuthorDate: Sun Jul 26 17:58:06 2026 +0300 CAY-2985 DataDomain init and structure cleanup --- .../java/org/apache/cayenne/access/DataDomain.java | 21 +- .../configuration/runtime/CoreModuleExtender.java | 13 ++ .../configuration/runtime/DataDomainProvider.java | 45 ++-- .../cayenne/DataContextMaxIdQualifierIT.java | 244 ++++++++++++--------- 4 files changed, 181 insertions(+), 142 deletions(-) diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java b/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java index 0ff8754d5..3ced1dab4 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java @@ -71,8 +71,8 @@ public class DataDomain implements DataChannel { protected final QueryCache queryCache; protected final EntityResolver entityResolver; + protected final int maxIdQualifierSize; - protected int maxIdQualifierSize; protected List<DataChannelQueryFilter> queryFilters; protected List<DataChannelSyncFilter> syncFilters; protected Map<String, DataNode> nodes; @@ -85,14 +85,17 @@ public class DataDomain implements DataChannel { public DataDomain( String name, + TransactionManager transactionManager, TransactionFactory transactionFactory, DataDomainFlushActionFactory flushActionFactory, AdhocObjectFactory objectFactory, EventManager eventManager, - EntitySorter entitySorter, QueryCache queryCache, - EntityResolver entityResolver + int maxIdQualifierSize, + + EntityResolver entityResolver, + EntitySorter entitySorter ) { this.name = name; @@ -104,6 +107,7 @@ public class DataDomain implements DataChannel { this.entitySorter = entitySorter; this.queryCache = queryCache; this.entityResolver = entityResolver; + this.maxIdQualifierSize = maxIdQualifierSize; this.queryFilters = new CopyOnWriteArrayList<>(); this.syncFilters = new CopyOnWriteArrayList<>(); @@ -588,9 +592,7 @@ public class DataDomain implements DataChannel { * and DISJOINT_BY_ID prefetches and is intended to address database * limitations on the size of SQL statements as well as to cap memory use in * Cayenne when generating such queries. The default is 10000. It can be - * changed either by calling {@link #setMaxIdQualifierSize(int)} or changing - * the value for property - * {@link Constants#MAX_ID_QUALIFIER_SIZE_PROPERTY}. + * changed by setting the {@link Constants#MAX_ID_QUALIFIER_SIZE_PROPERTY} property. * * @since 3.1 */ @@ -598,13 +600,6 @@ public class DataDomain implements DataChannel { return maxIdQualifierSize; } - /** - * @since 3.1 - */ - public void setMaxIdQualifierSize(int maxIdQualifierSize) { - this.maxIdQualifierSize = maxIdQualifierSize; - } - TransactionManager getTransactionManager() { return transactionManager; } diff --git a/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/CoreModuleExtender.java b/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/CoreModuleExtender.java index 811f0876e..20d9bc9ef 100644 --- a/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/CoreModuleExtender.java +++ b/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/CoreModuleExtender.java @@ -134,6 +134,19 @@ public class CoreModuleExtender { return this; } + /** + * Sets the maximum number of object IDs to match in a single query for queries that select objects based on a + * collection of ObjectIds, such as paginated queries and DISJOINT_BY_ID prefetches. Zero or negative value means + * no limit. + * + * @param size max number of IDs per generated query + * @since 5.0 + */ + public CoreModuleExtender maxIdQualifierSize(int size) { + contributeProperties().put(Constants.MAX_ID_QUALIFIER_SIZE_PROPERTY, Integer.toString(size)); + return this; + } + /** * Adds a custom project location. */ diff --git a/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/DataDomainProvider.java b/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/DataDomainProvider.java index 31f4b4d0f..8ce33a81c 100644 --- a/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/DataDomainProvider.java +++ b/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/DataDomainProvider.java @@ -158,18 +158,18 @@ public class DataDomainProvider implements Provider<DataDomain> { flushActionFactory, objectFactory, eventManager, - entitySorter, new NestedQueryCache(queryCache), - entityResolver); - - domain.setMaxIdQualifierSize(runtimeProperties.getInt(Constants.MAX_ID_QUALIFIER_SIZE_PROPERTY, -1)); + runtimeProperties.getInt(Constants.MAX_ID_QUALIFIER_SIZE_PROPERTY, -1), + entityResolver, + entitySorter + ); Map<String, String> properties = descriptor.getProperties(); boolean sharedCache = "true".equals(properties.getOrDefault(SHARED_CACHE_ENABLED_PROPERTY, SHARED_CACHE_ENABLED_DEFAULT)); if (sharedCache) { DataRowStore cache = injector.getInstance(DataRowStoreFactory.class).createDataRowStore(descriptor.getName()); - // TODO: setSharedSnapshotCache mutates cache internally; also DataDomain.setName() does + // TODO: setSharedSnapshotCache mutates cache internally domain.setSharedSnapshotCache(cache); } @@ -180,23 +180,9 @@ public class DataDomainProvider implements Provider<DataDomain> { addDataNode(domain, nodeDescriptor); } - // init default node - DataNode defaultNode = null; - - if (descriptor.getDefaultNodeName() != null) { - defaultNode = domain.getDataNode(descriptor.getDefaultNodeName()); - } - - if (defaultNode == null) { - Collection<DataNode> allNodes = domain.getDataNodes(); - if (allNodes.size() == 1) { - defaultNode = allNodes.iterator().next(); - } - } - + DataNode defaultNode = resolveDefaultNode(domain, descriptor); if (defaultNode != null) { LOGGER.info("setting DataNode '{}' as default, used by all unlinked DataMaps", defaultNode.getName()); - domain.setDefaultNode(defaultNode); } @@ -215,6 +201,25 @@ public class DataDomainProvider implements Provider<DataDomain> { return domain; } + /** + * Returns a DataNode to use for DataMaps not linked to any node, either the one named in the descriptor, or the + * only node of the domain. Returns null if neither applies. + * + * @since 5.0 + */ + protected DataNode resolveDefaultNode(DataDomain domain, DataChannelDescriptor descriptor) { + + if (descriptor.getDefaultNodeName() != null) { + DataNode namedNode = domain.getDataNode(descriptor.getDefaultNodeName()); + if (namedNode != null) { + return namedNode; + } + } + + Collection<DataNode> allNodes = domain.getDataNodes(); + return allNodes.size() == 1 ? allNodes.iterator().next() : null; + } + protected EntityResolver createEntityResolver(DataChannelDescriptor descriptor) { EntityResolver entityResolver = new EntityResolver(); diff --git a/cayenne/src/test/java/org/apache/cayenne/DataContextMaxIdQualifierIT.java b/cayenne/src/test/java/org/apache/cayenne/DataContextMaxIdQualifierIT.java index b80eb668f..c8523641b 100644 --- a/cayenne/src/test/java/org/apache/cayenne/DataContextMaxIdQualifierIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/DataContextMaxIdQualifierIT.java @@ -18,17 +18,16 @@ ****************************************************************/ package org.apache.cayenne; -import org.apache.cayenne.access.DataContext; +import org.apache.cayenne.configuration.runtime.CoreModule; import org.apache.cayenne.query.ObjectSelect; -import org.apache.cayenne.runtime.CayenneRuntime; 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.extension.RegisterExtension; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.sql.SQLException; import java.sql.Types; @@ -37,161 +36,188 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +/** + * "maxIdQualifierSize" is immutable and comes from the runtime properties, so each size under test needs its own + * stack. + */ public class DataContextMaxIdQualifierIT { - @RegisterExtension - static final CayenneTestsEnv env = CayenneTestsEnv.forProject(CayenneProjects.TESTMAP_PROJECT); - - protected DataContext context; - protected CayenneRuntime runtime; - - private TableHelper tArtist; - private TableHelper tPainting; - - @BeforeEach - public void setUp() throws Exception { - context = env.context(); - runtime = env.runtime(); - tArtist = env.table("ARTIST", "ARTIST_ID", "ARTIST_NAME"); - - tPainting = env.table("PAINTING").setColumns("PAINTING_ID", "ARTIST_ID", "PAINTING_TITLE").setColumnTypes(Types.INTEGER, Types.BIGINT, - Types.VARCHAR); + private static CayenneTestsEnv envWithMaxIdQualifierSize(int size) { + return CayenneTestsEnv.forProject(CayenneProjects.TESTMAP_PROJECT) + .withExtraModules(b -> CoreModule.extend(b).maxIdQualifierSize(size)); } - private void insertData() throws SQLException { - + private static void insert100ArtistsWithAPaintingEach(CayenneTestsEnv env) throws SQLException { + TableHelper tArtist = artistTable(env); + TableHelper tPainting = paintingTable(env); + for (int i = 1; i <= 100; i++) { tArtist.insert(i, "AA" + i); tPainting.insert(i, i, "P" + i); } } - private void insertData_OneBag_100Boxes() throws SQLException { - tArtist.insert(1, "AA1"); + private static void insertOneArtistWith100Paintings(CayenneTestsEnv env) throws SQLException { + artistTable(env).insert(1, "AA1"); + TableHelper tPainting = paintingTable(env); for (int i = 1; i <= 100; i++) { tPainting.insert(i, 1, "P" + i); } } - @Test - public void disjointByIdPrefetch() throws Exception { - insertData(); - runtime.getDataDomain().setMaxIdQualifierSize(10); - - int queriesCount = env.runWithQueryCounter(() -> - ObjectSelect.query(Artist.class) - .prefetch(Artist.PAINTING_ARRAY.disjointById()) - .select(context)); - - assertEquals(11, queriesCount); + private static TableHelper artistTable(CayenneTestsEnv env) { + return env.table("ARTIST", "ARTIST_ID", "ARTIST_NAME"); } - @Test - public void disjointByIdPrefetch_Zero() throws Exception { - insertData(); - runtime.getDataDomain().setMaxIdQualifierSize(0); + private static TableHelper paintingTable(CayenneTestsEnv env) { + return env.table("PAINTING") + .setColumns("PAINTING_ID", "ARTIST_ID", "PAINTING_TITLE") + .setColumnTypes(Types.INTEGER, Types.BIGINT, Types.VARCHAR); + } - int queriesCount = env.runWithQueryCounter(() -> - ObjectSelect.query(Artist.class) - .prefetch(Artist.PAINTING_ARRAY.disjointById()) - .select(context)); + @Nested + public class Size10 { - assertEquals(2, queriesCount); - } + @RegisterExtension + final CayenneTestsEnv env = envWithMaxIdQualifierSize(10); - @Test - public void disjointByIdPrefetch_Negative() throws Exception { - insertData(); - runtime.getDataDomain().setMaxIdQualifierSize(-1); + @Test + public void disjointByIdPrefetch() throws Exception { + insert100ArtistsWithAPaintingEach(env); - int queriesCount = env.runWithQueryCounter(() -> - ObjectSelect.query(Artist.class) - .prefetch(Artist.PAINTING_ARRAY.disjointById()) - .select(context)); + int queriesCount = env.runWithQueryCounter(() -> + ObjectSelect.query(Artist.class) + .prefetch(Artist.PAINTING_ARRAY.disjointById()) + .select(env.context())); - assertEquals(2, queriesCount); + assertEquals(11, queriesCount); + } } - @Test - public void incrementalFaultList_Lower() throws Exception { - insertData_OneBag_100Boxes(); + @Nested + public class Size5 { - runtime.getDataDomain().setMaxIdQualifierSize(5); + @RegisterExtension + final CayenneTestsEnv env = envWithMaxIdQualifierSize(5); - ObjectSelect<Painting> query = ObjectSelect.query(Painting.class).pageSize(10); + @Test + public void incrementalFaultList() throws Exception { + insertOneArtistWith100Paintings(env); - int queriesCount = env.runWithQueryCounter(() -> { - List<Painting> boxes = query.select(context); - for (Painting box : boxes) { - box.getToArtist(); - } - }); + ObjectSelect<Painting> query = ObjectSelect.query(Painting.class).pageSize(10); - assertEquals(21, queriesCount); + int queriesCount = env.runWithQueryCounter(() -> { + List<Painting> paintings = query.select(env.context()); + for (Painting painting : paintings) { + painting.getToArtist(); + } + }); - queriesCount = env.runWithQueryCounter(() -> { - List<Painting> boxes = query.select(context); - List<Painting> tempList = new ArrayList<>(boxes); - }); + assertEquals(21, queriesCount); - assertEquals(21, queriesCount); + queriesCount = env.runWithQueryCounter(() -> { + List<Painting> paintings = query.select(env.context()); + List<Painting> tempList = new ArrayList<>(paintings); + }); + + assertEquals(21, queriesCount); + } } - @Test - public void incrementalFaultList_Higher() throws Exception { - insertData_OneBag_100Boxes(); + @Nested + public class Size101 { + + @RegisterExtension + final CayenneTestsEnv env = envWithMaxIdQualifierSize(101); - runtime.getDataDomain().setMaxIdQualifierSize(101); + @Test + public void incrementalFaultList() throws Exception { + insertOneArtistWith100Paintings(env); - ObjectSelect<Painting> query = ObjectSelect.query(Painting.class).pageSize(10); + ObjectSelect<Painting> query = ObjectSelect.query(Painting.class).pageSize(10); - int queriesCount = env.runWithQueryCounter(() -> { - final List<Painting> boxes = query.select(context); - for (Painting box : boxes) { - box.getToArtist(); - } - }); + int queriesCount = env.runWithQueryCounter(() -> { + List<Painting> paintings = query.select(env.context()); + for (Painting painting : paintings) { + painting.getToArtist(); + } + }); - assertEquals(11, queriesCount); + assertEquals(11, queriesCount); - queriesCount = env.runWithQueryCounter(() -> { - List<Painting> boxes = query.select(context); - List<Painting> tempList = new ArrayList<>(boxes); - }); + queriesCount = env.runWithQueryCounter(() -> { + List<Painting> paintings = query.select(env.context()); + List<Painting> tempList = new ArrayList<>(paintings); + }); - assertEquals(2, queriesCount); + assertEquals(2, queriesCount); + } } - @Test - public void incrementalFaultList_Zero() throws Exception { - insertData_OneBag_100Boxes(); + @Nested + public class SizeZero { + + @RegisterExtension + final CayenneTestsEnv env = envWithMaxIdQualifierSize(0); - runtime.getDataDomain().setMaxIdQualifierSize(0); + @Test + public void disjointByIdPrefetch() throws Exception { + insert100ArtistsWithAPaintingEach(env); - ObjectSelect<Painting> query = ObjectSelect.query(Painting.class).pageSize(10); + int queriesCount = env.runWithQueryCounter(() -> + ObjectSelect.query(Artist.class) + .prefetch(Artist.PAINTING_ARRAY.disjointById()) + .select(env.context())); + + assertEquals(2, queriesCount); + } - int queriesCount = env.runWithQueryCounter(() -> { - final List<Painting> boxes = query.select(context); - List<Painting> tempList = new ArrayList<>(boxes); - }); + @Test + public void incrementalFaultList() throws Exception { + insertOneArtistWith100Paintings(env); - assertEquals(2, queriesCount); + ObjectSelect<Painting> query = ObjectSelect.query(Painting.class).pageSize(10); + + int queriesCount = env.runWithQueryCounter(() -> { + List<Painting> paintings = query.select(env.context()); + List<Painting> tempList = new ArrayList<>(paintings); + }); + + assertEquals(2, queriesCount); + } } - @Test - public void incrementalFaultList_Negative() throws Exception { - insertData_OneBag_100Boxes(); + @Nested + public class SizeNegative { - runtime.getDataDomain().setMaxIdQualifierSize(-1); + @RegisterExtension + final CayenneTestsEnv env = envWithMaxIdQualifierSize(-1); - ObjectSelect<Painting> query = ObjectSelect.query(Painting.class).pageSize(10); + @Test + public void disjointByIdPrefetch() throws Exception { + insert100ArtistsWithAPaintingEach(env); - int queriesCount = env.runWithQueryCounter(() -> { - final List<Painting> boxes = query.select(context); - List<Painting> tempList = new ArrayList<>(boxes); - }); + int queriesCount = env.runWithQueryCounter(() -> + ObjectSelect.query(Artist.class) + .prefetch(Artist.PAINTING_ARRAY.disjointById()) + .select(env.context())); - assertEquals(2, queriesCount); + assertEquals(2, queriesCount); + } + + @Test + public void incrementalFaultList() throws Exception { + insertOneArtistWith100Paintings(env); + + ObjectSelect<Painting> query = ObjectSelect.query(Painting.class).pageSize(10); + + int queriesCount = env.runWithQueryCounter(() -> { + List<Painting> paintings = query.select(env.context()); + List<Painting> tempList = new ArrayList<>(paintings); + }); + + assertEquals(2, queriesCount); + } } }
