15
15
*/
16
16
package org .springframework .data .neo4j .integration .imperative ;
17
17
18
+ import static org .assertj .core .api .Assertions .assertThat ;
19
+
20
+ import java .util .Collection ;
21
+ import java .util .Collections ;
22
+ import java .util .List ;
23
+ import java .util .Map ;
24
+ import java .util .Optional ;
25
+ import java .util .function .Consumer ;
26
+ import java .util .stream .Collectors ;
27
+
18
28
import org .junit .jupiter .api .BeforeEach ;
19
29
import org .junit .jupiter .api .Test ;
20
30
import org .neo4j .driver .Driver ;
46
56
import org .springframework .transaction .annotation .Transactional ;
47
57
import org .springframework .transaction .support .TransactionTemplate ;
48
58
49
- import java .util .Collection ;
50
- import java .util .Collections ;
51
- import java .util .List ;
52
- import java .util .Optional ;
53
-
54
- import static org .assertj .core .api .Assertions .assertThat ;
55
-
56
59
/**
57
60
* @author Gerrit Meier
58
61
* @author Michael J. Simons
@@ -105,16 +108,7 @@ void relationshipsShouldHaveCorrectTypes(@Autowired BuildingRepository repositor
105
108
@ Test // GH-2138
106
109
void collectionsShouldHaveCorrectTypes (@ Autowired TerritoryRepository repository ) {
107
110
108
- Long territoryId ;
109
- try (Session session = driver .session (bookmarkCapture .createSessionConfig ())) {
110
- territoryId = session .run ("CREATE (c:Country:BaseTerritory:BaseEntity{nameEn:'country'}) " +
111
- "CREATE (c)-[:LINK]->(:Country:BaseTerritory:BaseEntity{nameEn:'anotherCountry', countryProperty:'large'}) " +
112
- "CREATE (c)-[:LINK]->(:Continent:BaseTerritory:BaseEntity{nameEn:'continent', continentProperty:'small'}) " +
113
- "CREATE (c)-[:LINK]->(:GenericTerritory:BaseTerritory:BaseEntity{nameEn:'generic'}) " +
114
- "return id(c) as id" ).single ()
115
- .get (0 ).asLong ();
116
- bookmarkCapture .seedWith (session .lastBookmark ());
117
- }
111
+ Long territoryId = createDivisionAndTerritories ().get ("territoryId" ).asLong ();
118
112
119
113
Inheritance .BaseTerritory territory = repository .findById (territoryId ).get ();
120
114
@@ -134,13 +128,7 @@ void collectionsShouldHaveCorrectTypes(@Autowired TerritoryRepository repository
134
128
@ Test // GH-2138
135
129
void resultCollectionShouldHaveCorrectTypes (@ Autowired TerritoryRepository repository ) {
136
130
137
- try (Session session = driver .session (bookmarkCapture .createSessionConfig ())) {
138
- session .run ("CREATE (c:Country:BaseTerritory:BaseEntity{nameEn:'country', countryProperty:'baseCountry'}) " +
139
- "CREATE (c)-[:LINK]->(:Country:BaseTerritory:BaseEntity{nameEn:'anotherCountry', countryProperty:'large'}) " +
140
- "CREATE (c)-[:LINK]->(:Continent:BaseTerritory:BaseEntity{nameEn:'continent', continentProperty:'small'}) " +
141
- "CREATE (c)-[:LINK]->(:GenericTerritory:BaseTerritory:BaseEntity{nameEn:'generic'})" ).consume ();
142
- bookmarkCapture .seedWith (session .lastBookmark ());
143
- }
131
+ createDivisionAndTerritories ();
144
132
145
133
List <Inheritance .BaseTerritory > territories = repository .findAll ();
146
134
@@ -343,6 +331,100 @@ void mixedInterfaces(@Autowired Neo4jTemplate template) {
343
331
}
344
332
}
345
333
334
+ @ Test // GH-2262
335
+ void shouldMatchPolymorphicClassesWhenFetchedById (@ Autowired DivisionRepository repository ) {
336
+
337
+ Record divisionAndTerritoryId = createDivisionAndTerritories ();
338
+
339
+ Optional <Inheritance .Division > optionalDivision = repository .findById (divisionAndTerritoryId .get ("divisionId" ).asLong ());
340
+ assertThat (optionalDivision ).isPresent ();
341
+ assertThat (optionalDivision ).hasValueSatisfying (twoDifferentClassesHaveBeenLoaded ());
342
+ }
343
+
344
+ @ Test // GH-2262
345
+ void shouldMatchPolymorphicClassesWhenFetchingAll (@ Autowired DivisionRepository repository ) {
346
+
347
+ createDivisionAndTerritories ();
348
+
349
+ List <Inheritance .Division > divisions = repository .findAll ();
350
+ assertThat (divisions ).hasSize (1 );
351
+ assertThat (divisions ).first ().satisfies (twoDifferentClassesHaveBeenLoaded ());
352
+ }
353
+
354
+ private Consumer <Inheritance .Division > twoDifferentClassesHaveBeenLoaded () {
355
+ return d -> {
356
+ assertThat (d .getIsActiveIn ()).hasSize (2 );
357
+ assertThat (d .getIsActiveIn ()).extracting (Inheritance .BaseTerritory ::getNameEn )
358
+ .containsExactlyInAnyOrder ("anotherCountry" , "continent" );
359
+ Map <String , Class > classByName = d .getIsActiveIn ().stream ()
360
+ .collect (Collectors .toMap (Inheritance .BaseTerritory ::getNameEn , v -> v .getClass ()));
361
+ assertThat (classByName ).containsEntry ("anotherCountry" , Inheritance .Country .class );
362
+ assertThat (classByName ).containsEntry ("continent" , Inheritance .Continent .class );
363
+ };
364
+ }
365
+
366
+ @ Test // GH-2262
367
+ void shouldMatchPolymorphicInterfacesWhenFetchedById (@ Autowired ParentModelRepository repository ) {
368
+
369
+ Record record = createRelationsToDifferentImplementations ();
370
+
371
+ Optional <Inheritance .ParentModel2 > optionalDivision = repository .findById (record .get (0 ).asNode ().id ());
372
+ assertThat (optionalDivision ).isPresent ();
373
+ assertThat (optionalDivision ).hasValueSatisfying (twoDifferentInterfacesHaveBeenLoaded ());
374
+ }
375
+
376
+ @ Test // GH-2262
377
+ void shouldMatchPolymorphicInterfacesWhenFetchingAll (@ Autowired ParentModelRepository repository ) {
378
+
379
+ createRelationsToDifferentImplementations ();
380
+
381
+ List <Inheritance .ParentModel2 > divisions = repository .findAll ();
382
+ assertThat (divisions ).hasSize (1 );
383
+ assertThat (divisions ).first ().satisfies (twoDifferentInterfacesHaveBeenLoaded ());
384
+ }
385
+
386
+ private Consumer <Inheritance .ParentModel2 > twoDifferentInterfacesHaveBeenLoaded () {
387
+ return d -> {
388
+ assertThat (d .getIsRelatedTo ()).hasSize (2 );
389
+ assertThat (d .getIsRelatedTo ()).extracting (Inheritance .SomeInterface3 ::getName )
390
+ .containsExactlyInAnyOrder ("3a" , "3b" );
391
+ Map <String , Class > classByName = d .getIsRelatedTo ().stream ()
392
+ .collect (Collectors .toMap (Inheritance .SomeInterface3 ::getName , v -> v .getClass ()));
393
+ assertThat (classByName ).containsEntry ("3a" , Inheritance .SomeInterfaceImpl3a .class );
394
+ assertThat (classByName ).containsEntry ("3b" , Inheritance .SomeInterfaceImpl3b .class );
395
+ };
396
+ }
397
+
398
+ private Record createDivisionAndTerritories () {
399
+ Record result ;
400
+ try (Session session = driver .session (bookmarkCapture .createSessionConfig ())) {
401
+
402
+ result = session .run ("CREATE (c:Country:BaseTerritory:BaseEntity{nameEn:'country', countryProperty:'baseCountry'}) " +
403
+ "CREATE (c)-[:LINK]->(ca:Country:BaseTerritory:BaseEntity{nameEn:'anotherCountry', countryProperty:'large'}) " +
404
+ "CREATE (c)-[:LINK]->(cb:Continent:BaseTerritory:BaseEntity{nameEn:'continent', continentProperty:'small'}) " +
405
+ "CREATE (c)-[:LINK]->(:GenericTerritory:BaseTerritory:BaseEntity{nameEn:'generic'}) " +
406
+ "CREATE (d:Division:BaseEntity{name:'Division'}) " +
407
+ "CREATE (d) -[:IS_ACTIVE_IN] -> (ca)" +
408
+ "CREATE (d) -[:IS_ACTIVE_IN] -> (cb)" +
409
+ "RETURN id(d) as divisionId, id(c) as territoryId" ).single ();
410
+ bookmarkCapture .seedWith (session .lastBookmark ());
411
+ }
412
+ return result ;
413
+ }
414
+
415
+ private Record createRelationsToDifferentImplementations () {
416
+ Record result ;
417
+ try (Session session = driver .session (bookmarkCapture .createSessionConfig ())) {
418
+
419
+ result = session .run ("CREATE (p:ParentModel2) " +
420
+ "CREATE (p)-[:IS_RELATED_TO]->(:SomeInterface3:SomeInterface3a {name: '3a'}) " +
421
+ "CREATE (p)-[:IS_RELATED_TO]->(:SomeInterface3:SomeInterface3b {name: '3b'}) " +
422
+ "RETURN p" ).single ();
423
+ bookmarkCapture .seedWith (session .lastBookmark ());
424
+ }
425
+ return result ;
426
+ }
427
+
346
428
interface PetsRepository extends Neo4jRepository <AbstractPet , Long > {
347
429
348
430
@ Transactional (readOnly = true )
@@ -355,6 +437,10 @@ interface BuildingRepository extends Neo4jRepository<Inheritance.Building, Long>
355
437
356
438
interface TerritoryRepository extends Neo4jRepository <Inheritance .BaseTerritory , Long > {}
357
439
440
+ interface DivisionRepository extends Neo4jRepository <Inheritance .Division , Long > {}
441
+
442
+ interface ParentModelRepository extends Neo4jRepository <Inheritance .ParentModel2 , Long > {}
443
+
358
444
@ Configuration
359
445
@ EnableNeo4jRepositories (considerNestedRepositories = true )
360
446
@ EnableTransactionManagement
0 commit comments