Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5b3fa3d

Browse files
mipo256schauder
authored andcommitted
Applies proper event handling before saving in batch.
Signed-off-by: mipo256 <mikhailpolivakha@gmail.com> Commit message edited by Jens Schauder. Original pull request #2065 Closes #2064
1 parent 1e50d5a commit 5b3fa3d

10 files changed

+170
-47
lines changed

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

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.stream.Collectors;
2828
import java.util.stream.StreamSupport;
2929

30+
import org.jspecify.annotations.Nullable;
3031
import org.springframework.context.ApplicationContext;
3132
import org.springframework.context.ApplicationEventPublisher;
3233
import org.springframework.data.domain.Page;
@@ -49,11 +50,22 @@
4950
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
5051
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
5152
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
52-
import org.springframework.data.relational.core.mapping.event.*;
53+
import org.springframework.data.relational.core.mapping.event.AbstractRelationalEvent;
54+
import org.springframework.data.relational.core.mapping.event.AfterConvertCallback;
55+
import org.springframework.data.relational.core.mapping.event.AfterConvertEvent;
56+
import org.springframework.data.relational.core.mapping.event.AfterDeleteCallback;
57+
import org.springframework.data.relational.core.mapping.event.AfterDeleteEvent;
58+
import org.springframework.data.relational.core.mapping.event.AfterSaveCallback;
59+
import org.springframework.data.relational.core.mapping.event.AfterSaveEvent;
60+
import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback;
61+
import org.springframework.data.relational.core.mapping.event.BeforeConvertEvent;
62+
import org.springframework.data.relational.core.mapping.event.BeforeDeleteCallback;
63+
import org.springframework.data.relational.core.mapping.event.BeforeDeleteEvent;
64+
import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback;
65+
import org.springframework.data.relational.core.mapping.event.BeforeSaveEvent;
66+
import org.springframework.data.relational.core.mapping.event.Identifier;
5367
import org.springframework.data.relational.core.query.Query;
5468
import org.springframework.data.support.PageableExecutionUtils;
55-
import org.springframework.data.util.Streamable;
56-
import org.springframework.lang.Nullable;
5769
import org.springframework.util.Assert;
5870
import org.springframework.util.ClassUtils;
5971

@@ -173,19 +185,7 @@ public <T> T save(T instance) {
173185

174186
@Override
175187
public <T> List<T> saveAll(Iterable<T> instances) {
176-
177-
Assert.notNull(instances, "Aggregate instances must not be null");
178-
179-
if (!instances.iterator().hasNext()) {
180-
return Collections.emptyList();
181-
}
182-
183-
List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
184-
for (T instance : instances) {
185-
verifyIdProperty(instance);
186-
entityAndChangeCreators.add(new EntityAndChangeCreator<>(instance, changeCreatorSelectorForSave(instance)));
187-
}
188-
return performSaveAll(entityAndChangeCreators);
188+
return saveInBatch(instances, instance -> changeCreatorSelectorForSave(instance));
189189
}
190190

191191
/**
@@ -206,21 +206,7 @@ public <T> T insert(T instance) {
206206

207207
@Override
208208
public <T> List<T> insertAll(Iterable<T> instances) {
209-
210-
Assert.notNull(instances, "Aggregate instances must not be null");
211-
212-
if (!instances.iterator().hasNext()) {
213-
return Collections.emptyList();
214-
}
215-
216-
List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
217-
for (T instance : instances) {
218-
219-
Function<T, RootAggregateChange<T>> changeCreator = entity -> createInsertChange(prepareVersionForInsert(entity));
220-
EntityAndChangeCreator<T> entityChange = new EntityAndChangeCreator<>(instance, changeCreator);
221-
entityAndChangeCreators.add(entityChange);
222-
}
223-
return performSaveAll(entityAndChangeCreators);
209+
return doInBatch(instances, entity -> createInsertChange(prepareVersionForInsert(entity)));
224210
}
225211

226212
/**
@@ -241,6 +227,10 @@ public <T> T update(T instance) {
241227

242228
@Override
243229
public <T> List<T> updateAll(Iterable<T> instances) {
230+
return doInBatch(instances, entity -> createUpdateChange(prepareVersionForUpdate(entity)));
231+
}
232+
233+
private <T> List<T> saveInBatch(Iterable<T> instances, Function<T, AggregateChangeCreator<T>> changes) {
244234

245235
Assert.notNull(instances, "Aggregate instances must not be null");
246236

@@ -249,11 +239,27 @@ public <T> List<T> updateAll(Iterable<T> instances) {
249239
}
250240

251241
List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
242+
252243
for (T instance : instances) {
244+
verifyIdProperty(instance);
245+
entityAndChangeCreators.add(new EntityAndChangeCreator<>(instance, changes.apply(instance)));
246+
}
247+
248+
return performSaveAll(entityAndChangeCreators);
249+
}
250+
251+
private <T> List<T> doInBatch(Iterable<T> instances, AggregateChangeCreator<T> changeCreatorFunction) {
252+
253+
Assert.notNull(instances, "Aggregate instances must not be null");
253254

254-
Function<T, RootAggregateChange<T>> changeCreator = entity -> createUpdateChange(prepareVersionForUpdate(entity));
255-
EntityAndChangeCreator<T> entityChange = new EntityAndChangeCreator<>(instance, changeCreator);
256-
entityAndChangeCreators.add(entityChange);
255+
if (!instances.iterator().hasNext()) {
256+
return Collections.emptyList();
257+
}
258+
259+
List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
260+
for (T instance : instances) {
261+
verifyIdProperty(instance);
262+
entityAndChangeCreators.add(new EntityAndChangeCreator<T>(instance, changeCreatorFunction));
257263
}
258264
return performSaveAll(entityAndChangeCreators);
259265
}
@@ -473,7 +479,7 @@ private <T> RootAggregateChange<T> beforeExecute(EntityAndChangeCreator<T> insta
473479

474480
T aggregateRoot = triggerBeforeConvert(instance.entity);
475481

476-
RootAggregateChange<T> change = instance.changeCreator.apply(aggregateRoot);
482+
RootAggregateChange<T> change = instance.changeCreator.createAggregateChange(aggregateRoot);
477483

478484
aggregateRoot = triggerBeforeSave(change.getRoot(), change);
479485

@@ -531,7 +537,7 @@ private <T> List<T> performSaveAll(Iterable<EntityAndChangeCreator<T>> instances
531537
return results;
532538
}
533539

534-
private <T> Function<T, RootAggregateChange<T>> changeCreatorSelectorForSave(T instance) {
540+
private <T> AggregateChangeCreator<T> changeCreatorSelectorForSave(T instance) {
535541

536542
return context.getRequiredPersistentEntity(instance.getClass()).isNew(instance)
537543
? entity -> createInsertChange(prepareVersionForInsert(entity))
@@ -671,6 +677,13 @@ private <T> T triggerBeforeDelete(@Nullable T aggregateRoot, Object id, MutableA
671677
private record EntityAndPreviousVersion<T> (T entity, @Nullable Number version) {
672678
}
673679

674-
private record EntityAndChangeCreator<T> (T entity, Function<T, RootAggregateChange<T>> changeCreator) {
680+
private record EntityAndChangeCreator<T> (T entity, AggregateChangeCreator<T> changeCreator) {
681+
}
682+
683+
private interface AggregateChangeCreator<T> extends Function<T, RootAggregateChange<T>> {
684+
685+
default RootAggregateChange<T> createAggregateChange(T instance) {
686+
return this.apply(instance);
687+
}
675688
}
676689
}

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

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
import java.util.function.Function;
2929
import java.util.stream.IntStream;
3030

31+
import org.assertj.core.api.Assertions;
3132
import org.assertj.core.api.SoftAssertions;
3233
import org.junit.jupiter.api.Test;
3334
import org.springframework.beans.factory.annotation.Autowired;
35+
import org.springframework.context.ApplicationContext;
3436
import org.springframework.context.ApplicationEventPublisher;
3537
import org.springframework.context.annotation.Bean;
3638
import org.springframework.context.annotation.Configuration;
@@ -52,6 +54,7 @@
5254
import org.springframework.data.jdbc.testing.TestClass;
5355
import org.springframework.data.jdbc.testing.TestConfiguration;
5456
import org.springframework.data.jdbc.testing.TestDatabaseFeatures;
57+
import org.springframework.data.mapping.callback.EntityCallbacks;
5558
import org.springframework.data.mapping.context.InvalidPersistentPropertyPath;
5659
import org.springframework.data.relational.core.conversion.DbActionExecutionException;
5760
import org.springframework.data.relational.core.mapping.Column;
@@ -60,6 +63,7 @@
6063
import org.springframework.data.relational.core.mapping.MappedCollection;
6164
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
6265
import org.springframework.data.relational.core.mapping.Table;
66+
import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback;
6367
import org.springframework.data.relational.core.query.Criteria;
6468
import org.springframework.data.relational.core.query.CriteriaDefinition;
6569
import org.springframework.data.relational.core.query.Query;
@@ -1328,6 +1332,22 @@ void mapWithEnumKey() {
13281332
assertThat(enumMapOwners).containsExactly(enumMapOwner);
13291333
}
13301334

1335+
@Test //GH-2064
1336+
void saveAllBeforeConvertCallback() {
1337+
var first = new BeforeConvertCallbackForSaveBatch("first");
1338+
var second = new BeforeConvertCallbackForSaveBatch("second");
1339+
var third = new BeforeConvertCallbackForSaveBatch("third");
1340+
1341+
template.saveAll(List.of(first, second, third));
1342+
1343+
var allEntriesInTable = template.findAll(BeforeConvertCallbackForSaveBatch.class);
1344+
1345+
Assertions.assertThat(allEntriesInTable)
1346+
.hasSize(3)
1347+
.extracting(BeforeConvertCallbackForSaveBatch::getName)
1348+
.containsOnly("first", "second", "third");
1349+
}
1350+
13311351
@Test // GH-1684
13321352
void oneToOneWithIdenticalIdColumnName() {
13331353

@@ -2139,6 +2159,32 @@ public Short getVersion() {
21392159
}
21402160
}
21412161

2162+
@Table("BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH")
2163+
static class BeforeConvertCallbackForSaveBatch {
2164+
2165+
@Id
2166+
private String id;
2167+
2168+
private String name;
2169+
2170+
public BeforeConvertCallbackForSaveBatch(String name) {
2171+
this.name = name;
2172+
}
2173+
2174+
public String getId() {
2175+
return id;
2176+
}
2177+
2178+
public BeforeConvertCallbackForSaveBatch setId(String id) {
2179+
this.id = id;
2180+
return this;
2181+
}
2182+
2183+
public String getName() {
2184+
return name;
2185+
}
2186+
}
2187+
21422188
@Table("VERSIONED_AGGREGATE")
21432189
static class AggregateWithPrimitiveShortVersion extends VersionedAggregate {
21442190

@@ -2226,9 +2272,17 @@ TestClass testClass() {
22262272
}
22272273

22282274
@Bean
2229-
JdbcAggregateOperations operations(ApplicationEventPublisher publisher, RelationalMappingContext context,
2275+
BeforeConvertCallback<BeforeConvertCallbackForSaveBatch> callback() {
2276+
return aggregate -> {
2277+
aggregate.setId(UUID.randomUUID().toString());
2278+
return aggregate;
2279+
};
2280+
}
2281+
2282+
@Bean
2283+
JdbcAggregateOperations operations(ApplicationContext applicationContext, RelationalMappingContext context,
22302284
DataAccessStrategy dataAccessStrategy, JdbcConverter converter) {
2231-
return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy);
2285+
return new JdbcAggregateTemplate(applicationContext, context, converter, dataAccessStrategy);
22322286
}
22332287
}
22342288

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-db2.sql‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ DROP TABLE THIRD;
5959
DROP TABLE SEC;
6060
DROP TABLE FIRST;
6161

62+
DROP TABLE BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH;
63+
6264
CREATE TABLE LEGO_SET
6365
(
6466
"id1" BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
@@ -467,4 +469,10 @@ CREATE TABLE THIRD
467469
SEC BIGINT NOT NULL,
468470
NAME VARCHAR(20) NOT NULL,
469471
FOREIGN KEY (SEC) REFERENCES SEC (ID)
470-
);
472+
);
473+
474+
CREATE TABLE BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH
475+
(
476+
ID VARCHAR(36) PRIMARY KEY NOT NULL,
477+
NAME VARCHAR(20)
478+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-h2.sql‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,4 +417,10 @@ CREATE TABLE THIRD
417417
SEC BIGINT NOT NULL,
418418
NAME VARCHAR(20) NOT NULL,
419419
FOREIGN KEY (SEC) REFERENCES SEC (ID)
420-
);
420+
);
421+
422+
CREATE TABLE BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH
423+
(
424+
ID VARCHAR PRIMARY KEY,
425+
NAME VARCHAR
426+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-hsql.sql‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,4 +419,10 @@ CREATE TABLE THIRD
419419
SEC BIGINT NOT NULL,
420420
NAME VARCHAR(20) NOT NULL,
421421
FOREIGN KEY (SEC) REFERENCES SEC (ID)
422-
);
422+
);
423+
424+
CREATE TABLE BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH
425+
(
426+
ID VARCHAR PRIMARY KEY,
427+
NAME VARCHAR
428+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mariadb.sql‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,4 +391,10 @@ CREATE TABLE THIRD
391391
SEC BIGINT NOT NULL,
392392
NAME VARCHAR(20) NOT NULL,
393393
FOREIGN KEY (SEC) REFERENCES SEC (ID)
394-
);
394+
);
395+
396+
CREATE TABLE BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH
397+
(
398+
ID VARCHAR(36) PRIMARY KEY,
399+
NAME VARCHAR(20)
400+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mssql.sql‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,4 +441,12 @@ CREATE TABLE THIRD
441441
SEC BIGINT NOT NULL,
442442
NAME VARCHAR(20) NOT NULL,
443443
FOREIGN KEY (SEC) REFERENCES SEC (ID)
444-
);
444+
);
445+
446+
DROP TABLE BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH;
447+
448+
CREATE TABLE BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH
449+
(
450+
ID VARCHAR PRIMARY KEY,
451+
NAME VARCHAR
452+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mysql.sql‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,4 +397,10 @@ CREATE TABLE THIRD
397397
SEC BIGINT NOT NULL,
398398
NAME VARCHAR(20) NOT NULL,
399399
FOREIGN KEY (SEC) REFERENCES SEC (ID)
400-
);
400+
);
401+
402+
CREATE TABLE BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH
403+
(
404+
ID VARCHAR(36) PRIMARY KEY,
405+
NAME VARCHAR(20)
406+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-oracle.sql‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ DROP TABLE THIRD CASCADE CONSTRAINTS PURGE;
4949
DROP TABLE SEC CASCADE CONSTRAINTS PURGE;
5050
DROP TABLE FIRST CASCADE CONSTRAINTS PURGE;
5151

52+
DROP TABLE BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH CASCADE CONSTRAINTS PURGE;
53+
5254
CREATE TABLE LEGO_SET
5355
(
5456
"id1" NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY,
@@ -447,4 +449,10 @@ CREATE TABLE THIRD
447449
SEC NUMBER NOT NULL,
448450
NAME VARCHAR(20) NOT NULL,
449451
FOREIGN KEY (SEC) REFERENCES SEC (ID)
450-
);
452+
);
453+
454+
CREATE TABLE BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH
455+
(
456+
ID VARCHAR PRIMARY KEY,
457+
NAME VARCHAR
458+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-postgres.sql‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ DROP TABLE THIRD;
5252
DROP TABLE SEC;
5353
DROP TABLE FIRST;
5454

55+
DROP TABLE "BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH";
56+
5557
CREATE TABLE LEGO_SET
5658
(
5759
"id1" SERIAL PRIMARY KEY,
@@ -470,4 +472,10 @@ CREATE TABLE THIRD
470472
SEC BIGINT NOT NULL,
471473
NAME VARCHAR(20) NOT NULL,
472474
FOREIGN KEY (SEC) REFERENCES SEC (ID)
473-
);
475+
);
476+
477+
CREATE TABLE "BEFORE_CONVERT_CALLBACK_FOR_SAVE_BATCH"
478+
(
479+
ID VARCHAR PRIMARY KEY,
480+
NAME VARCHAR
481+
);

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /