16
16
package org .springframework .data .jdbc .repository ;
17
17
18
18
import static java .util .Arrays .*;
19
+ import static java .util .Collections .*;
19
20
import static org .assertj .core .api .Assertions .*;
20
21
import static org .assertj .core .api .SoftAssertions .*;
21
22
import static org .springframework .test .context .TestExecutionListeners .MergeMode .*;
50
51
import org .springframework .data .domain .Pageable ;
51
52
import org .springframework .data .domain .Slice ;
52
53
import org .springframework .data .jdbc .core .mapping .AggregateReference ;
54
+ import org .springframework .data .relational .core .mapping .MappedCollection ;
53
55
import org .springframework .data .relational .repository .Lock ;
54
56
import org .springframework .data .jdbc .repository .query .Modifying ;
55
57
import org .springframework .data .jdbc .repository .query .Query ;
63
65
import org .springframework .data .relational .core .mapping .event .AfterLoadEvent ;
64
66
import org .springframework .data .relational .core .sql .LockMode ;
65
67
import org .springframework .data .repository .CrudRepository ;
68
+ import org .springframework .data .repository .ListCrudRepository ;
66
69
import org .springframework .data .repository .core .NamedQueries ;
67
70
import org .springframework .data .repository .core .support .PropertiesBasedNamedQueries ;
68
71
import org .springframework .data .repository .query .Param ;
80
83
*
81
84
* @author Jens Schauder
82
85
* @author Mark Paluch
86
+ * @author Chirag Tailor
83
87
*/
84
88
@ Transactional
85
89
@ TestExecutionListeners (value = AssumeFeatureTestExecutionListener .class , mergeMode = MERGE_WITH_DEFAULTS )
@@ -89,6 +93,7 @@ public class JdbcRepositoryIntegrationTests {
89
93
@ Autowired NamedParameterJdbcTemplate template ;
90
94
@ Autowired DummyEntityRepository repository ;
91
95
@ Autowired MyEventListener eventListener ;
96
+ @ Autowired RootRepository rootRepository ;
92
97
93
98
private static DummyEntity createDummyEntity () {
94
99
@@ -282,7 +287,7 @@ public void updateMany() {
282
287
.containsExactlyInAnyOrder (entity .getName (), other .getName ());
283
288
}
284
289
285
- @ Test
290
+ @ Test // GH-537
286
291
void insertsOrUpdatesManyEntities () {
287
292
288
293
DummyEntity entity = repository .save (createDummyEntity ());
@@ -589,6 +594,84 @@ void nullStringResult() {
589
594
assertThat (repository .returnInput (null )).isNull ();
590
595
}
591
596
597
+ @ Test // GH-537
598
+ void manyInsertsWithNestedEntities () {
599
+ Root root1 = createRoot ("root1" );
600
+ Root root2 = createRoot ("root2" );
601
+
602
+ List <Root > savedRoots = rootRepository .saveAll (asList (root1 , root2 ));
603
+
604
+ List <Root > reloadedRoots = rootRepository .findAll ();
605
+ assertThat (reloadedRoots ).isEqualTo (savedRoots );
606
+ assertThat (reloadedRoots ).hasSize (2 );
607
+ assertIsEqualToWithNonNullIds (reloadedRoots .get (0 ), root1 );
608
+ assertIsEqualToWithNonNullIds (reloadedRoots .get (1 ), root2 );
609
+ }
610
+
611
+ @ Test // GH-537
612
+ @ EnabledOnFeature (TestDatabaseFeatures .Feature .SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES )
613
+ void manyUpdatesWithNestedEntities () {
614
+ Root root1 = createRoot ("root1" );
615
+ Root root2 = createRoot ("root2" );
616
+ List <Root > roots = rootRepository .saveAll (asList (root1 , root2 ));
617
+ Root savedRoot1 = roots .get (0 );
618
+ Root updatedRoot1 = new Root (savedRoot1 .id , "updated" + savedRoot1 .name ,
619
+ new Intermediate (savedRoot1 .intermediate .id , "updated" + savedRoot1 .intermediate .name ,
620
+ new Leaf (savedRoot1 .intermediate .leaf .id , "updated" + savedRoot1 .intermediate .leaf .name ), emptyList ()),
621
+ savedRoot1 .intermediates );
622
+ Root savedRoot2 = roots .get (1 );
623
+ Root updatedRoot2 = new Root (savedRoot2 .id , "updated" + savedRoot2 .name , savedRoot2 .intermediate ,
624
+ singletonList (
625
+ new Intermediate (savedRoot2 .intermediates .get (0 ).id , "updated" + savedRoot2 .intermediates .get (0 ).name , null ,
626
+ singletonList (new Leaf (savedRoot2 .intermediates .get (0 ).leaves .get (0 ).id ,
627
+ "updated" + savedRoot2 .intermediates .get (0 ).leaves .get (0 ).name )))));
628
+
629
+ List <Root > updatedRoots = rootRepository .saveAll (asList (updatedRoot1 , updatedRoot2 ));
630
+
631
+ List <Root > reloadedRoots = rootRepository .findAll ();
632
+ assertThat (reloadedRoots ).isEqualTo (updatedRoots );
633
+ assertThat (reloadedRoots ).containsExactly (updatedRoot1 , updatedRoot2 );
634
+ }
635
+
636
+ @ Test // GH-537
637
+ @ EnabledOnFeature (TestDatabaseFeatures .Feature .SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES )
638
+ void manyInsertsAndUpdatesWithNestedEntities () {
639
+ Root root1 = createRoot ("root1" );
640
+ Root savedRoot1 = rootRepository .save (root1 );
641
+ Root updatedRoot1 = new Root (savedRoot1 .id , "updated" + savedRoot1 .name ,
642
+ new Intermediate (savedRoot1 .intermediate .id , "updated" + savedRoot1 .intermediate .name ,
643
+ new Leaf (savedRoot1 .intermediate .leaf .id , "updated" + savedRoot1 .intermediate .leaf .name ), emptyList ()),
644
+ savedRoot1 .intermediates );
645
+ Root root2 = createRoot ("root2" );
646
+ List <Root > savedRoots = rootRepository .saveAll (asList (updatedRoot1 , root2 ));
647
+
648
+ List <Root > reloadedRoots = rootRepository .findAll ();
649
+ assertThat (reloadedRoots ).isEqualTo (savedRoots );
650
+ assertThat (reloadedRoots .get (0 )).isEqualTo (updatedRoot1 );
651
+ assertIsEqualToWithNonNullIds (reloadedRoots .get (1 ), root2 );
652
+ }
653
+
654
+ private Root createRoot (String namePrefix ) {
655
+ return new Root (null , namePrefix ,
656
+ new Intermediate (null , namePrefix + "Intermediate" , new Leaf (null , namePrefix + "Leaf" ), emptyList ()),
657
+ singletonList (new Intermediate (null , namePrefix + "QualifiedIntermediate" , null ,
658
+ singletonList (new Leaf (null , namePrefix + "QualifiedLeaf" )))));
659
+ }
660
+
661
+ private void assertIsEqualToWithNonNullIds (Root reloadedRoot1 , Root root1 ) {
662
+ assertThat (reloadedRoot1 .id ).isNotNull ();
663
+ assertThat (reloadedRoot1 .name ).isEqualTo (root1 .name );
664
+ assertThat (reloadedRoot1 .intermediate .id ).isNotNull ();
665
+ assertThat (reloadedRoot1 .intermediate .name ).isEqualTo (root1 .intermediate .name );
666
+ assertThat (reloadedRoot1 .intermediates .get (0 ).id ).isNotNull ();
667
+ assertThat (reloadedRoot1 .intermediates .get (0 ).name ).isEqualTo (root1 .intermediates .get (0 ).name );
668
+ assertThat (reloadedRoot1 .intermediate .leaf .id ).isNotNull ();
669
+ assertThat (reloadedRoot1 .intermediate .leaf .name ).isEqualTo (root1 .intermediate .leaf .name );
670
+ assertThat (reloadedRoot1 .intermediates .get (0 ).leaves .get (0 ).id ).isNotNull ();
671
+ assertThat (reloadedRoot1 .intermediates .get (0 ).leaves .get (0 ).name )
672
+ .isEqualTo (root1 .intermediates .get (0 ).leaves .get (0 ).name );
673
+ }
674
+
592
675
private Instant createDummyBeforeAndAfterNow () {
593
676
594
677
Instant now = Instant .now ();
@@ -692,6 +775,11 @@ DummyEntityRepository dummyEntityRepository() {
692
775
return factory .getRepository (DummyEntityRepository .class );
693
776
}
694
777
778
+ @ Bean
779
+ RootRepository rootRepository () {
780
+ return factory .getRepository (RootRepository .class );
781
+ }
782
+
695
783
@ Bean
696
784
NamedQueries namedQueries () throws IOException {
697
785
@@ -707,6 +795,30 @@ MyEventListener eventListener() {
707
795
}
708
796
}
709
797
798
+ interface RootRepository extends ListCrudRepository <Root , Long > {}
799
+
800
+ @ Value
801
+ static class Root {
802
+ @ Id Long id ;
803
+ String name ;
804
+ Intermediate intermediate ;
805
+ @ MappedCollection (idColumn = "ROOT_ID" , keyColumn = "ROOT_KEY" ) List <Intermediate > intermediates ;
806
+ }
807
+
808
+ @ Value
809
+ static class Intermediate {
810
+ @ Id Long id ;
811
+ String name ;
812
+ Leaf leaf ;
813
+ @ MappedCollection (idColumn = "INTERMEDIATE_ID" , keyColumn = "INTERMEDIATE_KEY" ) List <Leaf > leaves ;
814
+ }
815
+
816
+ @ Value
817
+ static class Leaf {
818
+ @ Id Long id ;
819
+ String name ;
820
+ }
821
+
710
822
static class MyEventListener implements ApplicationListener <AbstractRelationalEvent <?>> {
711
823
712
824
private List <AbstractRelationalEvent <?>> events = new ArrayList <>();
0 commit comments