Skip to content

Commit 348ea72

Browse files
committed
Add overloads to CassandraBatchOperations.insert(…)/update(…), and delete(…) accepting WriteOptions.
CassandraBatchOperations and its reactive variant provide now overloads accepting a single entity and WriteOptions. We also guard methods accepting varargs against being called with an accidental WriteOptions argument by inspecting the array of objects. Closes #1135
1 parent 88dde71 commit 348ea72

File tree

6 files changed

+214
-26
lines changed

6 files changed

+214
-26
lines changed

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
*/
1616
package org.springframework.data.cassandra.core;
1717

18+
import java.util.Collections;
19+
1820
import org.springframework.data.cassandra.core.cql.WriteOptions;
21+
import org.springframework.util.Assert;
1922

2023
/**
2124
* Batch operations for insert/update/delete actions on a table. {@link CassandraBatchOperations} use logged Cassandra
@@ -54,6 +57,22 @@ public interface CassandraBatchOperations {
5457
*/
5558
CassandraBatchOperations withTimestamp(long timestamp);
5659

60+
/**
61+
* Add an insert to the batch.
62+
*
63+
* @param entity the entity to insert; must not be {@literal null}.
64+
* @param options the WriteOptions to apply; must not be {@literal null}.
65+
* @return {@code this} {@link CassandraBatchOperations}.
66+
* @throws IllegalStateException if the batch was already executed.
67+
* @since 3.2.2
68+
*/
69+
default CassandraBatchOperations insert(Object entity, WriteOptions options) {
70+
71+
Assert.notNull(entity, "Entity must not be null");
72+
73+
return insert(Collections.singleton(entity), options);
74+
}
75+
5776
/**
5877
* Add an array of inserts to the batch.
5978
*
@@ -84,6 +103,22 @@ public interface CassandraBatchOperations {
84103
*/
85104
CassandraBatchOperations insert(Iterable<?> entities, WriteOptions options);
86105

106+
/**
107+
* Add an update to the batch.
108+
*
109+
* @param entity the entity to update; must not be {@literal null}.
110+
* @param options the WriteOptions to apply; must not be {@literal null}.
111+
* @return {@code this} {@link CassandraBatchOperations}.
112+
* @throws IllegalStateException if the batch was already executed.
113+
* @since 3.2.2
114+
*/
115+
default CassandraBatchOperations update(Object entity, WriteOptions options) {
116+
117+
Assert.notNull(entity, "Entity must not be null");
118+
119+
return insert(Collections.singleton(entity), options);
120+
}
121+
87122
/**
88123
* Add an array of updates to the batch.
89124
*
@@ -114,6 +149,22 @@ public interface CassandraBatchOperations {
114149
*/
115150
CassandraBatchOperations update(Iterable<?> entities, WriteOptions options);
116151

152+
/**
153+
* Add delete to the batch.
154+
*
155+
* @param entity the entity to delete; must not be {@literal null}.
156+
* @param options the WriteOptions to apply; must not be {@literal null}.
157+
* @return {@code this} {@link CassandraBatchOperations}.
158+
* @throws IllegalStateException if the batch was already executed.
159+
* @since 3.2.2
160+
*/
161+
default CassandraBatchOperations delete(Object entity, WriteOptions options) {
162+
163+
Assert.notNull(entity, "Entity must not be null");
164+
165+
return delete(Collections.singleton(entity), options);
166+
}
167+
117168
/**
118169
* Add an array of deletes to the batch.
119170
*

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.springframework.data.cassandra.core.convert.CassandraConverter;
2222
import org.springframework.data.cassandra.core.convert.UpdateMapper;
23+
import org.springframework.data.cassandra.core.cql.QueryOptions;
2324
import org.springframework.data.cassandra.core.cql.WriteOptions;
2425
import org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity;
2526
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
@@ -154,9 +155,9 @@ public CassandraBatchOperations insert(Iterable<?> entities) {
154155
public CassandraBatchOperations insert(Iterable<?> entities, WriteOptions options) {
155156

156157
assertNotExecuted();
157-
158158
Assert.notNull(entities, "Entities must not be null");
159159
Assert.notNull(options, "WriteOptions must not be null");
160+
assertNotQueryOptions(entities);
160161

161162
CassandraMappingContext mappingContext = getMappingContext();
162163

@@ -202,9 +203,9 @@ public CassandraBatchOperations update(Iterable<?> entities) {
202203
public CassandraBatchOperations update(Iterable<?> entities, WriteOptions options) {
203204

204205
assertNotExecuted();
205-
206206
Assert.notNull(entities, "Entities must not be null");
207207
Assert.notNull(options, "WriteOptions must not be null");
208+
assertNotQueryOptions(entities);
208209

209210
for (Object entity : entities) {
210211

@@ -247,9 +248,9 @@ public CassandraBatchOperations delete(Iterable<?> entities) {
247248
public CassandraBatchOperations delete(Iterable<?> entities, WriteOptions options) {
248249

249250
assertNotExecuted();
250-
251251
Assert.notNull(entities, "Entities must not be null");
252252
Assert.notNull(options, "WriteOptions must not be null");
253+
assertNotQueryOptions(entities);
253254

254255
for (Object entity : entities) {
255256

@@ -266,6 +267,17 @@ public CassandraBatchOperations delete(Iterable<?> entities, WriteOptions option
266267
return this;
267268
}
268269

270+
private void assertNotQueryOptions(Iterable<?> entities) {
271+
272+
for (Object entity : entities) {
273+
if (entity instanceof QueryOptions) {
274+
throw new IllegalArgumentException(
275+
String.format("%s must not be used as entity. Please make sure to call the appropriate method accepting %s",
276+
ClassUtils.getDescriptiveType(entity), ClassUtils.getShortName(entity.getClass())));
277+
}
278+
}
279+
}
280+
269281
private void assertNotExecuted() {
270282
Assert.state(!this.executed.get(), "This Cassandra Batch was already executed");
271283
}

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717

1818
import reactor.core.publisher.Mono;
1919

20+
import java.util.Collections;
21+
2022
import org.reactivestreams.Subscriber;
23+
2124
import org.springframework.data.cassandra.core.cql.WriteOptions;
25+
import org.springframework.util.Assert;
2226

2327
/**
2428
* Reactive Batch operations for insert/update/delete actions on a table. {@link ReactiveCassandraBatchOperations} use
@@ -58,6 +62,22 @@ public interface ReactiveCassandraBatchOperations {
5862
*/
5963
ReactiveCassandraBatchOperations withTimestamp(long timestamp);
6064

65+
/**
66+
* Add an insert to the batch.
67+
*
68+
* @param entity the entity to insert; must not be {@literal null}.
69+
* @param options the WriteOptions to apply; must not be {@literal null}.
70+
* @return {@code this} {@link ReactiveCassandraBatchOperations}.
71+
* @throws IllegalStateException if the batch was already executed.
72+
* @since 3.2.2
73+
*/
74+
default ReactiveCassandraBatchOperations insert(Object entity, WriteOptions options) {
75+
76+
Assert.notNull(entity, "Entity must not be null");
77+
78+
return insert(Collections.singleton(entity), options);
79+
}
80+
6181
/**
6282
* Add an array of inserts to the batch.
6383
*
@@ -107,6 +127,22 @@ public interface ReactiveCassandraBatchOperations {
107127
*/
108128
ReactiveCassandraBatchOperations insert(Mono<? extends Iterable<?>> entities, WriteOptions options);
109129

130+
/**
131+
* Add an update to the batch.
132+
*
133+
* @param entity the entity to update; must not be {@literal null}.
134+
* @param options the WriteOptions to apply; must not be {@literal null}.
135+
* @return {@code this} {@link ReactiveCassandraBatchOperations}.
136+
* @throws IllegalStateException if the batch was already executed.
137+
* @since 3.2.2
138+
*/
139+
default ReactiveCassandraBatchOperations update(Object entity, WriteOptions options) {
140+
141+
Assert.notNull(entity, "Entity must not be null");
142+
143+
return insert(Collections.singleton(entity), options);
144+
}
145+
110146
/**
111147
* Add an array of updates to the batch.
112148
*
@@ -156,6 +192,22 @@ public interface ReactiveCassandraBatchOperations {
156192
*/
157193
ReactiveCassandraBatchOperations update(Mono<? extends Iterable<?>> entities, WriteOptions options);
158194

195+
/**
196+
* Add delete to the batch.
197+
*
198+
* @param entity the entity to delete; must not be {@literal null}.
199+
* @param options the WriteOptions to apply; must not be {@literal null}.
200+
* @return {@code this} {@link ReactiveCassandraBatchOperations}.
201+
* @throws IllegalStateException if the batch was already executed.
202+
* @since 3.2.2
203+
*/
204+
default ReactiveCassandraBatchOperations delete(Object entity, WriteOptions options) {
205+
206+
Assert.notNull(entity, "Entity must not be null");
207+
208+
return delete(Collections.singleton(entity), options);
209+
}
210+
159211
/**
160212
* Add an array of deletes to the batch.
161213
*

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

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@
1515
*/
1616
package org.springframework.data.cassandra.core;
1717

18+
import reactor.core.publisher.Flux;
19+
import reactor.core.publisher.Mono;
20+
1821
import java.util.ArrayList;
1922
import java.util.Arrays;
2023
import java.util.Collection;
2124
import java.util.List;
2225
import java.util.concurrent.CopyOnWriteArrayList;
2326
import java.util.concurrent.atomic.AtomicBoolean;
2427

25-
import reactor.core.publisher.Flux;
26-
import reactor.core.publisher.Mono;
2728
import org.springframework.data.cassandra.core.convert.CassandraConverter;
2829
import org.springframework.data.cassandra.core.convert.UpdateMapper;
30+
import org.springframework.data.cassandra.core.cql.QueryOptions;
2931
import org.springframework.data.cassandra.core.cql.WriteOptions;
3032
import org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity;
3133
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
@@ -193,9 +195,9 @@ public ReactiveCassandraBatchOperations insert(Mono<? extends Iterable<?>> entit
193195
public ReactiveCassandraBatchOperations insert(Iterable<?> entities, WriteOptions options) {
194196

195197
assertNotExecuted();
196-
197198
Assert.notNull(entities, "Entities must not be null");
198199
Assert.notNull(options, "WriteOptions must not be null");
200+
assertNotQueryOptions(entities);
199201

200202
this.batchMonos.add(Mono.just(doInsert(entities, options)));
201203

@@ -209,7 +211,6 @@ public ReactiveCassandraBatchOperations insert(Iterable<?> entities, WriteOption
209211
public ReactiveCassandraBatchOperations insert(Mono<? extends Iterable<?>> entities, WriteOptions options) {
210212

211213
assertNotExecuted();
212-
213214
Assert.notNull(entities, "Entities must not be null");
214215
Assert.notNull(options, "WriteOptions must not be null");
215216

@@ -273,9 +274,9 @@ public ReactiveCassandraBatchOperations update(Mono<? extends Iterable<?>> entit
273274
public ReactiveCassandraBatchOperations update(Iterable<?> entities, WriteOptions options) {
274275

275276
assertNotExecuted();
276-
277277
Assert.notNull(entities, "Entities must not be null");
278278
Assert.notNull(options, "WriteOptions must not be null");
279+
assertNotQueryOptions(entities);
279280

280281
this.batchMonos.add(Mono.just(doUpdate(entities, options)));
281282

@@ -289,7 +290,6 @@ public ReactiveCassandraBatchOperations update(Iterable<?> entities, WriteOption
289290
public ReactiveCassandraBatchOperations update(Mono<? extends Iterable<?>> entities, WriteOptions options) {
290291

291292
assertNotExecuted();
292-
293293
Assert.notNull(entities, "Entities must not be null");
294294
Assert.notNull(options, "WriteOptions must not be null");
295295

@@ -351,9 +351,9 @@ public ReactiveCassandraBatchOperations delete(Mono<? extends Iterable<?>> entit
351351
public ReactiveCassandraBatchOperations delete(Iterable<?> entities, WriteOptions options) {
352352

353353
assertNotExecuted();
354-
355354
Assert.notNull(entities, "Entities must not be null");
356355
Assert.notNull(options, "WriteOptions must not be null");
356+
assertNotQueryOptions(entities);
357357

358358
this.batchMonos.add(Mono.just(doDelete(entities, options)));
359359

@@ -367,7 +367,6 @@ public ReactiveCassandraBatchOperations delete(Iterable<?> entities, WriteOption
367367
public ReactiveCassandraBatchOperations delete(Mono<? extends Iterable<?>> entities, WriteOptions options) {
368368

369369
assertNotExecuted();
370-
371370
Assert.notNull(entities, "Entities must not be null");
372371
Assert.notNull(options, "WriteOptions must not be null");
373372

@@ -376,6 +375,17 @@ public ReactiveCassandraBatchOperations delete(Mono<? extends Iterable<?>> entit
376375
return this;
377376
}
378377

378+
private void assertNotQueryOptions(Iterable<?> entities) {
379+
380+
for (Object entity : entities) {
381+
if (entity instanceof QueryOptions) {
382+
throw new IllegalArgumentException(
383+
String.format("%s must not be used as entity. Please make sure to call the appropriate method accepting %s",
384+
ClassUtils.getDescriptiveType(entity), ClassUtils.getShortName(entity.getClass())));
385+
}
386+
}
387+
}
388+
379389
private Collection<SimpleStatement> doDelete(Iterable<?> entities, WriteOptions options) {
380390

381391
List<SimpleStatement> deleteQueries = new ArrayList<>();

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

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import static org.assertj.core.api.Assertions.*;
1919

2020
import java.util.Arrays;
21-
import java.util.Collections;
2221
import java.util.concurrent.TimeUnit;
2322

2423
import org.junit.jupiter.api.BeforeEach;
@@ -73,6 +72,12 @@ void shouldInsertEntities() {
7372
assertThat(loaded.getId().getUsername()).isEqualTo(walter.getId().getUsername());
7473
}
7574

75+
@Test // #1135
76+
void insertAsVarargsShouldRejectQueryOptions() {
77+
assertThatIllegalArgumentException()
78+
.isThrownBy(() -> template.batchOps().insert(mike, walter, InsertOptions.empty()));
79+
}
80+
7681
@Test // DATACASS-288
7782
void shouldInsertEntitiesWithLwt() {
7883

@@ -85,7 +90,8 @@ void shouldInsertEntitiesWithLwt() {
8590
walter.setAge(100);
8691

8792
CassandraBatchOperations batchOperations = new CassandraBatchTemplate(template);
88-
WriteResult writeResult = batchOperations.insert(Collections.singleton(walter), lwtOptions).insert(mike).execute();
93+
94+
WriteResult writeResult = batchOperations.insert(walter, lwtOptions).insert(mike).execute();
8995

9096
Group loadedWalter = template.selectOneById(walter.getId(), Group.class);
9197
Group loadedMike = template.selectOneById(mike.getId(), Group.class);
@@ -131,6 +137,12 @@ void shouldInsertCollectionOfEntitiesWithTtl() {
131137
}
132138
}
133139

140+
@Test // #1135
141+
void updateAsVarargsShouldRejectQueryOptions() {
142+
assertThatIllegalArgumentException()
143+
.isThrownBy(() -> template.batchOps().update(mike, walter, InsertOptions.empty()));
144+
}
145+
134146
@Test // DATACASS-288
135147
void shouldUpdateEntities() {
136148

@@ -169,14 +181,19 @@ void shouldUpdateCollectionOfEntitiesWithTtl() {
169181
WriteOptions options = WriteOptions.builder().ttl(ttl).build();
170182

171183
CassandraBatchOperations batchOperations = new CassandraBatchTemplate(template);
172-
batchOperations.update(Arrays.asList(walter, mike), options).execute();
184+
batchOperations.update(walter, options).execute();
173185

174-
ResultSet resultSet = template.getCqlOperations().queryForResultSet("SELECT TTL(email) FROM group;");
186+
ResultSet resultSet = template.getCqlOperations().queryForResultSet("SELECT TTL(email), email FROM group");
175187

176188
assertThat(resultSet.getAvailableWithoutFetching()).isEqualTo(2);
177189

178190
for (Row row : resultSet) {
179-
assertThat(row.getInt(0)).isBetween(1, ttl);
191+
192+
if (walter.getEmail().equals(row.getString("email"))) {
193+
assertThat(row.getInt(0)).isBetween(1, ttl);
194+
} else {
195+
assertThat(row.getInt(0)).isZero();
196+
}
180197
}
181198
}
182199

@@ -200,6 +217,12 @@ void shouldUpdatesCollectionOfEntities() {
200217
assertThat(loaded.getEmail()).isEqualTo(walter.getEmail());
201218
}
202219

220+
@Test // #1135
221+
void deleteAsVarargsShouldRejectQueryOptions() {
222+
assertThatIllegalArgumentException()
223+
.isThrownBy(() -> template.batchOps().delete(mike, walter, InsertOptions.empty()));
224+
}
225+
203226
@Test // DATACASS-288
204227
void shouldDeleteEntities() {
205228

0 commit comments

Comments
 (0)