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 25dbec28a7cc1aebe6da77fc4fa0663f260a768d Author: Andrus Adamchik <[email protected]> AuthorDate: Sun Jul 26 17:11:44 2026 +0300 EntitySorter refactoring: 1. Created via a factory explicitly. I would rather we have all services as singletons in CoreModule 2. No more lazy reindex. Just do it explicitly 3. EntityResolver should not hold EntitySorter (circlula relationship). Let the DataNode own it instead --- .../java/org/apache/cayenne/access/DataDomain.java | 19 ++--- .../java/org/apache/cayenne/access/DataNode.java | 21 ++++++ .../org/apache/cayenne/access/DbGenerator.java | 3 +- .../apache/cayenne/access/jdbc/BatchAction.java | 2 +- .../cayenne/ashwood/AshwoodEntitySorter.java | 81 ++++++---------------- .../ashwood/AshwoodEntitySorterFactory.java | 36 ++++++++++ .../ashwood/WeightedAshwoodEntitySorter.java | 27 ++++---- .../cayenne/configuration/runtime/CoreModule.java | 9 +-- .../configuration/runtime/DataDomainProvider.java | 14 ++-- .../org/apache/cayenne/map/EntityResolver.java | 23 ------ .../java/org/apache/cayenne/map/EntitySorter.java | 10 +-- .../apache/cayenne/map/EntitySorterFactory.java | 33 +++++++++ .../org/apache/cayenne/access/MockDataNode.java | 11 +++ .../cayenne/ashwood/AshwoodEntitySorterIT.java | 3 +- .../ashwood/AshwoodEntitySorter_InheritanceIT.java | 3 +- .../AshwoodEntitySorter_RelationshipsIT.java | 3 +- .../ashwood/WeightedAshwoodEntitySorterIT.java | 3 +- .../org/apache/cayenne/unit/DbSchemaManager.java | 4 +- 18 files changed, 163 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 2e2cd7fc9..0ff8754d5 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java @@ -60,7 +60,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * transparently routed to an appropriate DataNode. */ public class DataDomain implements DataChannel { - + protected final String name; protected final TransactionManager transactionManager; protected final TransactionFactory transactionFactory; @@ -109,13 +109,10 @@ public class DataDomain implements DataChannel { this.syncFilters = new CopyOnWriteArrayList<>(); this.nodesByDataMapName = new ConcurrentHashMap<>(); this.nodes = new ConcurrentHashMap<>(); - - refreshEntitySorter(); } /** - * Checks that Domain is not stopped. Throws DomainStoppedException - * otherwise. + * Throws DomainStoppedException if the domain was previously shut down. * * @since 3.0 */ @@ -208,7 +205,7 @@ public class DataDomain implements DataChannel { public void addDataMap(DataMap dataMap) { getEntityResolver().addDataMap(dataMap); - refreshEntitySorter(); + entitySorter.reindex(); } /** @@ -240,7 +237,7 @@ public class DataDomain implements DataChannel { // remove from EntityResolver getEntityResolver().removeDataMap(map); - refreshEntitySorter(); + entitySorter.reindex(); } /** @@ -252,6 +249,7 @@ public class DataDomain implements DataChannel { DataNode removed = nodes.remove(nodeName); if (removed != null) { removed.setEntityResolver(null); + removed.setEntitySorter(null); nodesByDataMapName.values().removeIf(dataNode -> dataNode == removed); } } @@ -279,6 +277,7 @@ public class DataDomain implements DataChannel { // add node to name->node map nodes.put(node.getName(), node); node.setEntityResolver(getEntityResolver()); + node.setEntitySorter(entitySorter); // add node to "ent name->node" map for (DataMap map : node.getDataMaps()) { @@ -450,12 +449,6 @@ public class DataDomain implements DataChannel { return queryCache; } - void refreshEntitySorter() { - if (entitySorter != null) { - entitySorter.setEntityResolver(getEntityResolver()); - } - } - /** * Returns an unmodifiable list of query filters registered with this DataDomain. * <p> diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataNode.java b/cayenne/src/main/java/org/apache/cayenne/access/DataNode.java index 36f0522b8..4072ecd5d 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/DataNode.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/DataNode.java @@ -32,6 +32,7 @@ import org.apache.cayenne.log.NoopSQLLogger; import org.apache.cayenne.log.SQLLogger; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.EntityResolver; +import org.apache.cayenne.map.EntitySorter; import org.apache.cayenne.query.DeleteBatchQuery; import org.apache.cayenne.query.InsertBatchQuery; import org.apache.cayenne.query.Query; @@ -61,6 +62,7 @@ public class DataNode { protected DbAdapter adapter; protected String dataSourceFactory; protected EntityResolver entityResolver; + protected EntitySorter entitySorter; protected SchemaUpdateStrategy schemaUpdateStrategy; protected Map<String, DataMap> dataMaps; @@ -301,6 +303,25 @@ public class DataNode { this.entityResolver = entityResolver; } + /** + * Returns an EntitySorter that sorts the entities of this node based on their mutual dependencies. + * + * @since 5.0 + */ + public EntitySorter getEntitySorter() { + return entitySorter; + } + + /** + * Sets EntitySorter. DataNode relies on an externally set sorter, so if the node is created outside of the + * DataDomain stack, a valid EntitySorter must be provided explicitly. + * + * @since 5.0 + */ + public void setEntitySorter(EntitySorter entitySorter) { + this.entitySorter = entitySorter; + } + @Override public String toString() { return new ToStringBuilder(this).append("name", getName()).toString(); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DbGenerator.java b/cayenne/src/main/java/org/apache/cayenne/access/DbGenerator.java index 074410c40..631bb2e47 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/DbGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/DbGenerator.java @@ -535,8 +535,7 @@ public class DbGenerator { // sort table list if (tables.size() > 1) { - EntitySorter sorter = new AshwoodEntitySorter(); - sorter.setEntityResolver(new EntityResolver(Collections.singleton(map))); + EntitySorter sorter = new AshwoodEntitySorter(new EntityResolver(Collections.singleton(map))); sorter.sortDbEntities(tables, false); } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java index 1f21eeb11..fd4eba604 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java @@ -106,7 +106,7 @@ public class BatchAction extends BaseSQLAction { if (hasGeneratedKeys()) { // turn off batch mode if we generate keys but can't do so in a batch return supportsGeneratedKeys(true) && - !dataNode.getEntityResolver().getEntitySorter().isReflexive(query.getDbEntity()); + !dataNode.getEntitySorter().isReflexive(query.getDbEntity()); } return true; diff --git a/cayenne/src/main/java/org/apache/cayenne/ashwood/AshwoodEntitySorter.java b/cayenne/src/main/java/org/apache/cayenne/ashwood/AshwoodEntitySorter.java index 942f39b0a..7e206e83f 100644 --- a/cayenne/src/main/java/org/apache/cayenne/ashwood/AshwoodEntitySorter.java +++ b/cayenne/src/main/java/org/apache/cayenne/ashwood/AshwoodEntitySorter.java @@ -48,6 +48,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Function; /** @@ -59,56 +60,32 @@ import java.util.function.Function; */ public class AshwoodEntitySorter implements EntitySorter { - protected EntityResolver entityResolver; - protected Map<DbEntity, ComponentRecord> components; - protected Map<DbEntity, List<DbRelationship>> reflexiveDbEntities; + protected final EntityResolver entityResolver; + + protected volatile Map<DbEntity, ComponentRecord> components; + protected volatile Map<DbEntity, List<DbRelationship>> reflexiveDbEntities; protected Comparator<DbEntity> dbEntityComparator; protected Comparator<ObjEntity> objEntityComparator; - private volatile boolean dirty; - - public AshwoodEntitySorter() { - dbEntityComparator = new DbEntityComparator(); - objEntityComparator = new ObjEntityComparator(); - dirty = true; - } + public AshwoodEntitySorter(EntityResolver entityResolver) { + this.entityResolver = Objects.requireNonNull(entityResolver); + this.dbEntityComparator = new DbEntityComparator(); + this.objEntityComparator = new ObjEntityComparator(); - /** - * Reindexes internal sorter in a thread-safe manner. - */ - protected void indexSorter() { - - // correct double check locking per Joshua Bloch - // http://java.sun.com/developer/technicalArticles/Interviews/bloch_effective_08_qa.html - // (maybe we should use something like CountDownLatch or a Cyclic - // barrier - // instead?) - - boolean localDirty = dirty; - if (localDirty) { - synchronized (this) { - localDirty = dirty; - if (localDirty) { - doIndexSorter(); - dirty = false; - } - } - } + reindex(); } /** - * Reindexes internal sorter without synchronization. + * @since 5.0 */ - protected void doIndexSorter() { - + @Override + public synchronized void reindex() { Map<DbEntity, List<DbRelationship>> reflexiveDbEntities = new HashMap<>(); Digraph<DbEntity, List<DbAttribute>> referentialDigraph = new MapDigraph<>(); - if (entityResolver != null) { - for (DbEntity entity : entityResolver.getDbEntities()) { - referentialDigraph.addVertex(entity); - } + for (DbEntity entity : entityResolver.getDbEntities()) { + referentialDigraph.addVertex(entity); } for (DbEntity destination : entityResolver.getDbEntities()) { @@ -164,43 +141,30 @@ public class AshwoodEntitySorter implements EntitySorter { this.components = components; } - /** - * @since 3.1 - */ - @Override - public void setEntityResolver(EntityResolver entityResolver) { - this.entityResolver = entityResolver; - this.entityResolver.setEntitySorter(this); - this.dirty = true; - } - @Override public void sortDbEntities(List<DbEntity> dbEntities, boolean deleteOrder) { - indexSorter(); dbEntities.sort(getDbEntityComparator(deleteOrder)); } @Override public void sortObjEntities(List<ObjEntity> objEntities, boolean deleteOrder) { - indexSorter(); objEntities.sort(getObjEntityComparator(deleteOrder)); } @SuppressWarnings("unchecked") @Override public void sortObjectsForEntity(ObjEntity objEntity, List<?> objects, boolean deleteOrder) { - if(objects == null || objects.size() == 0) { + if(objects == null || objects.isEmpty()) { return; } - indexSorter(); DbEntity dbEntity = objEntity.getDbEntity(); // if no sorting is required if (!isReflexive(dbEntity)) { return; } - Object probe = objects.get(0); + Object probe = objects.getFirst(); if (probe instanceof DbRowOp) { sortObjectsForEntity(objEntity, (List<DbRowOp>) objects, deleteOrder, DbRowOp::getObject); } else if(probe instanceof Persistent) { @@ -220,7 +184,7 @@ public class AshwoodEntitySorter implements EntitySorter { } protected <E> Digraph<E, Boolean> buildDigraph(ObjEntity objEntity, List<E> objects, Function<E, Persistent> converter) { - EntityResolver resolver = converter.apply(objects.get(0)).getObjectContext().getEntityResolver(); + EntityResolver resolver = converter.apply(objects.getFirst()).getObjectContext().getEntityResolver(); ClassDescriptor descriptor = resolver.getClassDescriptor(objEntity.getName()); String[] reflexiveRelNames = getReflexiveRelationshipsNames(objEntity); @@ -307,7 +271,7 @@ public class AshwoodEntitySorter implements EntitySorter { protected Persistent findReflexiveMaster(Persistent object, ObjRelationship toOneRel, String targetEntityName) { - DbRelationship finalRel = toOneRel.getDbRelationships().get(0); + DbRelationship finalRel = toOneRel.getDbRelationships().getFirst(); ObjectContext context = object.getObjectContext(); // find committed snapshot - so we can't fetch from the context as it will return dirty snapshot; @@ -321,11 +285,11 @@ public class AshwoodEntitySorter implements EntitySorter { ObjectIdQuery query = new ObjectIdQuery(object.getObjectId(), true, ObjectIdQuery.CACHE); QueryResponse response = context.getParent().onQuery(null, query); List<?> result = response.firstList(); - if (result == null || result.size() == 0) { + if (result == null || result.isEmpty()) { return null; } - DataRow snapshot = (DataRow) result.get(0); + DataRow snapshot = (DataRow) result.getFirst(); ObjectId id = snapshot.createTargetObjectId(targetEntityName, finalRel); @@ -336,13 +300,11 @@ public class AshwoodEntitySorter implements EntitySorter { @Override public Comparator<DbEntity> getDbEntityComparator() { - indexSorter(); return dbEntityComparator; } @Override public Comparator<ObjEntity> getObjEntityComparator() { - indexSorter(); return objEntityComparator; } @@ -364,7 +326,6 @@ public class AshwoodEntitySorter implements EntitySorter { @Override public boolean isReflexive(DbEntity metadata) { - indexSorter(); return reflexiveDbEntities.containsKey(metadata); } diff --git a/cayenne/src/main/java/org/apache/cayenne/ashwood/AshwoodEntitySorterFactory.java b/cayenne/src/main/java/org/apache/cayenne/ashwood/AshwoodEntitySorterFactory.java new file mode 100644 index 000000000..fba1da39f --- /dev/null +++ b/cayenne/src/main/java/org/apache/cayenne/ashwood/AshwoodEntitySorterFactory.java @@ -0,0 +1,36 @@ +/***************************************************************** + * 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.ashwood; + +import org.apache.cayenne.map.EntityResolver; +import org.apache.cayenne.map.EntitySorter; +import org.apache.cayenne.map.EntitySorterFactory; + +/** + * A factory of {@link AshwoodEntitySorter}. + * + * @since 5.0 + */ +public class AshwoodEntitySorterFactory implements EntitySorterFactory { + + @Override + public EntitySorter createEntitySorter(EntityResolver entityResolver) { + return new AshwoodEntitySorter(entityResolver); + } +} diff --git a/cayenne/src/main/java/org/apache/cayenne/ashwood/WeightedAshwoodEntitySorter.java b/cayenne/src/main/java/org/apache/cayenne/ashwood/WeightedAshwoodEntitySorter.java index 855983e76..d435c2753 100644 --- a/cayenne/src/main/java/org/apache/cayenne/ashwood/WeightedAshwoodEntitySorter.java +++ b/cayenne/src/main/java/org/apache/cayenne/ashwood/WeightedAshwoodEntitySorter.java @@ -18,14 +18,14 @@ ****************************************************************/ package org.apache.cayenne.ashwood; -import java.util.Collections; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.EntityResolver; +import org.apache.cayenne.map.ObjEntity; + import java.util.Comparator; import java.util.HashMap; import java.util.Map; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.ObjEntity; - /** * EntitySorter that takes into account entity "weights", and otherwise delegating to * another (topological) sorter. @@ -34,20 +34,23 @@ import org.apache.cayenne.map.ObjEntity; */ public class WeightedAshwoodEntitySorter extends AshwoodEntitySorter { - private Comparator<DbEntity> weightedDbEntityComparator; - private Comparator<ObjEntity> weightedObjEntityComparator; + private final Comparator<DbEntity> weightedDbEntityComparator; + private final Comparator<ObjEntity> weightedObjEntityComparator; + + protected volatile Map<DbEntity, Integer> entityWeights; - protected Map<DbEntity, Integer> entityWeights; + public WeightedAshwoodEntitySorter(EntityResolver entityResolver) { + super(entityResolver); - public WeightedAshwoodEntitySorter() { + // the comparators are stateless, reading the sorter fields only when actually comparing, so creating them + // after super() (i.e. after the initial indexing) is safe this.weightedDbEntityComparator = new WeightedDbEntityComparator(); this.weightedObjEntityComparator = new WeightedObjEntityComparator(); - this.entityWeights = Collections.emptyMap(); } @Override - protected void doIndexSorter() { - super.doIndexSorter(); + public synchronized void reindex() { + super.reindex(); entityWeights = new HashMap<>(); @@ -66,7 +69,6 @@ public class WeightedAshwoodEntitySorter extends AshwoodEntitySorter { } } - @SuppressWarnings("unchecked") @Override protected Comparator<DbEntity> getDbEntityComparator(boolean dependantFirst) { Comparator<DbEntity> c = weightedDbEntityComparator; @@ -76,7 +78,6 @@ public class WeightedAshwoodEntitySorter extends AshwoodEntitySorter { return c; } - @SuppressWarnings("unchecked") @Override protected Comparator<ObjEntity> getObjEntityComparator(boolean dependantFirst) { Comparator<ObjEntity> c = weightedObjEntityComparator; diff --git a/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/CoreModule.java b/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/CoreModule.java index dc785a235..185348de9 100644 --- a/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/CoreModule.java +++ b/cayenne/src/main/java/org/apache/cayenne/configuration/runtime/CoreModule.java @@ -50,7 +50,7 @@ import org.apache.cayenne.access.translator.sqltemplate.DefaultSQLTemplateTransl import org.apache.cayenne.access.translator.sqltemplate.DefaultTemplateContextFactory; import org.apache.cayenne.access.translator.sqltemplate.TemplateContextFactory; import org.apache.cayenne.access.types.*; -import org.apache.cayenne.ashwood.AshwoodEntitySorter; +import org.apache.cayenne.ashwood.AshwoodEntitySorterFactory; import org.apache.cayenne.cache.MapQueryCacheProvider; import org.apache.cayenne.cache.QueryCache; import org.apache.cayenne.commitlog.CommitLogFilter; @@ -126,7 +126,7 @@ import org.apache.cayenne.event.EventManagerProvider; import org.apache.cayenne.event.NoopEventBridgeProvider; import org.apache.cayenne.log.SQLLogger; import org.apache.cayenne.log.Slf4jSQLLogger; -import org.apache.cayenne.map.EntitySorter; +import org.apache.cayenne.map.EntitySorterFactory; import org.apache.cayenne.reflect.generic.DefaultValueComparisonStrategyFactory; import org.apache.cayenne.reflect.generic.ValueComparisonStrategyFactory; import org.apache.cayenne.resource.ClassLoaderResourceLocator; @@ -462,10 +462,7 @@ public class CoreModule implements Module { // a default DBAdapterFactory used to load custom and automatic DbAdapters binder.bind(DbAdapterFactory.class).to(DefaultDbAdapterFactory.class); - - // binding AshwoodEntitySorter without scope, as this is a stateful object and is - // configured by the owning domain - binder.bind(EntitySorter.class).to(AshwoodEntitySorter.class).withoutScope(); + binder.bind(EntitySorterFactory.class).to(AshwoodEntitySorterFactory.class); binder.bind(Key.get(BatchTranslator.class, BatchTranslator.INSERT)).to(InsertBatchTranslator.class); binder.bind(Key.get(BatchTranslator.class, BatchTranslator.UPDATE)).to(UpdateBatchTranslator.class); 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 6c5758b2d..31f4b4d0f 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 @@ -45,6 +45,7 @@ import org.apache.cayenne.event.EventManager; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.EntityResolver; import org.apache.cayenne.map.EntitySorter; +import org.apache.cayenne.map.EntitySorterFactory; import org.apache.cayenne.reflect.generic.ValueComparisonStrategyFactory; import org.apache.cayenne.resource.Resource; import org.apache.cayenne.resource.ResourceLocator; @@ -128,7 +129,7 @@ public class DataDomainProvider implements Provider<DataDomain> { protected EventManager eventManager; @Inject - protected EntitySorter entitySorter; + protected EntitySorterFactory entitySorterFactory; @Override public DataDomain get() throws ConfigurationException { @@ -147,6 +148,8 @@ public class DataDomainProvider implements Provider<DataDomain> { protected DataDomain createAndInitDataDomain() throws Exception { DataChannelDescriptor descriptor = loadDescriptor(); + EntityResolver entityResolver = createEntityResolver(descriptor); + EntitySorter entitySorter = entitySorterFactory.createEntitySorter(entityResolver); DataDomain domain = new DataDomain( descriptor.getName(), @@ -157,11 +160,10 @@ public class DataDomainProvider implements Provider<DataDomain> { eventManager, entitySorter, new NestedQueryCache(queryCache), - createEntityResolver(descriptor)); + entityResolver); domain.setMaxIdQualifierSize(runtimeProperties.getInt(Constants.MAX_ID_QUALIFIER_SIZE_PROPERTY, -1)); - Map<String, String> properties = descriptor.getProperties(); boolean sharedCache = "true".equals(properties.getOrDefault(SHARED_CACHE_ENABLED_PROPERTY, SHARED_CACHE_ENABLED_DEFAULT)); @@ -213,12 +215,6 @@ public class DataDomainProvider implements Provider<DataDomain> { return domain; } - /** - * Creates an EntityResolver fully configured off the descriptor, so that the DataDomain can take it as a - * constructor argument. - * - * @since 5.0 - */ protected EntityResolver createEntityResolver(DataChannelDescriptor descriptor) { EntityResolver entityResolver = new EntityResolver(); diff --git a/cayenne/src/main/java/org/apache/cayenne/map/EntityResolver.java b/cayenne/src/main/java/org/apache/cayenne/map/EntityResolver.java index 89edc83bf..f203e1427 100644 --- a/cayenne/src/main/java/org/apache/cayenne/map/EntityResolver.java +++ b/cayenne/src/main/java/org/apache/cayenne/map/EntityResolver.java @@ -93,17 +93,8 @@ public class EntityResolver implements MappingNamespace, Serializable { protected transient LifecycleCallbackRegistry callbackRegistry; protected transient ValueObjectTypeRegistry valueObjectTypeRegistry; - - /** - * @since 4.2 - */ protected transient ValueComparisonStrategyFactory valueComparisonStrategyFactory; - /** - * @since 4.2 - */ - protected transient EntitySorter entitySorter; - /** * @since 5.0 */ @@ -547,20 +538,6 @@ public class EntityResolver implements MappingNamespace, Serializable { this.valueComparisonStrategyFactory = valueComparisonStrategyFactory; } - /** - * @since 4.2 - */ - public void setEntitySorter(EntitySorter entitySorter) { - this.entitySorter = entitySorter; - } - - /** - * @since 4.2 - */ - public EntitySorter getEntitySorter() { - return entitySorter; - } - /** * @since 5.0 */ diff --git a/cayenne/src/main/java/org/apache/cayenne/map/EntitySorter.java b/cayenne/src/main/java/org/apache/cayenne/map/EntitySorter.java index c29a33bda..6d2b16131 100644 --- a/cayenne/src/main/java/org/apache/cayenne/map/EntitySorter.java +++ b/cayenne/src/main/java/org/apache/cayenne/map/EntitySorter.java @@ -30,12 +30,12 @@ import java.util.List; public interface EntitySorter { /** - * Sets EntityResolver for this sorter. All entities present in the resolver will be - * used to determine sort ordering. - * - * @since 3.1 + * Rebuilds the internal index off the current state of the sorter's EntityResolver. Called when the set of + * entities changes. + * + * @since 5.0 */ - void setEntityResolver(EntityResolver resolver); + void reindex(); /** * Sorts a list of DbEntities. diff --git a/cayenne/src/main/java/org/apache/cayenne/map/EntitySorterFactory.java b/cayenne/src/main/java/org/apache/cayenne/map/EntitySorterFactory.java new file mode 100644 index 000000000..6e3b38f00 --- /dev/null +++ b/cayenne/src/main/java/org/apache/cayenne/map/EntitySorterFactory.java @@ -0,0 +1,33 @@ +/***************************************************************** + * 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.map; + +/** + * Creates {@link EntitySorter} instances. As a sorter is a stateful object indexed off a specific set of entities, it + * can not be shared. + * + * @since 5.0 + */ +public interface EntitySorterFactory { + + /** + * Creates an EntitySorter that sorts the entities of the provided EntityResolver. + */ + EntitySorter createEntitySorter(EntityResolver entityResolver); +} diff --git a/cayenne/src/test/java/org/apache/cayenne/access/MockDataNode.java b/cayenne/src/test/java/org/apache/cayenne/access/MockDataNode.java index db5522043..26d3b614d 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/MockDataNode.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/MockDataNode.java @@ -30,6 +30,7 @@ import javax.sql.DataSource; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.EntityResolver; +import org.apache.cayenne.map.EntitySorter; import org.apache.cayenne.query.Query; public class MockDataNode extends DataNode { @@ -136,6 +137,11 @@ public class MockDataNode extends DataNode { return node.getEntityResolver(); } + @Override + public EntitySorter getEntitySorter() { + return node.getEntitySorter(); + } + @Override public String getName() { return node.getName(); @@ -171,6 +177,11 @@ public class MockDataNode extends DataNode { node.setEntityResolver(entityResolver); } + @Override + public void setEntitySorter(EntitySorter entitySorter) { + node.setEntitySorter(entitySorter); + } + @Override public void setName(String name) { node.setName(name); diff --git a/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorterIT.java b/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorterIT.java index 5662ca992..5d73410a2 100644 --- a/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorterIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorterIT.java @@ -50,8 +50,7 @@ public class AshwoodEntitySorterIT { public void before() { this.resolver = env.context().getEntityResolver(); - this.sorter = new AshwoodEntitySorter(); - sorter.setEntityResolver(resolver); + this.sorter = new AshwoodEntitySorter(resolver); this.artist = resolver.getDbEntity("ARTIST"); this.artistExhibit = resolver.getDbEntity("ARTIST_EXHIBIT"); diff --git a/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorter_InheritanceIT.java b/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorter_InheritanceIT.java index e8164b391..e447cf3ab 100644 --- a/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorter_InheritanceIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorter_InheritanceIT.java @@ -49,8 +49,7 @@ public class AshwoodEntitySorter_InheritanceIT extends PeopleTestBase { context = env.context(); this.resolver = context.getEntityResolver(); - this.sorter = new AshwoodEntitySorter(); - sorter.setEntityResolver(resolver); + this.sorter = new AshwoodEntitySorter(resolver); this.address = resolver.getDbEntity("ADDRESS"); this.clientCompany = resolver.getDbEntity("CLIENT_COMPANY"); diff --git a/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorter_RelationshipsIT.java b/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorter_RelationshipsIT.java index 489186881..44d32ca2f 100644 --- a/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorter_RelationshipsIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorter_RelationshipsIT.java @@ -46,8 +46,7 @@ public class AshwoodEntitySorter_RelationshipsIT { @BeforeEach public void setUp() throws Exception { - this.sorter = new AshwoodEntitySorter(); - sorter.setEntityResolver(env.context().getEntityResolver()); + this.sorter = new AshwoodEntitySorter(env.context().getEntityResolver()); tRelationshipHelper = env.table("RELATIONSHIP_HELPER", "RELATIONSHIP_HELPER_ID", "NAME"); diff --git a/cayenne/src/test/java/org/apache/cayenne/ashwood/WeightedAshwoodEntitySorterIT.java b/cayenne/src/test/java/org/apache/cayenne/ashwood/WeightedAshwoodEntitySorterIT.java index b8c704edf..83b855e4d 100644 --- a/cayenne/src/test/java/org/apache/cayenne/ashwood/WeightedAshwoodEntitySorterIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/ashwood/WeightedAshwoodEntitySorterIT.java @@ -54,8 +54,7 @@ public class WeightedAshwoodEntitySorterIT { List<DbEntity> e2 = Arrays.asList(resolver.getDbEntity("SORT_DEP"), resolver.getDbEntity("SORT_ROOT")); - WeightedAshwoodEntitySorter sorter = new WeightedAshwoodEntitySorter(); - sorter.setEntityResolver(resolver); + WeightedAshwoodEntitySorter sorter = new WeightedAshwoodEntitySorter(resolver); sorter.sortDbEntities(e1, false); assertEquals(eSorted, e1); diff --git a/cayenne/src/test/java/org/apache/cayenne/unit/DbSchemaManager.java b/cayenne/src/test/java/org/apache/cayenne/unit/DbSchemaManager.java index ef4b8cc97..314e42603 100644 --- a/cayenne/src/test/java/org/apache/cayenne/unit/DbSchemaManager.java +++ b/cayenne/src/test/java/org/apache/cayenne/unit/DbSchemaManager.java @@ -89,8 +89,8 @@ public class DbSchemaManager { filterDataMap(map); } - // TODO: suspect - domain.getEntitySorter().setEntityResolver(domain.getEntityResolver()); + // "filterDataMap" above mutated the maps, so the sorter index is stale + domain.getEntitySorter().reindex(); this.dataMapsInSchemaSetupOrder = sortDataMapsInSchemaSetupOrder(); }
