Skip to content

Commit 75a0b68

Browse files
schaudermp911de
authored andcommitted
DATAJDBC-417 - Fixed saving an entity containing a null embeddable with a reference to a further entity.
Constructing the DbActions assumed that the parent of a path exists (i.e. is not null) and created parent nodes. Original pull request: #169.
1 parent caadaa0 commit 75a0b68

File tree

2 files changed

+77
-3
lines changed

2 files changed

+77
-3
lines changed

spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ private List<PathNode> from(PersistentPropertyPath<RelationalPersistentProperty>
202202

203203
} else {
204204

205-
List<PathNode> pathNodes = nodesCache.get(path.getParentPath());
205+
List<PathNode> pathNodes = nodesCache.getOrDefault(path.getParentPath(), Collections.emptyList());
206+
206207
pathNodes.forEach(parentNode -> {
207208

208209
// todo: this should go into pathnode
@@ -238,7 +239,17 @@ private boolean isDirectlyReferencedByRootIgnoringEmbeddables(
238239

239240
@Nullable
240241
private Object getFromRootValue(PersistentPropertyPath<RelationalPersistentProperty> path) {
241-
return path.getBaseProperty().getOwner().getPropertyAccessor(entity).getProperty(path);
242+
243+
if (path.getLength() == 0)
244+
return entity;
245+
246+
Object parent = getFromRootValue(path.getParentPath());
247+
if (parent == null) {
248+
return null;
249+
}
250+
251+
return context.getRequiredPersistentEntity(parent.getClass()).getPropertyAccessor(parent)
252+
.getProperty(path.getRequiredLeafProperty());
242253
}
243254

244255
private List<PathNode> createNodes(PersistentPropertyPath<RelationalPersistentProperty> path,

spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityWriterUnitTests.java

+64-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.junit.Test;
3030
import org.junit.runner.RunWith;
3131
import org.mockito.junit.MockitoJUnitRunner;
32-
3332
import org.springframework.data.annotation.Id;
3433
import org.springframework.data.mapping.PersistentPropertyPath;
3534
import org.springframework.data.mapping.PersistentPropertyPaths;
@@ -118,6 +117,7 @@ public void newEntityGetsConvertedToOneInsertByEmbeddedEntities() {
118117
);
119118
}
120119

120+
121121
@Test // DATAJDBC-112
122122
public void newEntityWithReferenceGetsConvertedToTwoInserts() {
123123

@@ -530,6 +530,51 @@ public void multiLevelQualifiedReferencesWithOutId() {
530530
);
531531
}
532532

533+
@Test // DATAJDBC-417
534+
public void savingANullEmbeddedWithEntity() {
535+
536+
EmbeddedReferenceChainEntity entity = new EmbeddedReferenceChainEntity(null);
537+
// the embedded is null !!!
538+
539+
AggregateChange<EmbeddedReferenceChainEntity> aggregateChange = //
540+
new AggregateChange<>(Kind.SAVE, EmbeddedReferenceChainEntity.class, entity);
541+
542+
converter.write(entity, aggregateChange);
543+
544+
assertThat(aggregateChange.getActions()) //
545+
.extracting(DbAction::getClass, //
546+
DbAction::getEntityType, //
547+
DbActionTestSupport::extractPath, //
548+
DbActionTestSupport::actualEntityType, //
549+
DbActionTestSupport::isWithDependsOn) //
550+
.containsExactly( //
551+
tuple(InsertRoot.class, EmbeddedReferenceChainEntity.class, "", EmbeddedReferenceChainEntity.class, false) //
552+
);
553+
}
554+
@Test // DATAJDBC-417
555+
public void savingInnerNullEmbeddedWithEntity() {
556+
557+
RootWithEmbeddedReferenceChainEntity root = new RootWithEmbeddedReferenceChainEntity(null);
558+
root.other = new EmbeddedReferenceChainEntity(null);
559+
// the embedded is null !!!
560+
561+
AggregateChange<RootWithEmbeddedReferenceChainEntity> aggregateChange = //
562+
new AggregateChange<>(Kind.SAVE, RootWithEmbeddedReferenceChainEntity.class, root);
563+
564+
converter.write(root, aggregateChange);
565+
566+
assertThat(aggregateChange.getActions()) //
567+
.extracting(DbAction::getClass, //
568+
DbAction::getEntityType, //
569+
DbActionTestSupport::extractPath, //
570+
DbActionTestSupport::actualEntityType, //
571+
DbActionTestSupport::isWithDependsOn) //
572+
.containsExactly( //
573+
tuple(InsertRoot.class, RootWithEmbeddedReferenceChainEntity.class, "", RootWithEmbeddedReferenceChainEntity.class, false), //
574+
tuple(Insert.class, EmbeddedReferenceChainEntity.class, "other", EmbeddedReferenceChainEntity.class, true) //
575+
);
576+
}
577+
533578
private CascadingReferenceMiddleElement createMiddleElement(Element first, Element second) {
534579

535580
CascadingReferenceMiddleElement middleElement1 = new CascadingReferenceMiddleElement(null);
@@ -585,6 +630,19 @@ static class EmbeddedReferenceEntity {
585630
@Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_") Element other;
586631
}
587632

633+
@RequiredArgsConstructor
634+
static class EmbeddedReferenceChainEntity {
635+
636+
@Id final Long id;
637+
@Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_") ElementReference other;
638+
}
639+
@RequiredArgsConstructor
640+
static class RootWithEmbeddedReferenceChainEntity {
641+
642+
@Id final Long id;
643+
EmbeddedReferenceChainEntity other;
644+
}
645+
588646
@RequiredArgsConstructor
589647
static class ReferenceWoIdEntity {
590648

@@ -641,6 +699,11 @@ private static class Element {
641699
@Id final Long id;
642700
}
643701

702+
@RequiredArgsConstructor
703+
private static class ElementReference {
704+
final Element element;
705+
}
706+
644707
@RequiredArgsConstructor
645708
private static class NoIdListMapContainer {
646709

0 commit comments

Comments
 (0)