42
42
import java .util .Collection ;
43
43
import java .util .Collections ;
44
44
import java .util .List ;
45
+ import java .util .Map ;
45
46
import java .util .Optional ;
47
+ import java .util .function .Consumer ;
48
+ import java .util .stream .Collectors ;
46
49
47
50
import static org .assertj .core .api .Assertions .assertThat ;
48
51
@@ -89,15 +92,7 @@ void relationshipsShouldHaveCorrectTypes(@Autowired BuildingRepository repositor
89
92
@ Test // GH-2138
90
93
void collectionsShouldHaveCorrectTypes (@ Autowired TerritoryRepository repository ) {
91
94
92
- Long territoryId ;
93
- try (Session session = driver .session ()) {
94
- territoryId = session .run ("CREATE (c:Country:BaseTerritory:BaseEntity{nameEn:'country'}) " +
95
- "CREATE (c)-[:LINK]->(:Country:BaseTerritory:BaseEntity{nameEn:'anotherCountry', countryProperty:'large'}) " +
96
- "CREATE (c)-[:LINK]->(:Continent:BaseTerritory:BaseEntity{nameEn:'continent', continentProperty:'small'}) " +
97
- "CREATE (c)-[:LINK]->(:GenericTerritory:BaseTerritory:BaseEntity{nameEn:'generic'}) " +
98
- "return id(c) as id" ).single ()
99
- .get (0 ).asLong ();
100
- }
95
+ Long territoryId = createDivisionAndTerritories ().get ("territoryId" ).asLong ();
101
96
102
97
Inheritance .BaseTerritory territory = repository .findById (territoryId ).get ();
103
98
@@ -117,12 +112,7 @@ void collectionsShouldHaveCorrectTypes(@Autowired TerritoryRepository repository
117
112
@ Test // GH-2138
118
113
void resultCollectionShouldHaveCorrectTypes (@ Autowired TerritoryRepository repository ) {
119
114
120
- try (Session session = driver .session ()) {
121
- session .run ("CREATE (c:Country:BaseTerritory:BaseEntity{nameEn:'country', countryProperty:'baseCountry'}) " +
122
- "CREATE (c)-[:LINK]->(:Country:BaseTerritory:BaseEntity{nameEn:'anotherCountry', countryProperty:'large'}) " +
123
- "CREATE (c)-[:LINK]->(:Continent:BaseTerritory:BaseEntity{nameEn:'continent', continentProperty:'small'}) " +
124
- "CREATE (c)-[:LINK]->(:GenericTerritory:BaseTerritory:BaseEntity{nameEn:'generic'})" ).consume ();
125
- }
115
+ createDivisionAndTerritories ();
126
116
127
117
List <Inheritance .BaseTerritory > territories = repository .findAll ();
128
118
@@ -324,6 +314,98 @@ void mixedInterfaces(@Autowired Neo4jTemplate template) {
324
314
}
325
315
}
326
316
317
+ @ Test // GH-2262
318
+ void shouldMatchPolymorphicClassesWhenFetchedById (@ Autowired DivisionRepository repository ) {
319
+
320
+ Record divisionAndTerritoryId = createDivisionAndTerritories ();
321
+
322
+ Optional <Inheritance .Division > optionalDivision = repository .findById (divisionAndTerritoryId .get ("divisionId" ).asLong ());
323
+ assertThat (optionalDivision ).isPresent ();
324
+ assertThat (optionalDivision ).hasValueSatisfying (twoDifferentClassesHaveBeenLoaded ());
325
+ }
326
+
327
+ @ Test // GH-2262
328
+ void shouldMatchPolymorphicClassesWhenFetchingAll (@ Autowired DivisionRepository repository ) {
329
+
330
+ createDivisionAndTerritories ();
331
+
332
+ List <Inheritance .Division > divisions = repository .findAll ();
333
+ assertThat (divisions ).hasSize (1 );
334
+ assertThat (divisions ).first ().satisfies (twoDifferentClassesHaveBeenLoaded ());
335
+ }
336
+
337
+ private Consumer <Inheritance .Division > twoDifferentClassesHaveBeenLoaded () {
338
+ return d -> {
339
+ assertThat (d .getIsActiveIn ()).hasSize (2 );
340
+ assertThat (d .getIsActiveIn ()).extracting (Inheritance .BaseTerritory ::getNameEn )
341
+ .containsExactlyInAnyOrder ("anotherCountry" , "continent" );
342
+ Map <String , Class > classByName = d .getIsActiveIn ().stream ()
343
+ .collect (Collectors .toMap (Inheritance .BaseTerritory ::getNameEn , v -> v .getClass ()));
344
+ assertThat (classByName ).containsEntry ("anotherCountry" , Inheritance .Country .class );
345
+ assertThat (classByName ).containsEntry ("continent" , Inheritance .Continent .class );
346
+ };
347
+ }
348
+
349
+ @ Test // GH-2262
350
+ void shouldMatchPolymorphicInterfacesWhenFetchedById (@ Autowired ParentModelRepository repository ) {
351
+
352
+ Record record = createRelationsToDifferentImplementations ();
353
+
354
+ Optional <Inheritance .ParentModel2 > optionalDivision = repository .findById (record .get (0 ).asNode ().id ());
355
+ assertThat (optionalDivision ).isPresent ();
356
+ assertThat (optionalDivision ).hasValueSatisfying (twoDifferentInterfacesHaveBeenLoaded ());
357
+ }
358
+
359
+ @ Test // GH-2262
360
+ void shouldMatchPolymorphicInterfacesWhenFetchingAll (@ Autowired ParentModelRepository repository ) {
361
+
362
+ createRelationsToDifferentImplementations ();
363
+
364
+ List <Inheritance .ParentModel2 > divisions = repository .findAll ();
365
+ assertThat (divisions ).hasSize (1 );
366
+ assertThat (divisions ).first ().satisfies (twoDifferentInterfacesHaveBeenLoaded ());
367
+ }
368
+
369
+ private Consumer <Inheritance .ParentModel2 > twoDifferentInterfacesHaveBeenLoaded () {
370
+ return d -> {
371
+ assertThat (d .getIsRelatedTo ()).hasSize (2 );
372
+ assertThat (d .getIsRelatedTo ()).extracting (Inheritance .SomeInterface3 ::getName )
373
+ .containsExactlyInAnyOrder ("3a" , "3b" );
374
+ Map <String , Class > classByName = d .getIsRelatedTo ().stream ()
375
+ .collect (Collectors .toMap (Inheritance .SomeInterface3 ::getName , v -> v .getClass ()));
376
+ assertThat (classByName ).containsEntry ("3a" , Inheritance .SomeInterfaceImpl3a .class );
377
+ assertThat (classByName ).containsEntry ("3b" , Inheritance .SomeInterfaceImpl3b .class );
378
+ };
379
+ }
380
+
381
+ private Record createDivisionAndTerritories () {
382
+ Record result ;
383
+ try (Session session = driver .session ()) {
384
+
385
+ result = session .run ("CREATE (c:Country:BaseTerritory:BaseEntity{nameEn:'country', countryProperty:'baseCountry'}) " +
386
+ "CREATE (c)-[:LINK]->(ca:Country:BaseTerritory:BaseEntity{nameEn:'anotherCountry', countryProperty:'large'}) " +
387
+ "CREATE (c)-[:LINK]->(cb:Continent:BaseTerritory:BaseEntity{nameEn:'continent', continentProperty:'small'}) " +
388
+ "CREATE (c)-[:LINK]->(:GenericTerritory:BaseTerritory:BaseEntity{nameEn:'generic'}) " +
389
+ "CREATE (d:Division:BaseEntity{name:'Division'}) " +
390
+ "CREATE (d) -[:IS_ACTIVE_IN] -> (ca)" +
391
+ "CREATE (d) -[:IS_ACTIVE_IN] -> (cb)" +
392
+ "RETURN id(d) as divisionId, id(c) as territoryId" ).single ();
393
+ }
394
+ return result ;
395
+ }
396
+
397
+ private Record createRelationsToDifferentImplementations () {
398
+ Record result ;
399
+ try (Session session = driver .session ()) {
400
+
401
+ result = session .run ("CREATE (p:ParentModel2) " +
402
+ "CREATE (p)-[:IS_RELATED_TO]->(:SomeInterface3:SomeInterface3a {name: '3a'}) " +
403
+ "CREATE (p)-[:IS_RELATED_TO]->(:SomeInterface3:SomeInterface3b {name: '3b'}) " +
404
+ "RETURN p" ).single ();
405
+ }
406
+ return result ;
407
+ }
408
+
327
409
interface PetsRepository extends Neo4jRepository <AbstractPet , Long > {
328
410
329
411
@ Query ("MATCH (n {name: $name}) RETURN n" )
@@ -335,6 +417,10 @@ interface BuildingRepository extends Neo4jRepository<Inheritance.Building, Long>
335
417
336
418
interface TerritoryRepository extends Neo4jRepository <Inheritance .BaseTerritory , Long > {}
337
419
420
+ interface DivisionRepository extends Neo4jRepository <Inheritance .Division , Long > {}
421
+
422
+ interface ParentModelRepository extends Neo4jRepository <Inheritance .ParentModel2 , Long > {}
423
+
338
424
@ Configuration
339
425
@ EnableNeo4jRepositories (considerNestedRepositories = true )
340
426
@ EnableTransactionManagement
0 commit comments