DATACASS-250 - Support lightweight transactions.
We now support lightweight transactions via InsertOptions and UpdateOptions. Options can be used with the imperative, asynchronous and reactive templates using insert(…) and update(…) write methods. Write methods do not return the entity if the operation is not applied because of a lightweight transaction. Specifically, the resulting entity is null using the imperative and asynchronous Cassandra templates.
The reactive Cassandra template suppresses particular entities (inserted/updated through a entity stream) if its write operation was not applied due to a lightweight transaction.
InsertOptions lwtInsertOptions = InsertOptions.builder().withIfNotExists().build();
User user = new User("heisenberg", "Walter", "White");
User inserted = template.insert(user, lwtInsertOptions);
UpdateOptions lwtUpdateOptions = UpdateOptions.builder().withIfExists().build();
User user = new User("heisenberg", "Walter", "White");
User updated = template.update(user, lwtUpdateOptions);
This commit is contained in:
@@ -37,7 +37,10 @@ import com.datastax.driver.core.Statement;
|
||||
* @author John Blum
|
||||
* @since 2.0
|
||||
* @see AsyncCassandraTemplate
|
||||
* @see CassandraOperations
|
||||
* @see AsyncCqlOperations
|
||||
* @see Statement
|
||||
* @see InsertOptions
|
||||
* @see UpdateOptions
|
||||
*/
|
||||
public interface AsyncCassandraOperations {
|
||||
|
||||
@@ -234,10 +237,11 @@ public interface AsyncCassandraOperations {
|
||||
*
|
||||
* @param entity The entity to insert, must not be {@literal null}.
|
||||
* @param options may be {@literal null}.
|
||||
* @return the inserted entity.
|
||||
* @return the inserted entity or a {@literal null} inside of {@link ListenableFuture} if the {@code INSERT} operation
|
||||
* was not applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> insert(T entity, WriteOptions options) throws DataAccessException;
|
||||
<T> ListenableFuture<T> insert(T entity, InsertOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Update the given entity and return the entity if the update was applied.
|
||||
@@ -253,10 +257,11 @@ public interface AsyncCassandraOperations {
|
||||
*
|
||||
* @param entity The entity to update, must not be {@literal null}.
|
||||
* @param options may be {@literal null}.
|
||||
* @return the updated entity.
|
||||
* @return the updated entityor a {@literal null} inside of {@link ListenableFuture} if the {@code UPDATE} operation
|
||||
* was not applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> update(T entity, WriteOptions options) throws DataAccessException;
|
||||
<T> ListenableFuture<T> update(T entity, UpdateOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity and return the entity if the delete was applied.
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.data.cql.core.CqlIdentifier;
|
||||
import org.springframework.data.cql.core.CqlProvider;
|
||||
import org.springframework.data.cql.core.GuavaListenableFutureAdapter;
|
||||
import org.springframework.data.cql.core.QueryOptions;
|
||||
import org.springframework.data.cql.core.WriteOptions;
|
||||
import org.springframework.data.cql.core.session.DefaultSessionFactory;
|
||||
import org.springframework.data.cql.core.session.SessionFactory;
|
||||
import org.springframework.data.cql.support.CqlExceptionTranslator;
|
||||
@@ -424,10 +423,10 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#insert(java.lang.Object, org.springframework.data.cql.core.WriteOptions)
|
||||
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#insert(java.lang.Object, org.springframework.data.cassandra.core.InsertOptions)
|
||||
*/
|
||||
@Override
|
||||
public <T> ListenableFuture<T> insert(T entity, WriteOptions options) {
|
||||
public <T> ListenableFuture<T> insert(T entity, InsertOptions options) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
|
||||
@@ -448,10 +447,10 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#update(java.lang.Object, org.springframework.data.cql.core.WriteOptions)
|
||||
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#update(java.lang.Object, org.springframework.data.cassandra.core.UpdateOptions)
|
||||
*/
|
||||
@Override
|
||||
public <T> ListenableFuture<T> update(T entity, WriteOptions options) {
|
||||
public <T> ListenableFuture<T> update(T entity, UpdateOptions options) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ import com.datastax.driver.core.Statement;
|
||||
* @see CassandraTemplate
|
||||
* @see CqlOperations
|
||||
* @see Statement
|
||||
* @see InsertOptions
|
||||
* @see UpdateOptions
|
||||
*/
|
||||
public interface CassandraOperations {
|
||||
|
||||
@@ -266,10 +268,10 @@ public interface CassandraOperations {
|
||||
*
|
||||
* @param entity The entity to insert, must not be {@literal null}.
|
||||
* @param options may be {@literal null}.
|
||||
* @return the inserted entity.
|
||||
* @return the inserted entity or {@literal null} if the {@code INSERT} operation was not applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> T insert(T entity, WriteOptions options) throws DataAccessException;
|
||||
<T> T insert(T entity, InsertOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Update the given entity and return the entity if the update was applied.
|
||||
@@ -285,10 +287,10 @@ public interface CassandraOperations {
|
||||
*
|
||||
* @param entity The entity to update, must not be {@literal null}.
|
||||
* @param options may be {@literal null}.
|
||||
* @return the updated entity.
|
||||
* @return the updated entity or {@literal null} if the {@code UPDATE} operation was not applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> T update(T entity, WriteOptions options) throws DataAccessException;
|
||||
<T> T update(T entity, UpdateOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity and return the entity if the delete was applied.
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.springframework.data.cql.core.CqlProvider;
|
||||
import org.springframework.data.cql.core.CqlTemplate;
|
||||
import org.springframework.data.cql.core.QueryOptions;
|
||||
import org.springframework.data.cql.core.SessionCallback;
|
||||
import org.springframework.data.cql.core.WriteOptions;
|
||||
import org.springframework.data.cql.core.session.DefaultSessionFactory;
|
||||
import org.springframework.data.cql.core.session.SessionFactory;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
@@ -433,10 +432,10 @@ public class CassandraTemplate implements CassandraOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.CassandraOperations#insert(java.lang.Object, org.springframework.data.cql.core.WriteOptions)
|
||||
* @see org.springframework.data.cassandra.core.CassandraOperations#insert(java.lang.Object, org.springframework.data.cassandra.core.InsertOptions)
|
||||
*/
|
||||
@Override
|
||||
public <T> T insert(T entity, WriteOptions options) {
|
||||
public <T> T insert(T entity, InsertOptions options) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
|
||||
@@ -456,10 +455,10 @@ public class CassandraTemplate implements CassandraOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.CassandraOperations#update(java.lang.Object, org.springframework.data.cql.core.WriteOptions)
|
||||
* @see org.springframework.data.cassandra.core.CassandraOperations#update(java.lang.Object, org.springframework.data.cassandra.core.UpdateOptions)
|
||||
*/
|
||||
@Override
|
||||
public <T> T update(T entity, WriteOptions options) {
|
||||
public <T> T update(T entity, UpdateOptions options) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.core;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.cql.core.WriteOptions;
|
||||
|
||||
/**
|
||||
* Extension to {@link WriteOptions} for use with {@code INSERT} operations.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class InsertOptions extends WriteOptions {
|
||||
|
||||
private boolean ifNotExists;
|
||||
|
||||
/**
|
||||
* Creates new {@link InsertOptions}.
|
||||
*/
|
||||
InsertOptions() {}
|
||||
|
||||
/**
|
||||
* Create a new {@link InsertOptionsBuilder}.
|
||||
*
|
||||
* @return a new {@link InsertOptionsBuilder}.
|
||||
*/
|
||||
public static InsertOptionsBuilder builder() {
|
||||
return new InsertOptionsBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} to apply {@code IF NOT EXISTS} to {@code INSERT} operations.
|
||||
*/
|
||||
public boolean isIfNotExists() {
|
||||
return ifNotExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link InsertOptions}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public static class InsertOptionsBuilder extends WriteOptionsBuilder {
|
||||
|
||||
private boolean ifNotExists;
|
||||
|
||||
private InsertOptionsBuilder() {}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#consistencyLevel(com.datastax.driver.core.ConsistencyLevel)
|
||||
*/
|
||||
@Override
|
||||
public InsertOptionsBuilder consistencyLevel(com.datastax.driver.core.ConsistencyLevel consistencyLevel) {
|
||||
return (InsertOptionsBuilder) super.consistencyLevel(consistencyLevel);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#retryPolicy(org.springframework.data.cql.core.RetryPolicy)
|
||||
*/
|
||||
@Override
|
||||
public InsertOptionsBuilder retryPolicy(com.datastax.driver.core.policies.RetryPolicy driverRetryPolicy) {
|
||||
return (InsertOptionsBuilder) super.retryPolicy(driverRetryPolicy);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#fetchSize(int)
|
||||
*/
|
||||
@Override
|
||||
public InsertOptionsBuilder fetchSize(int fetchSize) {
|
||||
return (InsertOptionsBuilder) super.fetchSize(fetchSize);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#readTimeout(long)
|
||||
*/
|
||||
@Override
|
||||
public InsertOptionsBuilder readTimeout(long readTimeout) {
|
||||
return (InsertOptionsBuilder) super.readTimeout(readTimeout);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#readTimeout(long, java.util.concurrent.TimeUnit)
|
||||
*/
|
||||
@Override
|
||||
public InsertOptionsBuilder readTimeout(long readTimeout, TimeUnit timeUnit) {
|
||||
return (InsertOptionsBuilder) super.readTimeout(readTimeout, timeUnit);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#tracing(boolean)
|
||||
*/
|
||||
@Override
|
||||
public InsertOptionsBuilder tracing(boolean tracing) {
|
||||
return (InsertOptionsBuilder) super.tracing(tracing);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#withTracing()
|
||||
*/
|
||||
@Override
|
||||
public InsertOptionsBuilder withTracing() {
|
||||
return (InsertOptionsBuilder) super.withTracing();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.WriteOptions.WriteOptionsBuilder#ttl(int)
|
||||
*/
|
||||
public InsertOptionsBuilder ttl(int ttl) {
|
||||
return (InsertOptionsBuilder) super.ttl(ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use light-weight transactions by applying {@code IF NOT EXISTS}.
|
||||
*
|
||||
* @return {@code this} {@link InsertOptionsBuilder}
|
||||
*/
|
||||
public InsertOptionsBuilder withIfNotExists() {
|
||||
return ifNotExists(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use light-weight transactions by applying {@code IF NOT EXISTS}.
|
||||
*
|
||||
* @param ifNotExists {@literal true} to enable {@code IF NOT EXISTS}.
|
||||
* @return {@code this} {@link InsertOptionsBuilder}
|
||||
*/
|
||||
public InsertOptionsBuilder ifNotExists(boolean ifNotExists) {
|
||||
|
||||
this.ifNotExists = ifNotExists;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new {@link InsertOptions} with the configured values.
|
||||
*
|
||||
* @return a new {@link InsertOptions} with the configured values
|
||||
*/
|
||||
public InsertOptions build() {
|
||||
|
||||
InsertOptions insertOptions = applyOptions(new InsertOptions());
|
||||
|
||||
insertOptions.ifNotExists = ifNotExists;
|
||||
|
||||
return insertOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,15 @@ class QueryUtils {
|
||||
|
||||
Insert insert = QueryOptionsUtil.addWriteOptions(QueryBuilder.insertInto(tableName), options);
|
||||
|
||||
if (options instanceof InsertOptions) {
|
||||
|
||||
InsertOptions insertOptions = (InsertOptions) options;
|
||||
|
||||
if (insertOptions.isIfNotExists()) {
|
||||
insert = insert.ifNotExists();
|
||||
}
|
||||
}
|
||||
|
||||
entityWriter.write(objectToUpdate, insert);
|
||||
|
||||
return insert;
|
||||
@@ -79,6 +88,15 @@ class QueryUtils {
|
||||
|
||||
Update update = QueryOptionsUtil.addWriteOptions(QueryBuilder.update(tableName), options);
|
||||
|
||||
if (options instanceof UpdateOptions) {
|
||||
|
||||
UpdateOptions updateOptions = (UpdateOptions) options;
|
||||
|
||||
if (updateOptions.isIfExists()) {
|
||||
update.where().ifExists();
|
||||
}
|
||||
}
|
||||
|
||||
entityWriter.write(objectToUpdate, update);
|
||||
|
||||
return update;
|
||||
|
||||
@@ -36,6 +36,10 @@ import com.datastax.driver.core.Statement;
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
* @see ReactiveCassandraTemplate
|
||||
* @see ReactiveCqlOperations
|
||||
* @see Statement
|
||||
* @see InsertOptions
|
||||
* @see UpdateOptions
|
||||
* @see Flux
|
||||
* @see Mono
|
||||
*/
|
||||
@@ -180,10 +184,10 @@ public interface ReactiveCassandraOperations {
|
||||
*
|
||||
* @param entity The entity to insert, must not be {@literal null}.
|
||||
* @param options may be {@literal null}.
|
||||
* @@return the inserted entity.
|
||||
* @return the inserted entity or {@link Mono#empty()} if the {@code INSERT} operation was not applied.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
*/
|
||||
<T> Mono<T> insert(T entity, WriteOptions options) throws DataAccessException;
|
||||
<T> Mono<T> insert(T entity, InsertOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Insert the given entities and emit the entity if the insert was applied.
|
||||
@@ -199,10 +203,10 @@ public interface ReactiveCassandraOperations {
|
||||
*
|
||||
* @param entities The entities to insert, must not be {@literal null}.
|
||||
* @param options may be {@literal null}.
|
||||
* @return the inserted entities.
|
||||
* @return the inserted entities. Does not emit items for which the {@code INSERT} operation was not applied.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
*/
|
||||
<T> Flux<T> insert(Publisher<? extends T> entities, WriteOptions options) throws DataAccessException;
|
||||
<T> Flux<T> insert(Publisher<? extends T> entities, InsertOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Update the given entity and emit the entity if the update was applied.
|
||||
@@ -218,10 +222,10 @@ public interface ReactiveCassandraOperations {
|
||||
*
|
||||
* @param entity The entity to update, must not be {@literal null}.
|
||||
* @param options may be {@literal null}.
|
||||
* @return the updated entity.
|
||||
* @return the updated entity or {@link Mono#empty()} if the {@code UPDATE} operation was not applied.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
*/
|
||||
<T> Mono<T> update(T entity, WriteOptions options) throws DataAccessException;
|
||||
<T> Mono<T> update(T entity, UpdateOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Update the given entities and emit the entity if the update was applied.
|
||||
@@ -237,10 +241,10 @@ public interface ReactiveCassandraOperations {
|
||||
*
|
||||
* @param entities The entities to update.
|
||||
* @param options may be {@literal null}.
|
||||
* @return the updated entities.
|
||||
* @return the updated entities. Does not emit items for which the {@code UPDATE} operation was not applied.
|
||||
* @throws DataAccessException if there is any problem issuing the execution.
|
||||
*/
|
||||
<T> Flux<T> update(Publisher<? extends T> entities, WriteOptions options) throws DataAccessException;
|
||||
<T> Flux<T> update(Publisher<? extends T> entities, UpdateOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Remove the given object from the table by id.
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.springframework.data.cql.core.QueryOptions;
|
||||
import org.springframework.data.cql.core.ReactiveCqlOperations;
|
||||
import org.springframework.data.cql.core.ReactiveCqlTemplate;
|
||||
import org.springframework.data.cql.core.ReactiveSessionCallback;
|
||||
import org.springframework.data.cql.core.WriteOptions;
|
||||
import org.springframework.data.cql.core.session.DefaultReactiveSessionFactory;
|
||||
import org.springframework.data.cql.core.session.ReactiveResultSet;
|
||||
import org.springframework.data.cql.core.session.ReactiveSession;
|
||||
@@ -380,10 +379,10 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#insert(java.lang.Object, org.springframework.data.cql.core.WriteOptions)
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#insert(java.lang.Object, org.springframework.data.cassandra.core.InsertOptions)
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<T> insert(T entity, WriteOptions options) {
|
||||
public <T> Mono<T> insert(T entity, InsertOptions options) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
|
||||
@@ -417,10 +416,10 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#insert(org.reactivestreams.Publisher, org.springframework.data.cql.core.WriteOptions)
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#insert(org.reactivestreams.Publisher, org.springframework.data.cassandra.core.InsertOptions)
|
||||
*/
|
||||
@Override
|
||||
public <T> Flux<T> insert(Publisher<? extends T> entities, WriteOptions options) {
|
||||
public <T> Flux<T> insert(Publisher<? extends T> entities, InsertOptions options) {
|
||||
|
||||
Assert.notNull(entities, "Entity publisher must not be null");
|
||||
|
||||
@@ -438,10 +437,10 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#update(java.lang.Object, org.springframework.data.cql.core.WriteOptions)
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#update(java.lang.Object, org.springframework.data.cassandra.core.UpdateOptions)
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<T> update(T entity, WriteOptions options) {
|
||||
public <T> Mono<T> update(T entity, UpdateOptions options) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
|
||||
@@ -475,10 +474,10 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#update(org.reactivestreams.Publisher, org.springframework.data.cql.core.WriteOptions)
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#update(org.reactivestreams.Publisher, org.springframework.data.cassandra.core.UpdateOptions)
|
||||
*/
|
||||
@Override
|
||||
public <T> Flux<T> update(Publisher<? extends T> entities, WriteOptions options) {
|
||||
public <T> Flux<T> update(Publisher<? extends T> entities, UpdateOptions options) {
|
||||
|
||||
Assert.notNull(entities, "Entity publisher must not be null");
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.core;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.cql.core.WriteOptions;
|
||||
|
||||
/**
|
||||
* Extension to {@link WriteOptions} for use with {@code UPDATE} operations.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class UpdateOptions extends WriteOptions {
|
||||
|
||||
private boolean ifExists;
|
||||
|
||||
/**
|
||||
* Creates new {@link UpdateOptions}.
|
||||
*/
|
||||
UpdateOptions() {}
|
||||
|
||||
/**
|
||||
* Create a new {@link UpdateOptionsBuilder}.
|
||||
*
|
||||
* @return a new {@link UpdateOptionsBuilder}.
|
||||
*/
|
||||
public static UpdateOptionsBuilder builder() {
|
||||
return new UpdateOptionsBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} to apply {@code IF EXISTS} to {@code UPDATE} operations.
|
||||
*/
|
||||
public boolean isIfExists() {
|
||||
return ifExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link UpdateOptions}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public static class UpdateOptionsBuilder extends WriteOptionsBuilder {
|
||||
|
||||
private boolean ifExists;
|
||||
|
||||
private UpdateOptionsBuilder() {}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#consistencyLevel(com.datastax.driver.core.ConsistencyLevel)
|
||||
*/
|
||||
@Override
|
||||
public UpdateOptionsBuilder consistencyLevel(com.datastax.driver.core.ConsistencyLevel consistencyLevel) {
|
||||
return (UpdateOptionsBuilder) super.consistencyLevel(consistencyLevel);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#retryPolicy(org.springframework.data.cql.core.RetryPolicy)
|
||||
*/
|
||||
@Override
|
||||
public UpdateOptionsBuilder retryPolicy(com.datastax.driver.core.policies.RetryPolicy driverRetryPolicy) {
|
||||
return (UpdateOptionsBuilder) super.retryPolicy(driverRetryPolicy);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#fetchSize(int)
|
||||
*/
|
||||
@Override
|
||||
public UpdateOptionsBuilder fetchSize(int fetchSize) {
|
||||
return (UpdateOptionsBuilder) super.fetchSize(fetchSize);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#readTimeout(long)
|
||||
*/
|
||||
@Override
|
||||
public UpdateOptionsBuilder readTimeout(long readTimeout) {
|
||||
return (UpdateOptionsBuilder) super.readTimeout(readTimeout);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#readTimeout(long, java.util.concurrent.TimeUnit)
|
||||
*/
|
||||
@Override
|
||||
public UpdateOptionsBuilder readTimeout(long readTimeout, TimeUnit timeUnit) {
|
||||
return (UpdateOptionsBuilder) super.readTimeout(readTimeout, timeUnit);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#tracing(boolean)
|
||||
*/
|
||||
@Override
|
||||
public UpdateOptionsBuilder tracing(boolean tracing) {
|
||||
return (UpdateOptionsBuilder) super.tracing(tracing);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.QueryOptions.QueryOptionsBuilder#withTracing()
|
||||
*/
|
||||
@Override
|
||||
public UpdateOptionsBuilder withTracing() {
|
||||
return (UpdateOptionsBuilder) super.withTracing();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cql.core.WriteOptions.WriteOptionsBuilder#ttl(int)
|
||||
*/
|
||||
public UpdateOptionsBuilder ttl(int ttl) {
|
||||
return (UpdateOptionsBuilder) super.ttl(ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use light-weight transactions by applying {@code IF EXISTS}.
|
||||
*
|
||||
* @return {@code this} {@link UpdateOptionsBuilder}
|
||||
*/
|
||||
public UpdateOptionsBuilder withIfExists() {
|
||||
return ifExists(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use light-weight transactions by applying {@code IF EXISTS}.
|
||||
*
|
||||
* @param ifNotExists {@literal true} to enable {@code IF EXISTS}.
|
||||
* @return {@code this} {@link UpdateOptionsBuilder}
|
||||
*/
|
||||
public UpdateOptionsBuilder ifExists(boolean ifNotExists) {
|
||||
|
||||
this.ifExists = ifNotExists;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new {@link UpdateOptions} with the configured values.
|
||||
*
|
||||
* @return a new {@link UpdateOptions} with the configured values
|
||||
*/
|
||||
public UpdateOptions build() {
|
||||
|
||||
UpdateOptions insertOptions = applyOptions(new UpdateOptions());
|
||||
|
||||
insertOptions.ifExists = ifExists;
|
||||
|
||||
return insertOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,7 +313,9 @@ public class QueryOptions {
|
||||
return applyOptions(new QueryOptions());
|
||||
}
|
||||
|
||||
<T extends QueryOptions> T applyOptions(T queryOptions) {
|
||||
protected <T> T applyOptions(T options) {
|
||||
|
||||
QueryOptions queryOptions = (QueryOptions) options;
|
||||
|
||||
queryOptions.setConsistencyLevel(consistencyLevel);
|
||||
queryOptions.setRetryPolicy(retryPolicy);
|
||||
@@ -330,7 +332,7 @@ public class QueryOptions {
|
||||
queryOptions.setTracing(tracing);
|
||||
}
|
||||
|
||||
return queryOptions;
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public class WriteOptions extends QueryOptions {
|
||||
|
||||
private Integer ttl;
|
||||
|
||||
private WriteOptionsBuilder() {}
|
||||
protected WriteOptionsBuilder() {}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -177,12 +177,16 @@ public class WriteOptions extends QueryOptions {
|
||||
* @return a new {@link WriteOptions} with the configured values
|
||||
*/
|
||||
public WriteOptions build() {
|
||||
return applyOptions(new WriteOptions());
|
||||
}
|
||||
|
||||
WriteOptions queryOptions = applyOptions(new WriteOptions());
|
||||
@Override
|
||||
protected <T> T applyOptions(T queryOptions) {
|
||||
|
||||
queryOptions.setTtl(ttl);
|
||||
WriteOptions writeOptions = (WriteOptions) queryOptions;
|
||||
writeOptions.setTtl(ttl);
|
||||
|
||||
return queryOptions;
|
||||
return super.applyOptions(queryOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,12 +99,41 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
assertThat(getUninterruptibly(template.selectOneById(user.getId(), User.class))).isNull();
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
|
||||
ListenableFuture<User> insert = template.insert(user);
|
||||
|
||||
assertThat(getUninterruptibly(insert)).isEqualTo(user);
|
||||
assertThat(getUninterruptibly(template.selectOneById(user.getId(), User.class))).isEqualTo(user);
|
||||
assertThat(getUser(user.getId())).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-250
|
||||
public void insertShouldCreateEntityWithLwt() {
|
||||
|
||||
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<User> inserted = template.insert(user, lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(inserted)).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-250
|
||||
public void insertShouldNotUpdateEntityWithLwt() {
|
||||
|
||||
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
getUninterruptibly(template.insert(user, lwtOptions));
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
ListenableFuture<User> lwt = template.insert(user, lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(lwt)).isNull();
|
||||
assertThat(getUser(user.getId()).getFirstname()).isEqualTo("Walter");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
@@ -125,10 +154,40 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
User updated = getUninterruptibly(template.update(user));
|
||||
|
||||
assertThat(updated).isNotNull();
|
||||
assertThat(getUninterruptibly(template.selectOneById(user.getId(), User.class))).isEqualTo(user);
|
||||
assertThat(getUser(user.getId())).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
public void updateShouldNotCreateEntityWithLwt() {
|
||||
|
||||
UpdateOptions lwtOptions = UpdateOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<User> lwt = template.update(user, lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(lwt)).isNull();
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
public void updateShouldUpdateEntityWithLwt() {
|
||||
|
||||
UpdateOptions lwtOptions = UpdateOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
ListenableFuture<User> updated = template.update(user, lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(updated)).isNotNull();
|
||||
assertThat(getUser(user.getId()).getFirstname()).isEqualTo("Walter Hartwell");
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
@@ -142,8 +201,7 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
|
||||
template.update(query, Update.empty().set("firstname", "Walter Hartwell"), User.class));
|
||||
assertThat(result).isTrue();
|
||||
|
||||
assertThat(getUninterruptibly(template.selectOneById(user.getId(), User.class)).getFirstname())
|
||||
.isEqualTo("Walter Hartwell");
|
||||
assertThat(getUser(user.getId()).getFirstname()).isEqualTo("Walter Hartwell");
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
@@ -155,7 +213,7 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
|
||||
Query query = Query.query(Criteria.where("id").is("heisenberg"));
|
||||
assertThat(getUninterruptibly(template.delete(query, User.class))).isTrue();
|
||||
|
||||
assertThat(getUninterruptibly(template.selectOneById(user.getId(), User.class))).isNull();
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
@@ -168,7 +226,7 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
|
||||
|
||||
assertThat(getUninterruptibly(template.delete(query, User.class))).isTrue();
|
||||
|
||||
User loaded = getUninterruptibly(template.selectOneById(user.getId(), User.class));
|
||||
User loaded = getUser(user.getId());
|
||||
assertThat(loaded.getFirstname()).isEqualTo("Walter");
|
||||
assertThat(loaded.getLastname()).isNull();
|
||||
}
|
||||
@@ -182,7 +240,7 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
|
||||
User deleted = getUninterruptibly(template.delete(user));
|
||||
|
||||
assertThat(deleted).isNotNull();
|
||||
assertThat(getUninterruptibly(template.selectOneById(user.getId(), User.class))).isNull();
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
@@ -194,7 +252,11 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
|
||||
Boolean deleted = getUninterruptibly(template.deleteById(user.getId(), User.class));
|
||||
assertThat(deleted).isTrue();
|
||||
|
||||
assertThat(getUninterruptibly(template.selectOneById(user.getId(), User.class))).isNull();
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
}
|
||||
|
||||
private User getUser(String id) {
|
||||
return getUninterruptibly(template.selectOneById(id, User.class));
|
||||
}
|
||||
|
||||
private static <T> T getUninterruptibly(Future<T> future) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -72,6 +73,29 @@ public class CassandraBatchTemplateIntegrationTests extends AbstractKeyspaceCrea
|
||||
assertThat(loaded.getId().getUsername()).isEqualTo(walter.getId().getUsername());
|
||||
}
|
||||
|
||||
@Test // DATACASS-288
|
||||
public void shouldInsertEntitiesWithLwt() {
|
||||
|
||||
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
|
||||
|
||||
Group previousWalter = new Group(new GroupKey("users", "0x1", "walter"));
|
||||
previousWalter.setAge(42);
|
||||
template.insert(previousWalter);
|
||||
|
||||
walter.setAge(100);
|
||||
|
||||
CassandraBatchOperations batchOperations = new CassandraBatchTemplate(template);
|
||||
batchOperations.insert(Collections.singleton(walter), lwtOptions).insert(mike).execute();
|
||||
|
||||
Group loadedWalter = template.selectOneById(walter.getId(), Group.class);
|
||||
Group loadedMike = template.selectOneById(mike.getId(), Group.class);
|
||||
|
||||
assertThat(loadedWalter.getId().getUsername()).isEqualTo(walter.getId().getUsername());
|
||||
assertThat(loadedWalter.getAge()).isEqualTo(42);
|
||||
|
||||
assertThat(loadedMike).isNotNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-288
|
||||
public void shouldInsertCollectionOfEntities() {
|
||||
|
||||
|
||||
@@ -146,6 +146,35 @@ public class CassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingI
|
||||
assertThat(template.selectOneById(user.getId(), User.class)).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-250
|
||||
public void insertShouldCreateEntityWithLwt() {
|
||||
|
||||
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
User inserted = template.insert(user, lwtOptions);
|
||||
|
||||
assertThat(inserted).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-250
|
||||
public void insertShouldNotUpdateEntityWithLwt() {
|
||||
|
||||
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
template.insert(user, lwtOptions);
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
User lwt = template.insert(user, lwtOptions);
|
||||
|
||||
assertThat(lwt).isNull();
|
||||
assertThat(template.selectOneById(user.getId(), User.class).getFirstname()).isEqualTo("Walter");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
public void shouldInsertAndCountEntities() {
|
||||
|
||||
@@ -171,6 +200,35 @@ public class CassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingI
|
||||
assertThat(template.selectOneById(user.getId(), User.class)).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
public void updateShouldNotCreateEntityWithLwt() {
|
||||
|
||||
UpdateOptions lwtOptions = UpdateOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
User lwt = template.update(user, lwtOptions);
|
||||
|
||||
assertThat(lwt).isNull();
|
||||
assertThat(template.selectOneById(user.getId(), User.class)).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
public void updateShouldUpdateEntityWithLwt() {
|
||||
|
||||
UpdateOptions lwtOptions = UpdateOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
template.insert(user);
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
User updated = template.update(user, lwtOptions);
|
||||
|
||||
assertThat(updated).isNotNull();
|
||||
assertThat(template.selectOneById(user.getId(), User.class).getFirstname()).isEqualTo("Walter Hartwell");
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
public void updateShouldUpdateEntityByQuery() {
|
||||
|
||||
|
||||
@@ -204,6 +204,23 @@ public class CassandraTemplateUnitTests {
|
||||
.isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES ('Walter','heisenberg','White');");
|
||||
}
|
||||
|
||||
@Test // DATACASS-250
|
||||
public void insertShouldInsertWithOptionsEntity() {
|
||||
|
||||
InsertOptions insertOptions = InsertOptions.builder().withIfNotExists().build();
|
||||
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
User inserted = template.insert(user, insertOptions);
|
||||
|
||||
assertThat(inserted).isEqualTo(user);
|
||||
verify(session).execute(statementCaptor.capture());
|
||||
assertThat(statementCaptor.getValue().toString())
|
||||
.isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES ('Walter','heisenberg','White') IF NOT EXISTS;");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
public void insertShouldTranslateException() throws Exception {
|
||||
|
||||
@@ -246,6 +263,23 @@ public class CassandraTemplateUnitTests {
|
||||
.isEqualTo("UPDATE users SET firstname='Walter',lastname='White' WHERE id='heisenberg';");
|
||||
}
|
||||
|
||||
@Test // DATACASS-250
|
||||
public void updateShouldUpdateEntityWithOptions() {
|
||||
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
UpdateOptions updateOptions = UpdateOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
User updated = template.update(user, updateOptions);
|
||||
|
||||
assertThat(updated).isEqualTo(user);
|
||||
verify(session).execute(statementCaptor.capture());
|
||||
assertThat(statementCaptor.getValue().toString())
|
||||
.isEqualTo("UPDATE users SET firstname='Walter',lastname='White' WHERE id='heisenberg' IF EXISTS;");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
public void updateShouldTranslateException() throws Exception {
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link InsertOptions}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class InsertOptionsUnitTests {
|
||||
|
||||
@Test // DATACASS-250
|
||||
public void shouldConfigureInsertOptions() {
|
||||
|
||||
InsertOptions insertOptions = InsertOptions.builder() //
|
||||
.ttl(10) //
|
||||
.withIfNotExists() //
|
||||
.build();
|
||||
|
||||
assertThat(insertOptions.getTtl()).isEqualTo(10);
|
||||
assertThat(insertOptions.isIfNotExists()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.*;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
import reactor.test.StepVerifier;
|
||||
import reactor.test.StepVerifier.FirstStep;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -68,11 +69,39 @@ public class ReactiveCassandraTemplateIntegrationTests extends AbstractKeyspaceC
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
Mono<User> insert = template.insert(user);
|
||||
StepVerifier.create(template.selectOneById(user.getId(), User.class)).verifyComplete();
|
||||
verifyUser(user.getId()).verifyComplete();
|
||||
|
||||
StepVerifier.create(insert).expectNext(user).verifyComplete();
|
||||
|
||||
StepVerifier.create(template.selectOneById(user.getId(), User.class)).expectNext(user).verifyComplete();
|
||||
verifyUser(user.getId()).expectNext(user).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATACASS-250
|
||||
public void insertShouldCreateEntityWithLwt() {
|
||||
|
||||
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
Mono<User> inserted = template.insert(user, lwtOptions);
|
||||
|
||||
StepVerifier.create(inserted).expectNext(user).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATACASS-250
|
||||
public void insertShouldNotUpdateEntityWithLwt() {
|
||||
|
||||
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
StepVerifier.create(template.insert(user, lwtOptions)).expectNext(user).verifyComplete();
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
StepVerifier.create(template.insert(user, lwtOptions)).verifyComplete();
|
||||
|
||||
verifyUser(user.getId()).consumeNextWith(it -> assertThat(it.getFirstname()).isEqualTo("Walter")).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATACASS-335
|
||||
@@ -96,7 +125,35 @@ public class ReactiveCassandraTemplateIntegrationTests extends AbstractKeyspaceC
|
||||
|
||||
StepVerifier.create(template.insert(user)).expectNextCount(1).verifyComplete();
|
||||
|
||||
StepVerifier.create(template.selectOneById(user.getId(), User.class)).expectNext(user).verifyComplete();
|
||||
verifyUser(user.getId()).expectNext(user).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
public void updateShouldNotCreateEntityWithLwt() {
|
||||
|
||||
UpdateOptions lwtOptions = UpdateOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
StepVerifier.create(template.update(user, lwtOptions)).verifyComplete();
|
||||
|
||||
verifyUser(user.getId()).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
public void updateShouldUpdateEntityWithLwt() {
|
||||
|
||||
UpdateOptions lwtOptions = UpdateOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
StepVerifier.create(template.insert(user)).expectNextCount(1).verifyComplete();
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
StepVerifier.create(template.update(user, lwtOptions)).expectNextCount(1).verifyComplete();
|
||||
|
||||
verifyUser(user.getId()).consumeNextWith(it -> assertThat(it.getFirstname()).isEqualTo("Walter Hartwell"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
@@ -149,7 +206,7 @@ public class ReactiveCassandraTemplateIntegrationTests extends AbstractKeyspaceC
|
||||
|
||||
StepVerifier.create(template.delete(user)).expectNext(user).verifyComplete();
|
||||
|
||||
StepVerifier.create(template.selectOneById(user.getId(), User.class)).verifyComplete();
|
||||
verifyUser(user.getId()).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATACASS-335
|
||||
@@ -161,7 +218,7 @@ public class ReactiveCassandraTemplateIntegrationTests extends AbstractKeyspaceC
|
||||
|
||||
StepVerifier.create(template.deleteById(user.getId(), User.class)).expectNext(true).verifyComplete();
|
||||
|
||||
StepVerifier.create(template.selectOneById(user.getId(), User.class)).verifyComplete();
|
||||
verifyUser(user.getId()).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
@@ -199,4 +256,8 @@ public class ReactiveCassandraTemplateIntegrationTests extends AbstractKeyspaceC
|
||||
|
||||
assertThat(template.selectOne(query, UserToken.class).block()).isEqualTo(token1);
|
||||
}
|
||||
|
||||
private FirstStep<User> verifyUser(String userId) {
|
||||
return StepVerifier.create(template.selectOneById(userId, User.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link UpdateOptions}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class UpdateOptionsUnitTests {
|
||||
|
||||
@Test // DATACASS-250
|
||||
public void shouldConfigureInsertOptions() {
|
||||
|
||||
UpdateOptions insertOptions = UpdateOptions.builder() //
|
||||
.ttl(10) //
|
||||
.withIfExists() //
|
||||
.build();
|
||||
|
||||
assertThat(insertOptions.getTtl()).isEqualTo(10);
|
||||
assertThat(insertOptions.isIfExists()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
== What's new in Spring Data for Apache Cassandra 2.0
|
||||
* `Update` and `Query` objects.
|
||||
* CRUD repository interface renaming: `CassandraRepository` using `MapId` is now renamed to `MapIdCassandraRepository`. `TypedIdCassandraRepository` is renamed to `CassandraRepository`.
|
||||
* Lightweight transactions via `InsertOptions` and `UpdateOptions` using the Template API.
|
||||
|
||||
[[new-features.1-5-0]]
|
||||
== What's new in Spring Data for Apache Cassandra 1.5
|
||||
|
||||
@@ -1160,14 +1160,15 @@ Person qp = cassandraTemplate.selectOne(query(where("age").is(33)), Person.class
|
||||
The insert/save operations available to you are listed below.
|
||||
|
||||
* `T` *insert* `(T objectToSave)` Insert the object in an Apache Cassandra table.
|
||||
* `T` *insert* `(T objectToSave, WriteOptions writeOptions)` Insert the object in an Apache Cassandra table applying `WriteOptions`.
|
||||
* `T` *insert* `(T objectToSave, InsertOptions options)` Insert the object in an Apache Cassandra table applying `InsertOptions`.
|
||||
|
||||
A similar set of update operations is listed below
|
||||
|
||||
* `T` *update* `(T objectToSave)` Update the object in an Apache Cassandra table.
|
||||
* `T` *update* `(T objectToSave, WriteOptions writeOptions)` Update the object in an Apache Cassandra table applying `WriteOptions`.
|
||||
* `T` *update* `(T objectToSave, UpdateOptions options)` Update the object in an Apache Cassandra table applying `UpdateOptions`.
|
||||
|
||||
Then, there is always the old fashioned way. You can write your own CQL statements.
|
||||
Then, there is always the old fashioned way. You can write your own CQL statements. You can configure with `InsertOptions` and `UpdateOptions`
|
||||
additional options such as TTL, consistency level and lightweight transactions.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
Reference in New Issue
Block a user