2020import java .util .AbstractMap ;
2121import java .util .Collection ;
2222import java .util .Map ;
23+ import java .util .Optional ;
2324
2425import org .junit .jupiter .api .BeforeEach ;
2526import org .junit .jupiter .api .Test ;
2627import org .neo4j .driver .Driver ;
28+ import org .neo4j .driver .Record ;
2729import org .neo4j .driver .Session ;
2830import org .neo4j .driver .Transaction ;
2931import org .neo4j .driver .Values ;
4042import org .springframework .data .neo4j .integration .shared .common .NamesOnlyDto ;
4143import org .springframework .data .neo4j .integration .shared .common .Person ;
4244import org .springframework .data .neo4j .integration .shared .common .PersonSummary ;
45+ import org .springframework .data .neo4j .integration .shared .common .ProjectionTestRoot ;
4346import org .springframework .data .neo4j .repository .Neo4jRepository ;
4447import org .springframework .data .neo4j .repository .config .EnableNeo4jRepositories ;
4548import org .springframework .data .neo4j .test .Neo4jExtension ;
@@ -61,6 +64,8 @@ class ProjectionIT {
6164 private static Neo4jExtension .Neo4jConnectionSupport neo4jConnectionSupport ;
6265
6366 private final Driver driver ;
67+ private Long projectionTestRootId ;
68+ private Long projectionTestLevel1Id ;
6469
6570 @ Autowired
6671 ProjectionIT (Driver driver ) {
@@ -69,24 +74,39 @@ class ProjectionIT {
6974
7075 @ BeforeEach
7176 void setup () {
72- Session session = driver .session ();
73- Transaction transaction = session .beginTransaction ();
74-
75- transaction .run ("MATCH (n) detach delete n" );
76-
77- for (Map .Entry <String , String > person : new Map .Entry [] {
78- new AbstractMap .SimpleEntry (FIRST_NAME , LAST_NAME ),
79- new AbstractMap .SimpleEntry (FIRST_NAME2 , LAST_NAME ),
80- }) {
81- transaction .run (" MERGE (address:Address{city: $city})"
82- + "CREATE (:Person{firstName: $firstName, lastName: $lastName})"
83- + "-[:LIVES_AT]-> (address)" ,
84- Values .parameters ("firstName" , person .getKey (), "lastName" , person .getValue (), "city" , CITY ));
85- }
8677
87- transaction .commit ();
88- transaction .close ();
89- session .close ();
78+ try (Session session = driver .session ();
79+ Transaction transaction = session .beginTransaction ();) {
80+
81+ transaction .run ("MATCH (n) detach delete n" );
82+
83+ for (Map .Entry <String , String > person : new Map .Entry [] {
84+ new AbstractMap .SimpleEntry (FIRST_NAME , LAST_NAME ),
85+ new AbstractMap .SimpleEntry (FIRST_NAME2 , LAST_NAME ),
86+ }) {
87+ transaction .run (" MERGE (address:Address{city: $city})"
88+ + "CREATE (:Person{firstName: $firstName, lastName: $lastName})"
89+ + "-[:LIVES_AT]-> (address)" ,
90+ Values .parameters ("firstName" , person .getKey (), "lastName" , person .getValue (), "city" , CITY ));
91+ }
92+
93+ Record result = transaction .run ("create (r:ProjectionTestRoot {name: 'root'}) \n "
94+ + "create (l11:ProjectionTestLevel1 {name: 'level11'})\n "
95+ + "create (l12:ProjectionTestLevel1 {name: 'level12'})\n "
96+ + "create (l21:ProjectionTestLevel2 {name: 'level21'})\n "
97+ + "create (l22:ProjectionTestLevel2 {name: 'level22'})\n "
98+ + "create (l23:ProjectionTestLevel2 {name: 'level23'})\n "
99+ + "create (r) - [:LEVEL_1] -> (l11)\n "
100+ + "create (r) - [:LEVEL_1] -> (l12)\n "
101+ + "create (l11) - [:LEVEL_2] -> (l21)\n "
102+ + "create (l11) - [:LEVEL_2] -> (l22)\n "
103+ + "create (l12) - [:LEVEL_2] -> (l23)\n "
104+ + "return id(r), id(l11)" ).single ();
105+
106+ projectionTestRootId = result .get (0 ).asLong ();
107+ projectionTestLevel1Id = result .get (1 ).asLong ();
108+ transaction .commit ();
109+ }
90110 }
91111
92112 @ Test
@@ -188,6 +208,29 @@ void projectionsShouldBeSliceable(@Autowired ProjectionPersonRepository reposito
188208 assertThat (people ).extracting (NamesOnly ::getFullName ).containsExactly (FIRST_NAME + " " + LAST_NAME );
189209 }
190210
211+ @ Test // GH-2164
212+ void findByIdWithProjectionShouldWork (@ Autowired TreestructureRepository repository ) {
213+
214+ Optional <SimpleProjection > optionalProjection = repository
215+ .findById (projectionTestRootId , SimpleProjection .class );
216+ assertThat (optionalProjection ).map (SimpleProjection ::getName ).hasValue ("root" );
217+ }
218+
219+ @ Test // GH-2164
220+ void findByIdInDerivedFinderMethodInRelatedObjectShouldWork (@ Autowired TreestructureRepository repository ) {
221+
222+ Optional <ProjectionTestRoot > optionalProjection = repository .findOneByLevel1Id (projectionTestLevel1Id );
223+ assertThat (optionalProjection ).map (ProjectionTestRoot ::getName ).hasValue ("root" );
224+ }
225+
226+ @ Test // GH-2164
227+ void findByIdInDerivedFinderMethodInRelatedObjectWithProjectionShouldWork (
228+ @ Autowired TreestructureRepository repository ) {
229+
230+ Optional <SimpleProjection > optionalProjection = repository .findOneByLevel1Id (projectionTestLevel1Id , SimpleProjection .class );
231+ assertThat (optionalProjection ).map (SimpleProjection ::getName ).hasValue ("root" );
232+ }
233+
191234 interface ProjectionPersonRepository extends Neo4jRepository <Person , Long > {
192235
193236 Collection <NamesOnly > findByLastName (String lastName );
@@ -203,6 +246,20 @@ interface ProjectionPersonRepository extends Neo4jRepository<Person, Long> {
203246 <T > Collection <T > findByLastNameAndFirstName (String lastName , String firstName , Class <T > projectionClass );
204247 }
205248
249+ interface TreestructureRepository extends Neo4jRepository <ProjectionTestRoot , Long > {
250+
251+ <T > Optional <T > findById (Long id , Class <T > typeOfProjection );
252+
253+ Optional <ProjectionTestRoot > findOneByLevel1Id (Long idOfLevel1 );
254+
255+ <T > Optional <T > findOneByLevel1Id (Long idOfLevel1 , Class <T > typeOfProjection );
256+ }
257+
258+ interface SimpleProjection {
259+
260+ String getName ();
261+ }
262+
206263 @ Configuration
207264 @ EnableNeo4jRepositories (considerNestedRepositories = true )
208265 @ EnableTransactionManagement
0 commit comments