Skip to content

Commit 13d266d

Browse files
committed
Use BeforeConvertCallback.onBeforeConvert(…) outcome for the actual insert.
We now use correctly the result of the `onBeforeConvert` callback for insert instead of the original entity. Closes #1295
1 parent 06b87d4 commit 13d266d

File tree

4 files changed

+138
-60
lines changed

4 files changed

+138
-60
lines changed

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java

+29-15
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,8 @@ public <T> ListenableFuture<List<T>> select(Query query, Class<T> entityClass) t
436436
Assert.notNull(query, "Query must not be null");
437437
Assert.notNull(entityClass, "Entity type must not be null");
438438

439-
return select(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)).build(), entityClass);
439+
return select(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass))
440+
.build(), entityClass);
440441
}
441442

442443
/* (non-Javadoc)
@@ -450,7 +451,8 @@ public <T> ListenableFuture<Void> select(Query query, Consumer<T> entityConsumer
450451
Assert.notNull(entityConsumer, "Entity Consumer must not be empty");
451452
Assert.notNull(entityClass, "Entity type must not be null");
452453

453-
return select(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)).build(), entityConsumer,
454+
return select(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass))
455+
.build(), entityConsumer,
454456
entityClass);
455457
}
456458

@@ -463,7 +465,8 @@ public <T> ListenableFuture<T> selectOne(Query query, Class<T> entityClass) thro
463465
Assert.notNull(query, "Query must not be null");
464466
Assert.notNull(entityClass, "Entity type must not be null");
465467

466-
return selectOne(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)).build(),
468+
return selectOne(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass))
469+
.build(),
467470
entityClass);
468471
}
469472

@@ -476,7 +479,8 @@ public <T> ListenableFuture<Slice<T>> slice(Query query, Class<T> entityClass) t
476479
Assert.notNull(query, "Query must not be null");
477480
Assert.notNull(entityClass, "Entity type must not be null");
478481

479-
return slice(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)).build(), entityClass);
482+
return slice(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass))
483+
.build(), entityClass);
480484
}
481485

482486
/* (non-Javadoc)
@@ -490,7 +494,8 @@ public ListenableFuture<Boolean> update(Query query, org.springframework.data.ca
490494
Assert.notNull(update, "Update must not be null");
491495
Assert.notNull(entityClass, "Entity type must not be null");
492496

493-
return doExecute(getStatementFactory().update(query, update, getRequiredPersistentEntity(entityClass)).build(),
497+
return doExecute(getStatementFactory().update(query, update, getRequiredPersistentEntity(entityClass))
498+
.build(),
494499
AsyncResultSet::wasApplied);
495500
}
496501

@@ -516,7 +521,8 @@ private ListenableFuture<Boolean> doDelete(Query query, Class<?> entityClass, Cq
516521

517522
ListenableFuture<Boolean> future = doExecute(delete, AsyncResultSet::wasApplied);
518523

519-
future.addCallback(success -> maybeEmitEvent(new AfterDeleteEvent<>(delete, entityClass, tableName)), e -> {});
524+
future.addCallback(success -> maybeEmitEvent(new AfterDeleteEvent<>(delete, entityClass, tableName)), e -> {
525+
});
520526

521527
return future;
522528
}
@@ -559,7 +565,8 @@ ListenableFuture<Long> doCount(Query query, Class<?> entityClass, CqlIdentifier
559565

560566
SingleColumnRowMapper<Long> mapper = SingleColumnRowMapper.newInstance(Long.class);
561567

562-
Row row = DataAccessUtils.requiredSingleResult(Streamable.of(it.currentPage()).toList());
568+
Row row = DataAccessUtils.requiredSingleResult(Streamable.of(it.currentPage())
569+
.toList());
563570
return mapper.mapRow(row, 0);
564571
});
565572

@@ -642,7 +649,7 @@ private <T> ListenableFuture<EntityWriteResult<T>> doInsert(T entity, WriteOptio
642649
getConverter().getConversionService());
643650
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
644651

645-
T entityToUse = source.isVersionedEntity() ? source.initializeVersionProperty() : entity;
652+
T entityToUse = source.isVersionedEntity() ? source.initializeVersionProperty() : source.getBean();
646653

647654
StatementBuilder<RegularInsert> builder = getStatementFactory().insert(entityToUse, options, persistentEntity,
648655
tableName);
@@ -762,7 +769,8 @@ private ListenableFuture<WriteResult> doDeleteVersioned(Object entity, QueryOpti
762769
StatementBuilder<Delete> delete = getStatementFactory().delete(entity, options, getConverter(), tableName);
763770
;
764771

765-
return executeDelete(entity, tableName, source.appendVersionCondition(delete).build(), result -> {
772+
return executeDelete(entity, tableName, source.appendVersionCondition(delete)
773+
.build(), result -> {
766774

767775
if (!result.wasApplied()) {
768776
throw new OptimisticLockingFailureException(
@@ -776,7 +784,8 @@ private ListenableFuture<WriteResult> doDelete(Object entity, QueryOptions optio
776784

777785
StatementBuilder<Delete> delete = getStatementFactory().delete(entity, options, getConverter(), tableName);
778786

779-
return executeDelete(entity, tableName, delete.build(), result -> {});
787+
return executeDelete(entity, tableName, delete.build(), result -> {
788+
});
780789
}
781790

782791
/* (non-Javadoc)
@@ -797,7 +806,8 @@ public ListenableFuture<Boolean> deleteById(Object id, Class<?> entityClass) {
797806
maybeEmitEvent(new BeforeDeleteEvent<>(delete, entityClass, tableName));
798807

799808
ListenableFuture<Boolean> future = doExecute(delete, AsyncResultSet::wasApplied);
800-
future.addCallback(success -> maybeEmitEvent(new AfterDeleteEvent<>(delete, entityClass, tableName)), e -> {});
809+
future.addCallback(success -> maybeEmitEvent(new AfterDeleteEvent<>(delete, entityClass, tableName)), e -> {
810+
});
801811

802812
return future;
803813
}
@@ -817,7 +827,8 @@ public ListenableFuture<Void> truncate(Class<?> entityClass) {
817827
maybeEmitEvent(new BeforeDeleteEvent<>(statement, entityClass, tableName));
818828

819829
ListenableFuture<Boolean> future = doExecute(statement, AsyncResultSet::wasApplied);
820-
future.addCallback(success -> maybeEmitEvent(new AfterDeleteEvent<>(statement, entityClass, tableName)), e -> {});
830+
future.addCallback(success -> maybeEmitEvent(new AfterDeleteEvent<>(statement, entityClass, tableName)), e -> {
831+
});
821832

822833
return new MappingListenableFutureAdapter<>(future, aBoolean -> null);
823834
}
@@ -842,7 +853,8 @@ protected AsyncPreparedStatementHandler createPreparedStatementHandler(Statement
842853
private <T> ListenableFuture<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName,
843854
SimpleStatement statement) {
844855

845-
return executeSave(entity, tableName, statement, ignore -> {});
856+
return executeSave(entity, tableName, statement, ignore -> {
857+
});
846858
}
847859

848860
private <T> ListenableFuture<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName,
@@ -926,11 +938,13 @@ private <T> ListenableFuture<T> doExecute(Statement<?> statement, Function<Async
926938
}
927939

928940
private static List<Row> getFirstPage(AsyncResultSet resultSet) {
929-
return StreamSupport.stream(resultSet.currentPage().spliterator(), false).collect(Collectors.toList());
941+
return StreamSupport.stream(resultSet.currentPage().spliterator(), false)
942+
.collect(Collectors.toList());
930943
}
931944

932945
private static int getConfiguredPageSize(CqlSession session) {
933-
return session.getContext().getConfig().getDefaultProfile().getInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 5000);
946+
return session.getContext().getConfig().getDefaultProfile()
947+
.getInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 5000);
934948
}
935949

936950
private int getEffectivePageSize(Statement<?> statement) {

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java

+11-6
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ <T> EntityWriteResult<T> doInsert(T entity, WriteOptions options, CqlIdentifier
669669
AdaptibleEntity<T> source = getEntityOperations().forEntity(maybeCallBeforeConvert(entity, tableName),
670670
getConverter().getConversionService());
671671

672-
T entityToUse = source.isVersionedEntity() ? source.initializeVersionProperty() : entity;
672+
T entityToUse = source.isVersionedEntity() ? source.initializeVersionProperty() : source.getBean();
673673

674674
StatementBuilder<RegularInsert> builder = getStatementFactory().insert(entityToUse, options,
675675
source.getPersistentEntity(), tableName);
@@ -736,7 +736,8 @@ private <T> EntityWriteResult<T> doUpdateVersioned(T entity, UpdateOptions optio
736736
T toSave = source.incrementVersion();
737737

738738
StatementBuilder<Update> builder = getStatementFactory().update(toSave, options, persistentEntity, tableName);
739-
SimpleStatement update = source.appendVersionCondition(builder, previousVersion).build();
739+
SimpleStatement update = source.appendVersionCondition(builder, previousVersion)
740+
.build();
740741

741742
return executeSave(toSave, tableName, update, result -> {
742743

@@ -780,7 +781,8 @@ public WriteResult delete(Object entity, QueryOptions options) {
780781
StatementBuilder<Delete> builder = getStatementFactory().delete(entity, options, getConverter(), tableName);
781782

782783
return source.isVersionedEntity()
783-
? doDeleteVersioned(source.appendVersionCondition(builder).build(), entity, source, tableName)
784+
? doDeleteVersioned(source.appendVersionCondition(builder)
785+
.build(), entity, source, tableName)
784786
: doDelete(builder.build(), entity, tableName);
785787

786788
}
@@ -799,7 +801,8 @@ private WriteResult doDeleteVersioned(SimpleStatement statement, Object entity,
799801
}
800802

801803
private WriteResult doDelete(SimpleStatement delete, Object entity, CqlIdentifier tableName) {
802-
return executeDelete(entity, tableName, delete, result -> {});
804+
return executeDelete(entity, tableName, delete, result -> {
805+
});
803806
}
804807

805808
/* (non-Javadoc)
@@ -899,7 +902,8 @@ protected PreparedStatementHandler createPreparedStatementHandler(Statement<?> s
899902
}
900903

901904
private <T> EntityWriteResult<T> executeSave(T entity, CqlIdentifier tableName, SimpleStatement statement) {
902-
return executeSave(entity, tableName, statement, ignore -> {});
905+
return executeSave(entity, tableName, statement, ignore -> {
906+
});
903907
}
904908

905909
private <T> EntityWriteResult<T> executeSave(T entity, CqlIdentifier tableName, SimpleStatement statement,
@@ -976,7 +980,8 @@ private <T> T doExecute(Statement<?> statement, Function<ResultSet, T> mappingFu
976980
}
977981

978982
private int getConfiguredPageSize(CqlSession session) {
979-
return session.getContext().getConfig().getDefaultProfile().getInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 5000);
983+
return session.getContext().getConfig().getDefaultProfile()
984+
.getInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 5000);
980985
}
981986

982987
@SuppressWarnings("ConstantConditions")

spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/AsyncCassandraTemplateUnitTests.java

+39-19
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,26 @@
1515
*/
1616
package org.springframework.data.cassandra.core;
1717

18-
import static org.assertj.core.api.Assertions.*;
19-
import static org.mockito.ArgumentMatchers.*;
20-
import static org.mockito.Mockito.*;
21-
import static org.springframework.data.cassandra.core.query.Criteria.*;
22-
2318
import java.util.ArrayList;
2419
import java.util.Collections;
2520
import java.util.List;
2621
import java.util.concurrent.CompletableFuture;
2722
import java.util.concurrent.ExecutionException;
2823
import java.util.concurrent.Future;
2924

25+
import com.datastax.oss.driver.api.core.CqlIdentifier;
26+
import com.datastax.oss.driver.api.core.CqlSession;
27+
import com.datastax.oss.driver.api.core.NoNodeAvailableException;
28+
import com.datastax.oss.driver.api.core.context.DriverContext;
29+
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
30+
import com.datastax.oss.driver.api.core.cql.ColumnDefinition;
31+
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
32+
import com.datastax.oss.driver.api.core.cql.Row;
33+
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
34+
import com.datastax.oss.driver.api.core.cql.Statement;
35+
import com.datastax.oss.driver.api.core.type.DataTypes;
36+
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
37+
import com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry;
3038
import org.junit.jupiter.api.BeforeEach;
3139
import org.junit.jupiter.api.Test;
3240
import org.junit.jupiter.api.extension.ExtendWith;
@@ -48,19 +56,10 @@
4856
import org.springframework.data.mapping.callback.EntityCallbacks;
4957
import org.springframework.util.concurrent.ListenableFuture;
5058

51-
import com.datastax.oss.driver.api.core.CqlIdentifier;
52-
import com.datastax.oss.driver.api.core.CqlSession;
53-
import com.datastax.oss.driver.api.core.NoNodeAvailableException;
54-
import com.datastax.oss.driver.api.core.context.DriverContext;
55-
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
56-
import com.datastax.oss.driver.api.core.cql.ColumnDefinition;
57-
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
58-
import com.datastax.oss.driver.api.core.cql.Row;
59-
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
60-
import com.datastax.oss.driver.api.core.cql.Statement;
61-
import com.datastax.oss.driver.api.core.type.DataTypes;
62-
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
63-
import com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry;
59+
import static org.assertj.core.api.Assertions.*;
60+
import static org.mockito.ArgumentMatchers.*;
61+
import static org.mockito.Mockito.*;
62+
import static org.springframework.data.cassandra.core.query.Criteria.*;
6463

6564
/**
6665
* Unit tests for {@link AsyncCassandraTemplate}.
@@ -326,7 +325,28 @@ void insertShouldInsertEntity() {
326325
assertThat(beforeSave).isSameAs(user);
327326
}
328327

329-
@Test // DATACASS-618
328+
@Test
329+
// GH-1295
330+
void insertShouldConsiderEntityAfterCallback() {
331+
332+
when(resultSet.wasApplied()).thenReturn(true);
333+
334+
User user = new User("heisenberg", "Walter", "White");
335+
336+
EntityCallbacks callbacks = EntityCallbacks.create();
337+
callbacks.addEntityCallback((BeforeConvertCallback<Object>) (entity, tableName) -> new User("ww", "Walter", "White"));
338+
template.setEntityCallbacks(callbacks);
339+
340+
ListenableFuture<User> future = template.insert(user);
341+
342+
assertThat(getUninterruptibly(future)).isNotSameAs(user);
343+
verify(session).executeAsync(statementCaptor.capture());
344+
assertThat(render(statementCaptor.getValue()))
345+
.isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES ('Walter','ww','White')");
346+
}
347+
348+
@Test
349+
// DATACASS-618
330350
void insertShouldInsertVersionedEntity() {
331351

332352
when(resultSet.wasApplied()).thenReturn(true);

spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateUnitTests.java

+59-20
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,22 @@
1515
*/
1616
package org.springframework.data.cassandra.core;
1717

18-
import static org.assertj.core.api.Assertions.*;
19-
import static org.mockito.ArgumentMatchers.*;
20-
import static org.mockito.Mockito.*;
21-
import static org.springframework.data.cassandra.core.query.Criteria.*;
22-
2318
import java.util.Collections;
2419
import java.util.List;
2520

21+
import com.datastax.oss.driver.api.core.CqlIdentifier;
22+
import com.datastax.oss.driver.api.core.CqlSession;
23+
import com.datastax.oss.driver.api.core.NoNodeAvailableException;
24+
import com.datastax.oss.driver.api.core.context.DriverContext;
25+
import com.datastax.oss.driver.api.core.cql.ColumnDefinition;
26+
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
27+
import com.datastax.oss.driver.api.core.cql.ResultSet;
28+
import com.datastax.oss.driver.api.core.cql.Row;
29+
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
30+
import com.datastax.oss.driver.api.core.cql.Statement;
31+
import com.datastax.oss.driver.api.core.type.DataTypes;
32+
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
33+
import com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry;
2634
import org.junit.jupiter.api.BeforeEach;
2735
import org.junit.jupiter.api.Test;
2836
import org.junit.jupiter.api.extension.ExtendWith;
@@ -43,19 +51,10 @@
4351
import org.springframework.data.cassandra.domain.VersionedUser;
4452
import org.springframework.data.mapping.callback.EntityCallbacks;
4553

46-
import com.datastax.oss.driver.api.core.CqlIdentifier;
47-
import com.datastax.oss.driver.api.core.CqlSession;
48-
import com.datastax.oss.driver.api.core.NoNodeAvailableException;
49-
import com.datastax.oss.driver.api.core.context.DriverContext;
50-
import com.datastax.oss.driver.api.core.cql.ColumnDefinition;
51-
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
52-
import com.datastax.oss.driver.api.core.cql.ResultSet;
53-
import com.datastax.oss.driver.api.core.cql.Row;
54-
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
55-
import com.datastax.oss.driver.api.core.cql.Statement;
56-
import com.datastax.oss.driver.api.core.type.DataTypes;
57-
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
58-
import com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry;
54+
import static org.assertj.core.api.Assertions.*;
55+
import static org.mockito.ArgumentMatchers.*;
56+
import static org.mockito.Mockito.*;
57+
import static org.springframework.data.cassandra.core.query.Criteria.*;
5958

6059
/**
6160
* Unit tests for {@link CassandraTemplate}.
@@ -307,7 +306,27 @@ void insertShouldInsertEntity() {
307306
assertThat(beforeSave).isSameAs(user);
308307
}
309308

310-
@Test // DATACASS-618
309+
@Test
310+
// GH-1295
311+
void insertShouldConsiderEntityAfterCallback() {
312+
313+
when(resultSet.wasApplied()).thenReturn(true);
314+
315+
User user = new User("heisenberg", "Walter", "White");
316+
317+
EntityCallbacks callbacks = EntityCallbacks.create();
318+
callbacks.addEntityCallback((BeforeConvertCallback<Object>) (entity, tableName) -> new User("ww", "Walter", "White"));
319+
template.setEntityCallbacks(callbacks);
320+
321+
template.insert(user);
322+
323+
verify(session).execute(statementCaptor.capture());
324+
assertThat(render(statementCaptor.getValue()))
325+
.isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES ('Walter','ww','White')");
326+
}
327+
328+
@Test
329+
// DATACASS-618
311330
void insertShouldInsertVersionedEntity() {
312331

313332
when(resultSet.wasApplied()).thenReturn(true);
@@ -323,7 +342,27 @@ void insertShouldInsertVersionedEntity() {
323342
assertThat(beforeSave).isSameAs(user);
324343
}
325344

326-
@Test // DATACASS-250
345+
@Test
346+
// GH-1295
347+
void insertShouldInsertVersionedEntityAfterCallback() {
348+
349+
when(resultSet.wasApplied()).thenReturn(true);
350+
351+
VersionedUser user = new VersionedUser("heisenberg", "Walter", "White");
352+
353+
EntityCallbacks callbacks = EntityCallbacks.create();
354+
callbacks.addEntityCallback((BeforeConvertCallback<Object>) (entity, tableName) -> new VersionedUser("ww", "Walter", "White"));
355+
template.setEntityCallbacks(callbacks);
356+
357+
template.insert(user);
358+
359+
verify(session).execute(statementCaptor.capture());
360+
assertThat(render(statementCaptor.getValue())).isEqualTo(
361+
"INSERT INTO vusers (firstname,id,lastname,version) VALUES ('Walter','ww','White',0) IF NOT EXISTS");
362+
}
363+
364+
@Test
365+
// DATACASS-250
327366
void insertShouldInsertWithOptionsEntity() {
328367

329368
InsertOptions insertOptions = InsertOptions.builder().withIfNotExists().build();

0 commit comments

Comments
 (0)