Skip to content

Commit 7e6f548

Browse files
committed
Dedicated exception for aggregate roots without id property.
We now distinguish between an id not set during insert and a supposed aggregate root without id property. Closes #1502 Original pull request #1855
1 parent 1c75679 commit 7e6f548

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java

+8
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ public <T> T save(T instance) {
165165

166166
Assert.notNull(instance, "Aggregate instance must not be null");
167167

168+
verifyIdProperty(instance);
169+
168170
return performSave(new EntityAndChangeCreator<>(instance, changeCreatorSelectorForSave(instance)));
169171
}
170172

@@ -179,6 +181,7 @@ public <T> Iterable<T> saveAll(Iterable<T> instances) {
179181

180182
List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
181183
for (T instance : instances) {
184+
verifyIdProperty(instance);
182185
entityAndChangeCreators.add(new EntityAndChangeCreator<>(instance, changeCreatorSelectorForSave(instance)));
183186
}
184187
return performSaveAll(entityAndChangeCreators);
@@ -425,6 +428,11 @@ public <T> void deleteAll(Iterable<? extends T> instances) {
425428
}
426429
}
427430

431+
private <T> void verifyIdProperty(T instance) {
432+
// accessing the id property just to raise an exception in the case it does not exist.
433+
context.getRequiredPersistentEntity(instance.getClass()).getRequiredIdProperty();
434+
}
435+
428436
private <T> void doDeleteAll(Iterable<? extends T> instances, Class<T> domainType) {
429437

430438
BatchingAggregateChange<T, DeleteAggregateChange<T>> batchingAggregateChange = BatchingAggregateChange

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java

+39
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232
import org.springframework.data.domain.PageRequest;
3333
import org.springframework.data.domain.Sort;
3434
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
35+
import org.springframework.data.jdbc.core.convert.Identifier;
3536
import org.springframework.data.jdbc.core.convert.JdbcConverter;
3637
import org.springframework.data.jdbc.core.convert.MappingJdbcConverter;
3738
import org.springframework.data.jdbc.core.convert.RelationResolver;
3839
import org.springframework.data.mapping.callback.EntityCallbacks;
40+
import org.springframework.data.relational.core.conversion.IdValueSource;
3941
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
4042
import org.springframework.data.relational.core.mapping.Column;
4143
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
@@ -46,6 +48,8 @@
4648
import org.springframework.data.relational.core.mapping.event.BeforeDeleteCallback;
4749
import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback;
4850

51+
import java.util.List;
52+
4953
/**
5054
* Unit tests for {@link JdbcAggregateTemplate}.
5155
*
@@ -333,6 +337,38 @@ void deleteAllByIdWithEmptyListDoesNothing() {
333337
template.deleteAllById(emptyList(), SampleEntity.class);
334338
}
335339

340+
@Test // GH-1502
341+
void saveThrowsExceptionWhenIdIsNotSet() {
342+
343+
SampleEntity alfred = new SampleEntity(null, "Alfred");
344+
when(callbacks.callback(any(), any(), any(Object[].class))).thenReturn(alfred);
345+
346+
when(dataAccessStrategy.insert(eq(alfred), any(Class.class), any(Identifier.class), any(IdValueSource.class)))
347+
.thenReturn(null);
348+
349+
assertThatIllegalArgumentException().isThrownBy(() -> template.save(alfred))
350+
.withMessage("After saving the identifier must not be null");
351+
}
352+
353+
@Test // GH-1502
354+
void saveThrowsExceptionWhenIdDoesNotExist() {
355+
356+
NoIdEntity alfred = new NoIdEntity("Alfred");
357+
358+
assertThatIllegalStateException().isThrownBy(() -> template.save(alfred))
359+
.withMessage("Required identifier property not found for class %s".formatted(NoIdEntity.class.getName()));
360+
}
361+
362+
@Test // GH-1502
363+
void saveThrowsExceptionWhenIdDoesNotExistOnSaveAll() {
364+
365+
NoIdEntity alfred = new NoIdEntity("Alfred");
366+
NoIdEntity berta = new NoIdEntity("Berta");
367+
368+
assertThatIllegalStateException().isThrownBy(() -> template.saveAll( List.of(alfred, berta)))
369+
.withMessage("Required identifier property not found for class %s".formatted(NoIdEntity.class.getName()));
370+
}
371+
336372
private static class SampleEntity {
337373

338374
@Column("id1")
@@ -451,4 +487,7 @@ public long getVersion() {
451487
return this.version;
452488
}
453489
}
490+
491+
record NoIdEntity(String name) {
492+
}
454493
}

0 commit comments

Comments
 (0)