|
49 | 49 | import org.springframework.data.domain.Sort; |
50 | 50 | import org.springframework.data.neo4j.core.Neo4jOperations; |
51 | 51 | import org.springframework.data.neo4j.integration.shared.common.DoritoEatingPerson; |
| 52 | +import org.springframework.data.neo4j.integration.shared.common.GH2621Domain; |
52 | 53 | import org.springframework.data.neo4j.test.Neo4jImperativeTestConfiguration; |
53 | 54 | import org.springframework.data.neo4j.core.DatabaseSelectionProvider; |
54 | 55 | import org.springframework.data.neo4j.core.Neo4jTemplate; |
|
79 | 80 | import org.springframework.data.repository.query.Param; |
80 | 81 | import org.springframework.transaction.PlatformTransactionManager; |
81 | 82 | import org.springframework.transaction.annotation.EnableTransactionManagement; |
| 83 | +import org.springframework.transaction.support.TransactionTemplate; |
82 | 84 |
|
83 | 85 | /** |
84 | 86 | * @author Gerrit Meier |
@@ -459,6 +461,57 @@ public void projectionRespected(@Autowired Neo4jOperations neo4jOperations) { |
459 | 461 | assertThat(saved).hasValueSatisfying(it -> assertThat(it.getFriends()).isEmpty()); |
460 | 462 | } |
461 | 463 |
|
| 464 | +@Test // GH-2621 |
| 465 | +public void nestedProjectWithFluentOpsShouldWork(@Autowired TransactionTemplate transactionTemplate, @Autowired Neo4jTemplate neo4jTemplate) { |
| 466 | + |
| 467 | +GH2621Domain.FooProjection fooProjection = transactionTemplate.execute(tx -> { |
| 468 | +final GH2621Domain.BarBarProjection barBarProjection = new GH2621Domain.BarBarProjection("v1", "v2"); |
| 469 | +return neo4jTemplate.save(GH2621Domain.Foo.class).one(new GH2621Domain.FooProjection(barBarProjection)); |
| 470 | +}); |
| 471 | + |
| 472 | +assertThat(fooProjection.getBar()).isNotNull(); |
| 473 | +assertThat(fooProjection.getBar().getValue1()).isEqualTo("v1"); |
| 474 | +// There is no way to deduce from a `BarProjection` field the correlation from `BarBarProjection to `BarBar` |
| 475 | +// without throwing a dice and we are not going to try this |
| 476 | +assertThat(fooProjection.getBar()).isInstanceOf(GH2621Domain.BarProjection.class); |
| 477 | + |
| 478 | +// The result above is reflected in the graph |
| 479 | +try (Session session = driver.session(bookmarkCapture.createSessionConfig())) { |
| 480 | +Record result = session.run("MATCH (n:GH2621Bar) RETURN n").single(); |
| 481 | +assertThat(result.get("n").asNode().get("value1").asString()).isEqualTo("v1"); |
| 482 | +} |
| 483 | +} |
| 484 | + |
| 485 | +@Test // GH-2621 |
| 486 | +public void nestedProjectWithFluentOpsShouldWork2(@Autowired TransactionTemplate transactionTemplate, @Autowired Neo4jTemplate neo4jTemplate) { |
| 487 | + |
| 488 | +GH2621Domain.FooProjection fooProjection = transactionTemplate.execute(tx -> { |
| 489 | +GH2621Domain.Foo foo = new GH2621Domain.Foo(new GH2621Domain.BarBar("v1", "v2")); |
| 490 | +return neo4jTemplate.saveAs(foo, GH2621Domain.FooProjection.class); |
| 491 | +}); |
| 492 | + |
| 493 | +assertThat(fooProjection.getBar()).isNotNull(); |
| 494 | +assertThat(fooProjection.getBar().getValue1()).isEqualTo("v1"); |
| 495 | +// There is no way to deduce from a `BarProjection` field the correlation from `BarBarProjection to `BarBar` |
| 496 | +// without throwing a dice and we are not going to try this |
| 497 | +assertThat(fooProjection.getBar()).isInstanceOf(GH2621Domain.BarProjection.class); |
| 498 | + |
| 499 | +// This is a different here as the concrete dto was used during save ops, so the |
| 500 | +try (Session session = driver.session(bookmarkCapture.createSessionConfig())) { |
| 501 | +Record result = session.run("MATCH (n:GH2621Bar:GH2621BarBar) RETURN n").single(); |
| 502 | +org.neo4j.driver.types.Node node = result.get("n").asNode(); |
| 503 | +assertThat(node.get("value1").asString()).isEqualTo("v1"); |
| 504 | +// This is a limitation of the Spring Data Commons support for the DTO projections |
| 505 | +// when we reach org/springframework/data/neo4j/core/PropertyFilterSupport.java:141 we call |
| 506 | +// org.springframework.data.projection.ProjectionFactory.getProjectionInformation and we only |
| 507 | +// have the concrete type information at hand, in the domain example FooProjection#bar, which points |
| 508 | +// to BarProjection, without any clue that we do want a BarBarProjection being used during saving. |
| 509 | +// So with the example in the ticket, saving value2 (or anything on the BarBarProjection) won't |
| 510 | +// be possible |
| 511 | +assertThat(node.get("value2").isNull()).isTrue(); |
| 512 | +} |
| 513 | +} |
| 514 | + |
462 | 515 | private static void projectedEntities(PersonDepartmentQueryResult personAndDepartment) { |
463 | 516 | assertThat(personAndDepartment.getPerson()).extracting(PersonEntity::getId).isEqualTo("p1"); |
464 | 517 | assertThat(personAndDepartment.getPerson()).extracting(PersonEntity::getEmail).isEqualTo("p1@dep1.org"); |
@@ -621,5 +674,10 @@ public PlatformTransactionManager transactionManager(Driver driver, DatabaseSele |
621 | 674 | public boolean isCypher5Compatible() { |
622 | 675 | return neo4jConnectionSupport.isCypher5SyntaxCompatible(); |
623 | 676 | } |
| 677 | + |
| 678 | +@Bean |
| 679 | +TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) { |
| 680 | +return new TransactionTemplate(transactionManager); |
| 681 | +} |
624 | 682 | } |
625 | 683 | } |
0 commit comments