2121
2222import org .apache .cayenne .access .DataDomain ;
2323import org .apache .cayenne .access .DataNode ;
24- import org .apache .cayenne .access .DbGenerator ;
25- import org .apache .cayenne .access .dbsync .SkipSchemaUpdateStrategy ;
26- import org .apache .cayenne .access .jdbc .reader .DefaultRowReaderFactory ;
27- import org .apache .cayenne .access .translator .batch .DefaultBatchTranslatorFactory ;
28- import org .apache .cayenne .access .translator .select .DefaultSelectTranslatorFactory ;
29- import org .apache .cayenne .ashwood .AshwoodEntitySorter ;
30- import org .apache .cayenne .cache .MapQueryCache ;
31- import org .apache .cayenne .configuration .DataMapLoader ;
3224import org .apache .cayenne .dba .DbAdapter ;
33- import org .apache .cayenne .event .DefaultEventManager ;
34- import org .apache .cayenne .log .JdbcEventLogger ;
3525import org .apache .cayenne .map .DataMap ;
3626import org .apache .cayenne .map .DbAttribute ;
3727import org .apache .cayenne .map .DbEntity ;
28+ import org .apache .cayenne .map .DbRelationship ;
3829import org .apache .cayenne .map .Procedure ;
39- import org .apache .cayenne .resource . URLResource ;
30+ import org .apache .cayenne .runtime . CayenneRuntime ;
4031import org .apache .cayenne .unit .dba .TestDbAdapter ;
4132import org .slf4j .Logger ;
4233import org .slf4j .LoggerFactory ;
5243import java .util .Collection ;
5344import java .util .Collections ;
5445import java .util .Comparator ;
46+ import java .util .IdentityHashMap ;
5547import java .util .List ;
5648import java .util .ListIterator ;
49+ import java .util .Map ;
5750import java .util .Set ;
58- import java .util .stream .Stream ;
5951
6052public class AllTestsSchemaManager {
6153
6254 private static final Logger LOGGER = LoggerFactory .getLogger (AllTestsSchemaManager .class );
6355
64- private static final String [] MAPS_FOR_SCHEMA_SETUP = {"testmap.map.xml" , "compound.map.xml" ,
65- "misc-types.map.xml" , "things.map.xml" , "numeric-types.map.xml" , "binary-pk.map.xml" , "no-pk.map.xml" ,
66- "lob.map.xml" , "date-time.map.xml" , "enum.map.xml" , "json.map.xml" , "extended-type.map.xml" ,
67- "generated.map.xml" , "mixed-persistence-strategy.map.xml" , "people.map.xml" , "primitive.map.xml" ,
68- "inheritance.map.xml" , "locking.map.xml" , "soft-delete.map.xml" , "empty.map.xml" , "relationships.map.xml" ,
69- "relationships-activity.map.xml" , "relationships-delete-rules.map.xml" ,
70- "relationships-collection-to-many.map.xml" , "relationships-child-master.map.xml" ,
71- "relationships-clob.map.xml" , "relationships-flattened.map.xml" , "relationships-many-to-many-join.map.xml" ,
72- "relationships-set-to-many.map.xml" , "relationships-to-many-fk.map.xml" , "relationships-to-one-fk.map.xml" ,
73- "return-types.map.xml" , "uuid.map.xml" , "multi-tier.map.xml" , "reflexive.map.xml" , "delete-rules.map.xml" ,
74- "lifecycle-callbacks-order.map.xml" , "lifecycles.map.xml" , "map-to-many.map.xml" , "toone.map.xml" ,
75- "meaningful-pk.map.xml" , "table-primitives.map.xml" , "generic.map.xml" , "map-db1.map.xml" ,
76- "map-db2.map.xml" , "embeddable.map.xml" , "qualified.map.xml" , "quoted-identifiers.map.xml" ,
77- "inheritance-single-table1.map.xml" , "inheritance-vertical.map.xml" , "oneway-rels.map.xml" ,
78- "unsupported-distinct-types.map.xml" , "array-type.map.xml" , "cay-2032.map.xml" ,
79- "weighted-sort.map.xml" , "hybrid-data-object.map.xml" , "legacy-date-time.map.xml" , "inheritance-with-enum.map.xml" ,
80- "lazy-attributes.map.xml" , "cay2666/datamap.map.xml" , "cay2641/datamapLazy.map.xml" ,
81- "annotation/datamapAnnotation.map.xml" };
82-
8356 // hardcoded dependent entities that should be excluded if LOBs are not supported
8457 private static final Set <String > EXTRA_EXCLUDED_FOR_NO_LOB = Set .of ("CLOB_DETAIL" );
8558 private static final Set <String > EXTRA_EXCLUDED_FOR_NO_NATIVE_JSON = Set .of ("JSON_OTHER" );
8659
8760 private final DataSource dataSource ;
8861 private final TestDbAdapter testDbAdapter ;
89- private final JdbcEventLogger jdbcEventLogger ;
9062 private final DataDomain domain ;
63+ private final List <DataMap > dataMapsInSchemaSetupOrder ;
9164
92- public AllTestsSchemaManager (
93- DataSource dataSource ,
94- DbAdapter dbAdapter ,
95- JdbcEventLogger jdbcEventLogger ,
96- DataMapLoader loader ) {
65+ public AllTestsSchemaManager (DataSource dataSource ) {
9766
9867 this .dataSource = dataSource ;
99- this .testDbAdapter = TestDbAdapter .of (dbAdapter );
100- this .jdbcEventLogger = jdbcEventLogger ;
101-
102- // TODO: just create a normal CayenneRuntime with all the defaults
103- this .domain = initDomain (loader , dbAdapter );
104- }
105-
106- private DataDomain initDomain (DataMapLoader loader , DbAdapter dbAdapter ) {
107- DataDomain domain = new DataDomain ("temp" );
108- domain .setEventManager (new DefaultEventManager (2 ));
109- domain .setEntitySorter (new AshwoodEntitySorter ());
110- domain .setQueryCache (new MapQueryCache (50 ));
111-
112- Stream .of (MAPS_FOR_SCHEMA_SETUP ).map (m -> getClass ().getClassLoader ().getResource (m ))
113- .map (u -> loader .load (new URLResource (u )))
114- .forEach (m -> domain .addNode (initNode (m , dbAdapter )));
115-
116- return domain ;
117- }
118-
119- private DataNode initNode (DataMap map , DbAdapter dbAdapter ) {
68+ this .domain = CayenneRuntime .builder ()
69+ .addConfig ("cayenne-ALL.xml" )
70+ .dataSource (dataSource )
71+ .build ()
72+ .getDataDomain ();
73+
74+ this .testDbAdapter = TestDbAdapter .of (domain .getDefaultNode ().getAdapter ());
75+
76+ for (DataMap map : domain .getDataMaps ()) {
77+ // tweak mapping with a delegate
78+ for (Procedure proc : map .getProcedures ()) {
79+ testDbAdapter .tweakProcedure (proc );
80+ }
12081
121- DataNode node = new DataNode (map .getName ());
122- node .setJdbcEventLogger (jdbcEventLogger );
123- node .setAdapter (dbAdapter );
124- node .setDataSource (dataSource );
125-
126- // tweak mapping with a delegate
127- for (Procedure proc : map .getProcedures ()) {
128- testDbAdapter .tweakProcedure (proc );
82+ filterDataMap (map );
12983 }
130- filterDataMap (map );
13184
132- node .addDataMap (map );
85+ // TODO: suspect
86+ domain .getEntitySorter ().setEntityResolver (domain .getEntityResolver ());
13387
134- node .setSchemaUpdateStrategy (new SkipSchemaUpdateStrategy ());
135- node .setRowReaderFactory (new DefaultRowReaderFactory ());
136- node .setBatchTranslatorFactory (new DefaultBatchTranslatorFactory ());
137- node .setSelectTranslatorFactory (new DefaultSelectTranslatorFactory ());
138- return node ;
88+ this .dataMapsInSchemaSetupOrder = sortDataMapsInSchemaSetupOrder ();
13989 }
14090
14191 /**
@@ -185,23 +135,25 @@ private void filterDataMap(DataMap map) {
185135 * Drops all test tables.
186136 */
187137 private void dropSchema () throws Exception {
188- for (DataNode node : domain .getDataNodes ()) {
189- dropSchema (node , node .getDataMaps ().iterator ().next ());
138+ ListIterator <DataMap > it = dataMapsInSchemaSetupOrder .listIterator (dataMapsInSchemaSetupOrder .size ());
139+ while (it .hasPrevious ()) {
140+ DataMap map = it .previous ();
141+ dropSchema (domain .lookupDataNode (map ), map );
190142 }
191143 }
192144
193145 /**
194146 * Creates all test tables in the database.
195147 */
196148 private void createSchema () throws Exception {
197- for (DataNode node : domain . getDataNodes () ) {
198- createSchema (node , node . getDataMaps (). iterator (). next () );
149+ for (DataMap map : dataMapsInSchemaSetupOrder ) {
150+ createSchema (domain . lookupDataNode ( map ), map );
199151 }
200152 }
201153
202154 public void dropPKSupport () throws Exception {
203- for (DataNode node : domain . getDataNodes () ) {
204- dropPKSupport (node , node . getDataMaps (). iterator (). next () );
155+ for (DataMap map : dataMapsInSchemaSetupOrder ) {
156+ dropPKSupport (domain . lookupDataNode ( map ), map );
205157 }
206158 }
207159
@@ -211,9 +163,91 @@ public void dropPKSupport() throws Exception {
211163 * objects and data for primary key support.
212164 */
213165 public void createPKSupport () throws Exception {
214- for (DataNode node : domain .getDataNodes ()) {
215- createPKSupport (node , node .getDataMaps ().iterator ().next ());
166+ for (DataMap map : dataMapsInSchemaSetupOrder ) {
167+ createPKSupport (domain .lookupDataNode (map ), map );
168+ }
169+ }
170+
171+ private List <DataMap > sortDataMapsInSchemaSetupOrder () {
172+ List <DataMap > maps = new ArrayList <>(domain .getDataMaps ());
173+ Map <DataMap , List <DataMap >> dependencies = new IdentityHashMap <>();
174+
175+ for (DataMap map : maps ) {
176+ dependencies .put (map , dataMapDependencies (map , maps ));
177+ }
178+
179+ List <DataMap > sorted = new ArrayList <>(maps .size ());
180+ Set <DataMap > visited = Collections .newSetFromMap (new IdentityHashMap <>());
181+ Set <DataMap > visiting = Collections .newSetFromMap (new IdentityHashMap <>());
182+
183+ for (DataMap map : maps ) {
184+ sortDataMap (map , dependencies , visited , visiting , sorted );
185+ }
186+
187+ return Collections .unmodifiableList (sorted );
188+ }
189+
190+ private List <DataMap > dataMapDependencies (DataMap map , List <DataMap > maps ) {
191+ List <DataMap > dependencies = new ArrayList <>();
192+
193+ for (DbEntity entity : map .getDbEntities ()) {
194+ for (DbRelationship relationship : entity .getRelationships ()) {
195+ DataMap targetMap = dataMapDependency (map , relationship );
196+ if (targetMap != null && maps .contains (targetMap ) && !dependencies .contains (targetMap )) {
197+ dependencies .add (targetMap );
198+ }
199+ }
200+ }
201+
202+ return dependencies ;
203+ }
204+
205+ private DataMap dataMapDependency (DataMap sourceMap , DbRelationship relationship ) {
206+ if (relationship .isRuntime () || relationship .isToMany () || !relationship .isToPK () || relationship .isToDependentPK ()) {
207+ return null ;
208+ }
209+
210+ boolean hasUnresolvedJoin = relationship .getJoins ().stream ()
211+ .anyMatch (join -> join .getSource () == null || join .getTarget () == null );
212+ if (hasUnresolvedJoin ) {
213+ return null ;
214+ }
215+
216+ DbEntity targetEntity = relationship .getTargetEntity ();
217+ DataMap targetMap = targetEntity != null ? targetEntity .getDataMap () : null ;
218+ if (targetMap == null || targetMap == sourceMap ) {
219+ return null ;
220+ }
221+
222+ if (domain .lookupDataNode (sourceMap ) != domain .lookupDataNode (targetMap )) {
223+ return null ;
216224 }
225+
226+ return targetMap ;
227+ }
228+
229+ private void sortDataMap (
230+ DataMap map ,
231+ Map <DataMap , List <DataMap >> dependencies ,
232+ Set <DataMap > visited ,
233+ Set <DataMap > visiting ,
234+ List <DataMap > sorted ) {
235+
236+ if (visited .contains (map )) {
237+ return ;
238+ }
239+
240+ if (!visiting .add (map )) {
241+ throw new IllegalStateException ("Cycle in DataMap dependencies involving " + map .getName ());
242+ }
243+
244+ for (DataMap dependency : dependencies .getOrDefault (map , Collections .emptyList ())) {
245+ sortDataMap (dependency , dependencies , visited , visiting , sorted );
246+ }
247+
248+ visiting .remove (map );
249+ visited .add (map );
250+ sorted .add (map );
217251 }
218252
219253 public List <DbEntity > dbEntitiesInInsertOrder (String mapName ) {
@@ -229,7 +263,7 @@ private List<DbEntity> sortedDbEntities(String mapName, boolean deleteOrder) {
229263 // intentionally taking "mapName", not a "map", as we need to resolve the corresponding map in our private
230264 // namespace defined by "domain"
231265
232- DataMap localMap = domain . getDataMap (mapName );
266+ DataMap localMap = dataMap (mapName );
233267 List <DbEntity > entities = new ArrayList <>(localMap .getDbEntities ());
234268 entities .removeAll (excludeEntities (entities ));
235269
@@ -376,8 +410,6 @@ private Collection<String> tableCreateQueries(DataNode node, DataMap map) {
376410 DbAdapter adapter = node .getAdapter ();
377411
378412 List <DbEntity > orderedEntities = dbEntitiesInInsertOrder (map .getName ());
379- List <DbEntity > excludedEntities = excludeEntities (map .getDbEntities ());
380- DbGenerator gen = new DbGenerator (adapter , map , excludedEntities , domain , jdbcEventLogger );
381413 List <String > queries = new ArrayList <>();
382414
383415 // table definitions
@@ -391,10 +423,72 @@ private Collection<String> tableCreateQueries(DataNode node, DataMap map) {
391423 continue ;
392424 }
393425
394- List <String > qs = gen .createConstraintsQueries (ent );
395- queries .addAll (qs );
426+ queries .addAll (createConstraintsQueries (adapter , ent ));
396427 }
397428
398429 return queries ;
399430 }
431+
432+ private List <String > createConstraintsQueries (DbAdapter adapter , DbEntity table ) {
433+ List <String > queries = new ArrayList <>();
434+
435+ for (DbRelationship rel : table .getRelationships ()) {
436+
437+ if (rel .isRuntime ()) {
438+ continue ;
439+ }
440+
441+ if (rel .isToMany ()) {
442+ continue ;
443+ }
444+
445+ DataMap srcMap = rel .getSourceEntity ().getDataMap ();
446+ DataMap targetMap = rel .getTargetEntity ().getDataMap ();
447+
448+ if (srcMap != null && targetMap != null && srcMap != targetMap ) {
449+ continue ;
450+ }
451+
452+ if (rel .isToPK () && !rel .isToDependentPK ()) {
453+ boolean hasUnresolvedJoin = rel .getJoins ().stream ()
454+ .anyMatch (join -> join .getSource () == null || join .getTarget () == null );
455+
456+ if (hasUnresolvedJoin ) {
457+ continue ;
458+ }
459+
460+ if (adapter .supportsUniqueConstraints ()) {
461+ DbRelationship reverse = rel .getReverseRelationship ();
462+ if (reverse != null && !reverse .isToMany () && !reverse .isToPK ()) {
463+ String unique = adapter .createUniqueConstraint (rel .getSourceEntity (), rel .getSourceAttributes ());
464+ if (unique != null ) {
465+ queries .add (unique );
466+ }
467+ }
468+ }
469+
470+ String fk = adapter .createFkConstraint (rel );
471+ if (fk != null ) {
472+ queries .add (fk );
473+ }
474+ }
475+ }
476+
477+ return queries ;
478+ }
479+
480+ private DataMap dataMap (String name ) {
481+ DataMap dataMap = domain .getDataMap (name );
482+ if (dataMap != null ) {
483+ return dataMap ;
484+ }
485+
486+ for (DataMap candidate : domain .getDataMaps ()) {
487+ if (candidate .getName ().endsWith ('/' + name )) {
488+ return candidate ;
489+ }
490+ }
491+
492+ throw new IllegalArgumentException ("Unknown DataMap: " + name );
493+ }
400494}
0 commit comments