DATACASS-656 - Migrate to Cassandra driver 4.

Original pull request: #167.
This commit is contained in:
Mark Paluch
2019-12-11 11:16:30 +01:00
parent 67bc99ff0a
commit 4ded76da0e
396 changed files with 6638 additions and 12099 deletions

13
pom.xml
View File

@@ -85,12 +85,13 @@
<properties>
<build.cassandra.host>localhost</build.cassandra.host>
<build.cassandra.mode>embedded</build.cassandra.mode>
<build.cassandra.native_transport_port>19042</build.cassandra.native_transport_port>
<build.cassandra.native_transport_port>19042
</build.cassandra.native_transport_port>
<build.cassandra.rpc_port>19160</build.cassandra.rpc_port>
<build.cassandra.ssl_storage_port>17001</build.cassandra.ssl_storage_port>
<build.cassandra.storage_port>17000</build.cassandra.storage_port>
<cassandra.version>3.11.4</cassandra.version>
<cassandra-driver.version>3.7.2</cassandra-driver.version>
<cassandra-driver.version>4.3.0</cassandra-driver.version>
<dist.id>spring-data-cassandra</dist.id>
<el.version>1.0</el.version>
<multithreadedtc.version>1.01</multithreadedtc.version>
@@ -117,15 +118,15 @@
<!-- Cassandra Driver -->
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-core</artifactId>
<version>${cassandra-driver.version}</version>
</dependency>
<dependency>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-core</artifactId>
<version>4.2.0</version>
<artifactId>java-driver-query-builder</artifactId>
<version>${cassandra-driver.version}</version>
</dependency>
<!-- CDI -->

View File

@@ -75,13 +75,13 @@
<!-- Cassandra driver -->
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-core</artifactId>
</dependency>
<dependency>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-core</artifactId>
<artifactId>java-driver-query-builder</artifactId>
</dependency>
<!-- Reactor -->

View File

@@ -15,10 +15,10 @@
*/
package org.springframework.data.cassandra;
import java.net.InetAddress;
import org.springframework.dao.PermissionDeniedDataAccessException;
import com.datastax.oss.driver.api.core.metadata.EndPoint;
/**
* Spring data access exception for a Cassandra authentication failure.
*
@@ -28,14 +28,14 @@ public class CassandraAuthenticationException extends PermissionDeniedDataAccess
private static final long serialVersionUID = 8556304586797273927L;
private InetAddress host;
private EndPoint host;
public CassandraAuthenticationException(InetAddress host, String msg, Throwable cause) {
public CassandraAuthenticationException(EndPoint host, String msg, Throwable cause) {
super(msg, cause);
this.host = host;
}
public InetAddress getHost() {
public EndPoint getHost() {
return host;
}
}

View File

@@ -15,13 +15,14 @@
*/
package org.springframework.data.cassandra;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.dao.DataAccessResourceFailureException;
import com.datastax.oss.driver.api.core.metadata.Node;
/**
* Spring data access exception for Cassandra when no host is available.
*
@@ -31,14 +32,14 @@ public class CassandraConnectionFailureException extends DataAccessResourceFailu
private static final long serialVersionUID = 6299912054261646552L;
private final Map<InetSocketAddress, Throwable> messagesByHost = new HashMap<>();
private final Map<Node, Throwable> messagesByHost = new HashMap<>();
public CassandraConnectionFailureException(Map<InetSocketAddress, Throwable> map, String msg, Throwable cause) {
public CassandraConnectionFailureException(Map<Node, Throwable> map, String msg, Throwable cause) {
super(msg, cause);
this.messagesByHost.putAll(map);
}
public Map<InetSocketAddress, Throwable> getMessagesByHost() {
public Map<Node, Throwable> getMessagesByHost() {
return Collections.unmodifiableMap(messagesByHost);
}
}

View File

@@ -33,6 +33,7 @@ public class CassandraReadTimeoutException extends QueryTimeoutException {
this.wasDataReceived = wasDataReceived;
}
// TODO: Rename wasDataPresent
public boolean getWasDataReceived() {
return wasDataReceived;
}

View File

@@ -16,16 +16,19 @@
package org.springframework.data.cassandra;
import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.lang.Nullable;
/**
* Spring data access exception for when Cassandra schema element being created already exists.
*
* @author Matthew T. Adams
* @author Mark Paluch
*/
public class CassandraSchemaElementExistsException extends NonTransientDataAccessException {
private static final long serialVersionUID = 7798361273692300162L;
@Deprecated
public enum ElementType {
KEYSPACE, TABLE, COLUMN, INDEX
}
@@ -33,6 +36,7 @@ public class CassandraSchemaElementExistsException extends NonTransientDataAcces
private String elementName;
private ElementType elementType;
@Deprecated
public CassandraSchemaElementExistsException(String elementName, ElementType elementType, String msg,
Throwable cause) {
super(msg, cause);
@@ -40,10 +44,18 @@ public class CassandraSchemaElementExistsException extends NonTransientDataAcces
this.elementType = elementType;
}
public CassandraSchemaElementExistsException(String msg, Throwable cause) {
super(msg, cause);
}
@Deprecated
@Nullable
public String getElementName() {
return elementName;
}
@Deprecated
@Nullable
public ElementType getElementType() {
return elementType;
}

View File

@@ -19,9 +19,9 @@ import reactor.core.publisher.Flux;
import java.util.List;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.ExecutionInfo;
import com.datastax.driver.core.Row;
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
import com.datastax.oss.driver.api.core.cql.ExecutionInfo;
import com.datastax.oss.driver.api.core.cql.Row;
/**
* The reactive result of a query.
@@ -29,13 +29,12 @@ import com.datastax.driver.core.Row;
* The retrieval of the rows of a {@link ReactiveResultSet} is generally paged (a first page of result is fetched and
* the next one is only fetched once all the results of the first one has been consumed). The size of the pages can be
* configured either globally through {@link com.datastax.driver.core.QueryOptions#setFetchSize} or per-statement with
* {@link com.datastax.driver.core.Statement#setFetchSize}.
* {@link com.datastax.oss.driver.api.core.cql.Statement#setFetchSize}.
* <p>
* Please note however that this {@link ReactiveResultSet} paging is not available with the version 1 of the native
* protocol (i.e. with Cassandra 1.2 or if version 1 has been explicitly requested through
* {@link com.datastax.driver.core.Cluster.Builder#withProtocolVersion}). If the protocol version 1 is in use, a
* {@link ReactiveResultSet} is always fetched in it's entirely and it's up to the client to make sure that no query can
* yield {@link ReactiveResultSet} that won't hold in memory.
* protocol (i.e. with Cassandra 1.2 or if version 1 has been explicitly requested). If the protocol version 1 is in
* use, a {@link ReactiveResultSet} is always fetched in it's entirely and it's up to the client to make sure that no
* query can yield {@link ReactiveResultSet} that won't hold in memory.
* <p>
* Note that this class is not thread-safe.
*
@@ -51,8 +50,8 @@ public interface ReactiveResultSet {
* Returns a {@link Flux} over the rows contained in this result set applying transparent paging.
* <p>
* The {@link Flux} will stream over all records that in this {@link ReactiveResultSet} according to the reactive
* demand and fetch next result chunks by issuing the underlying query with the current
* {@link com.datastax.driver.core.PagingState} applied.
* demand and fetch next result chunks by issuing the underlying query with the current {@link java.nio.ByteBuffer
* paging state} applied.
* <p>
*
* @return a {@link Flux} of rows that will stream over all {@link Row rows} of the entire result.
@@ -61,8 +60,8 @@ public interface ReactiveResultSet {
/**
* Returns a {@link Flux} over the rows contained in this result set chunk. This method does not apply transparent
* paging. Use {@link com.datastax.driver.core.PagingState} from {@link #getExecutionInfo()} to issue subsequent
* queries to obtain the next result chunk.
* paging. Use {@link java.nio.ByteBuffer paging state} from {@link #getExecutionInfo()} to issue subsequent queries
* to obtain the next result chunk.
*
* @return a {@link Flux} of rows that will stream over all {@link Row rows} in this {@link ReactiveResultSet}.
* @since 2.1

View File

@@ -20,14 +20,11 @@ import reactor.core.publisher.Mono;
import java.io.Closeable;
import java.util.Map;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.RegularStatement;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import com.datastax.driver.core.exceptions.QueryExecutionException;
import com.datastax.driver.core.exceptions.QueryValidationException;
import com.datastax.driver.core.exceptions.UnsupportedFeatureException;
import com.datastax.oss.driver.api.core.context.DriverContext;
import com.datastax.oss.driver.api.core.cql.BoundStatement;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
/**
* A session holds connections to a Cassandra cluster, allowing it to be queried. {@link ReactiveSession} executes
@@ -65,11 +62,11 @@ public interface ReactiveSession extends Closeable {
boolean isClosed();
/**
* Returns the {@code Cluster} object this session is part of.
* Returns a context that provides access to all the policies used by this driver instance.
*
* @return the {@code Cluster} object this session is part of.
* @return a context that provides access to all the policies used by this driver instance.
*/
Cluster getCluster();
DriverContext getContext();
/**
* Executes the provided query.
@@ -79,11 +76,6 @@ public interface ReactiveSession extends Closeable {
* @param query the CQL query to execute.
* @return the result of the query. That result will never be null but can be empty (and will be for any non SELECT
* query).
* @throws NoHostAvailableException if no host in the cluster can be contacted successfully to execute this query.
* @throws QueryExecutionException if the query triggered an execution exception, i.e. an exception thrown by
* Cassandra when it cannot execute the query with the requested consistency level successfully.
* @throws QueryValidationException if the query if invalid (syntax error, unauthorized or any other validation
* problem).
*/
Mono<ReactiveResultSet> execute(String query);
@@ -94,16 +86,9 @@ public interface ReactiveSession extends Closeable {
*
* @param query the CQL query to execute.
* @param values values required for the execution of {@code query}. See
* {@link SimpleStatement#SimpleStatement(String, Object...)} for more details.
* {@link SimpleStatement#newInstance(String, Object...)} for more details.
* @return the result of the query. That result will never be null but can be empty (and will be for any non SELECT
* query).
* @throws NoHostAvailableException if no host in the cluster can be contacted successfully to execute this query.
* @throws QueryExecutionException if the query triggered an execution exception, i.e. an exception thrown by
* Cassandra when it cannot execute the query with the requested consistency level successfully.
* @throws QueryValidationException if the query if invalid (syntax error, unauthorized or any other validation
* problem).
* @throws UnsupportedFeatureException if version 1 of the protocol is in use (i.e. if you've forced version 1 through
* {@link Cluster.Builder#withProtocolVersion} or you use Cassandra 1.2).
*/
Mono<ReactiveResultSet> execute(String query, Object... values);
@@ -114,16 +99,9 @@ public interface ReactiveSession extends Closeable {
*
* @param query the CQL query to execute.
* @param values values required for the execution of {@code query}. See
* {@link SimpleStatement#SimpleStatement(String, Map)} for more details.
* {@link SimpleStatement#newInstance(String, Map)} for more details.
* @return the result of the query. That result will never be null but can be empty (and will be for any non SELECT
* query).
* @throws NoHostAvailableException if no host in the cluster can be contacted successfully to execute this query.
* @throws QueryExecutionException if the query triggered an execution exception, i.e. an exception thrown by
* Cassandra when it cannot execute the query with the requested consistency level successfully.
* @throws QueryValidationException if the query if invalid (syntax error, unauthorized or any other validation
* problem).
* @throws UnsupportedFeatureException if version 1 or 2 of the protocol is in use (i.e. if you've forced it through
* {@link Cluster.Builder#withProtocolVersion} or you use Cassandra 1.2 or 2.0).
*/
Mono<ReactiveResultSet> execute(String query, Map<String, Object> values);
@@ -138,23 +116,14 @@ public interface ReactiveSession extends Closeable {
* @param statement the CQL query to execute (that can be any {@link Statement}).
* @return the result of the query. That result will never be null but can be empty (and will be for any non SELECT
* query).
* @throws NoHostAvailableException if no host in the cluster can be contacted successfully to execute this query.
* @throws QueryExecutionException if the query triggered an execution exception, i.e. an exception thrown by
* Cassandra when it cannot execute the query with the requested consistency level successfully.
* @throws QueryValidationException if the query if invalid (syntax error, unauthorized or any other validation
* problem).
* @throws UnsupportedFeatureException if the protocol version 1 is in use and a feature not supported has been used.
* Features that are not supported by the version protocol 1 include: BatchStatement, ReactiveResultSet
* paging and binary values in RegularStatement.
*/
Mono<ReactiveResultSet> execute(Statement statement);
Mono<ReactiveResultSet> execute(Statement<?> statement);
/**
* Prepares the provided query string.
*
* @param query the CQL query string to prepare
* @return the prepared statement corresponding to {@code query}.
* @throws NoHostAvailableException if no host in the cluster can be contacted successfully to prepare this query.
*/
Mono<PreparedStatement> prepare(String query);
@@ -165,7 +134,7 @@ public interface ReactiveSession extends Closeable {
* inherit the query properties set on {@code statement}. Concretely, this means that in the following code:
*
* <pre>
* RegularStatement toPrepare = new SimpleStatement("SELECT * FROM test WHERE k=?")
* Statement toPrepare = SimpleStatement.newInstance("SELECT * FROM test WHERE k=?")
* .setConsistencyLevel(ConsistencyLevel.QUORUM);
* PreparedStatement prepared = session.prepare(toPrepare);
* session.execute(prepared.bind("someValue"));
@@ -179,21 +148,14 @@ public interface ReactiveSession extends Closeable {
*
* @param statement the statement to prepare
* @return the prepared statement corresponding to {@code statement}.
* @throws NoHostAvailableException if no host in the cluster can be contacted successfully to prepare this statement.
* @throws IllegalArgumentException if {@code statement.getValues() != null} (values for executing a prepared
* statement should be provided after preparation though the {@link PreparedStatement#bind} method or
* through a corresponding {@link BoundStatement}).
*/
Mono<PreparedStatement> prepare(RegularStatement statement);
Mono<PreparedStatement> prepare(SimpleStatement statement);
/**
* Initiates a shutdown of this session instance and blocks until that shutdown completes.
* <p/>
* This method is a shortcut for {@code closeAsync().get()}.
* <p/>
* Note that this method does not close the corresponding {@code Cluster} instance (which holds additional resources,
* in particular internal executors that must be shut down in order for the client program to terminate). If you want
* to do so, use {@link Cluster#close}, but note that it will close all sessions created from that cluster.
*/
@Override
void close();

View File

@@ -15,18 +15,17 @@
*/
package org.springframework.data.cassandra;
import com.datastax.driver.core.Session;
import com.datastax.oss.driver.api.core.CqlSession;
/**
* A factory for Apache Cassandra sessions.
* <p>
* An alternative to the {@link com.datastax.driver.core.Cluster} facility, a {@link SessionFactory} object is the
* preferred means of getting a connection. The {@link SessionFactory} interface is implemented by a {@link Session}
* provider.
* A {@link SessionFactory} object is the preferred means of getting a connection. The {@link SessionFactory} interface
* is implemented by a {@link CqlSession} provider.
* <p>
* A {@link SessionFactory} object can have properties that can be modified when necessary. For example, if the
* {@link Session} is moved to a different server, the property for the server can be changed. The benefit is that
* {@link CqlSession} is moved to a different server, the property for the server can be changed. The benefit is that
* because the data source's properties can be changed, any code accessing that {@link SessionFactory} does not need to
* be changed.
*
@@ -37,23 +36,11 @@ import com.datastax.oss.driver.api.core.CqlSession;
public interface SessionFactory {
/**
* Attempts to establish a {@link Session} with the connection infrastructure that this {@link SessionFactory} object
* represents.
* Attempts to establish a {@link CqlSession} with the connection infrastructure that this {@link SessionFactory}
* object represents.
*
* @return a {@link Session} to Apache Cassandra.
* @return a {@link CqlSession} to Apache Cassandra.
*/
Session getSession();
/**
* Attempts to establish a {@link Session} with the connection infrastructure that this {@link SessionFactory} object
* represents.
*
* @return a {@link Session} to Apache Cassandra.
*/
default CqlSession getCqlSession() {
// TODO
return null;
}
CqlSession getSession();
}

View File

@@ -19,7 +19,10 @@ import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
@@ -28,7 +31,7 @@ import org.springframework.data.cassandra.core.CassandraAdminTemplate;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.convert.CassandraCustomConversions;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory;
import org.springframework.data.cassandra.core.cql.session.init.KeyspacePopulator;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.SimpleTupleTypeFactory;
import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver;
@@ -39,8 +42,8 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Session;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
/**
* Base class for Spring Data Cassandra configuration using JavaConfig.
@@ -51,91 +54,72 @@ import com.datastax.driver.core.Session;
* @author Mark Paluch
*/
@Configuration
public abstract class AbstractCassandraConfiguration extends AbstractClusterConfiguration
implements BeanClassLoaderAware {
public abstract class AbstractCassandraConfiguration extends AbstractSessionConfiguration
implements BeanClassLoaderAware, BeanFactoryAware {
private @Nullable ClassLoader beanClassLoader;
private @Nullable BeanFactory beanFactory;
/**
* Returns the initialized {@link Session} instance.
* Returns the initialized {@link CqlSession} instance.
*
* @return the {@link Session}.
* @return the {@link CqlSession}.
* @throws IllegalStateException if the session factory is not initialized.
*/
protected Session getRequiredSession() {
protected SessionFactory getRequiredSessionFactory() {
CassandraSessionFactoryBean factoryBean = session();
Assert.state(beanFactory != null, "BeanFactory not initialized");
Session session = factoryBean.getObject();
Assert.state(session != null, "Session factory not initialized");
return session;
return beanFactory.getBean(SessionFactory.class);
}
/**
* Creates a {@link CassandraSessionFactoryBean} that provides a Cassandra {@link com.datastax.driver.core.Session}.
* The lifecycle of {@link CassandraSessionFactoryBean} initializes the {@link #getSchemaAction() schema} in the
* Creates a {@link SessionFactoryFactoryBean} that provides a {@link SessionFactory}. The lifecycle of
* {@link SessionFactoryFactoryBean} initializes the {@link #getSchemaAction() schema} in the
* {@link #getKeyspaceName() configured keyspace}.
*
* @return the {@link CassandraSessionFactoryBean}.
* @see #cluster()
* @return the {@link SessionFactoryFactoryBean}.
* @see #cassandraConverter()
* @see #getKeyspaceName()
* @see #getSchemaAction()
* @see #getStartupScripts()
* @see #getShutdownScripts()
* @see #keyspacePopulator()
* @see #keyspaceCleaner()
*/
@Bean
public CassandraSessionFactoryBean session() {
public SessionFactoryFactoryBean sessionFactory(CqlSession cqlSession) {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
SessionFactoryFactoryBean bean = new SessionFactoryFactoryBean();
session.setCluster(getRequiredCluster());
session.setConverter(cassandraConverter());
session.setKeyspaceName(getKeyspaceName());
session.setSchemaAction(getSchemaAction());
session.setStartupScripts(getStartupScripts());
session.setShutdownScripts(getShutdownScripts());
bean.setSession(cqlSession);
return session;
bean.setConverter(beanFactory.getBean(CassandraConverter.class));
bean.setSchemaAction(getSchemaAction());
bean.setKeyspacePopulator(keyspacePopulator());
bean.setKeyspacePopulator(keyspaceCleaner());
return bean;
}
/**
* Creates a {@link CassandraCqlSessionFactoryBean} that provides a Cassandra
* {@link com.datastax.oss.driver.api.core.CqlSession}. The lifecycle of {@link CassandraSessionFactoryBean}
* initializes the {@link #getSchemaAction() schema} in the {@link #getKeyspaceName() configured keyspace}.
* Creates a {@link KeyspacePopulator} to initialize the keyspace.
*
* @return the {@link CassandraSessionFactoryBean}.
* @see #cluster()
* @see #cassandraConverter()
* @see #getKeyspaceName()
* @see #getSchemaAction()
* @see #getStartupScripts()
* @see #getShutdownScripts()
* @return the {@link KeyspacePopulator} or {@code null} if none configured.
* @see org.springframework.data.cassandra.core.cql.session.init.ResourceKeyspacePopulator
*/
@Bean
public CqlSessionFactoryBean cassandraSession() {
CqlSessionFactoryBean session = new CqlSessionFactoryBean();
session.setContactPoints(getContactPoints());
session.setPort(getPort());
session.setKeyspaceName(getKeyspaceName());
return session;
@Nullable
protected KeyspacePopulator keyspacePopulator() {
return null;
}
/**
* Creates a {@link DefaultSessionFactory} using the configured {@link #session()} to be used with
* {@link org.springframework.data.cassandra.core.CassandraTemplate}.
* Creates a {@link KeyspacePopulator} to cleanup the keyspace.
*
* @return {@link SessionFactory} used to initialize the Template API.
* @since 2.0
* @return the {@link KeyspacePopulator} or {@code null} if none configured.
* @see org.springframework.data.cassandra.core.cql.session.init.ResourceKeyspacePopulator
*/
@Bean
public SessionFactory sessionFactory() {
return new DefaultSessionFactory(getRequiredSession());
@Nullable
protected KeyspacePopulator keyspaceCleaner() {
return null;
}
/**
@@ -149,15 +133,12 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf
@Bean
public CassandraConverter cassandraConverter() {
try {
MappingCassandraConverter mappingCassandraConverter = new MappingCassandraConverter(cassandraMapping());
MappingCassandraConverter mappingCassandraConverter = new MappingCassandraConverter(
beanFactory.getBean(CassandraMappingContext.class));
mappingCassandraConverter.setCustomConversions(customConversions());
mappingCassandraConverter.setCustomConversions(beanFactory.getBean(CassandraCustomConversions.class));
return mappingCassandraConverter;
} catch (ClassNotFoundException cause) {
throw new IllegalStateException(cause);
}
return mappingCassandraConverter;
}
/**
@@ -170,22 +151,21 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf
@Bean
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
Cluster cluster = getRequiredCluster();
UserTypeResolver userTypeResolver = new SimpleUserTypeResolver(cluster, getKeyspaceName());
UserTypeResolver userTypeResolver = new SimpleUserTypeResolver(getRequiredSession(),
CqlIdentifier.fromCql(getKeyspaceName()));
CassandraMappingContext mappingContext = new CassandraMappingContext(userTypeResolver,
new SimpleTupleTypeFactory(cluster));
SimpleTupleTypeFactory.DEFAULT);
Optional.ofNullable(this.beanClassLoader).ifPresent(mappingContext::setBeanClassLoader);
mappingContext.setInitialEntitySet(getInitialEntitySet());
CustomConversions customConversions = customConversions();
CustomConversions customConversions = beanFactory.getBean(CassandraCustomConversions.class);
mappingContext.setCustomConversions(customConversions);
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
mappingContext.setCodecRegistry(cluster.getConfiguration().getCodecRegistry());
mappingContext.setCodecRegistry(getRequiredSession().getContext().getCodecRegistry());
return mappingContext;
}
@@ -225,7 +205,7 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf
*/
@Bean
public CassandraAdminTemplate cassandraTemplate() throws Exception {
return new CassandraAdminTemplate(sessionFactory(), cassandraConverter());
return new CassandraAdminTemplate(getRequiredSessionFactory(), beanFactory.getBean(CassandraConverter.class));
}
@Override
@@ -233,6 +213,11 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf
this.beanClassLoader = classLoader;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/**
* Base packages to scan for entities annotated with {@link Table} annotations. By default, returns the package name
* of {@literal this} ({@code this.getClass().getPackage().getName()}. This method must never return {@literal null}.
@@ -241,13 +226,6 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf
return new String[] { getClass().getPackage().getName() };
}
/**
* Return the name of the keyspace to connect to.
*
* @return must not be {@literal null}.
*/
protected abstract String getKeyspaceName();
/**
* The {@link SchemaAction} to perform at startup. Defaults to {@link SchemaAction#NONE}.
*/

View File

@@ -1,351 +0,0 @@
/*
* Copyright 2013-2020 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
*
* https://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.config;
import java.util.Collections;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.DropKeyspaceSpecification;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.AuthProvider;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.NettyOptions;
import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.ProtocolVersion;
import com.datastax.driver.core.QueryOptions;
import com.datastax.driver.core.SocketOptions;
import com.datastax.driver.core.TimestampGenerator;
import com.datastax.driver.core.policies.AddressTranslator;
import com.datastax.driver.core.policies.LoadBalancingPolicy;
import com.datastax.driver.core.policies.ReconnectionPolicy;
import com.datastax.driver.core.policies.RetryPolicy;
import com.datastax.driver.core.policies.SpeculativeExecutionPolicy;
/**
* Base class for Spring Cassandra configuration that can handle creating namespaces, execute arbitrary CQL on startup &
* shutdown, and optionally drop keyspaces.
*
* @author Matthew T. Adams
* @author Jorge Davison
* @author Mark Paluch
* @author John Blum
*/
@Configuration
public abstract class AbstractClusterConfiguration {
/**
* Returns the initialized {@link Cluster} instance.
*
* @return the {@link Cluster}.
* @throws IllegalStateException if the cluster factory is not initialized.
*/
protected Cluster getRequiredCluster() {
CassandraClusterFactoryBean factoryBean = cluster();
Cluster cluster = factoryBean.getObject();
Assert.state(cluster != null, "Cluster not initialized");
return cluster;
}
/**
* Creates a {@link CassandraClusterFactoryBean} that provides a Cassandra {@link com.datastax.driver.core.Cluster}.
* The lifecycle of {@link CassandraClusterFactoryBean} executes {@link #getStartupScripts() startup} and
* {@link #getShutdownScripts() shutdown} scripts.
*
* @return the {@link CassandraClusterFactoryBean}.
* @see #cluster()
* @see #getStartupScripts()
* @see #getShutdownScripts()
*/
@Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean bean = new CassandraClusterFactoryBean();
bean.setAddressTranslator(getAddressTranslator());
bean.setAuthProvider(getAuthProvider());
bean.setClusterBuilderConfigurer(getClusterBuilderConfigurer());
bean.setClusterName(getClusterName());
bean.setCompressionType(getCompressionType());
bean.setContactPoints(getContactPoints());
bean.setLoadBalancingPolicy(getLoadBalancingPolicy());
bean.setMaxSchemaAgreementWaitSeconds(getMaxSchemaAgreementWaitSeconds());
bean.setMetricsEnabled(getMetricsEnabled());
bean.setNettyOptions(getNettyOptions());
bean.setPoolingOptions(getPoolingOptions());
bean.setPort(getPort());
bean.setProtocolVersion(getProtocolVersion());
bean.setQueryOptions(getQueryOptions());
bean.setReconnectionPolicy(getReconnectionPolicy());
bean.setRetryPolicy(getRetryPolicy());
bean.setSpeculativeExecutionPolicy(getSpeculativeExecutionPolicy());
bean.setSocketOptions(getSocketOptions());
bean.setTimestampGenerator(getTimestampGenerator());
bean.setKeyspaceCreations(getKeyspaceCreations());
bean.setKeyspaceDrops(getKeyspaceDrops());
bean.setStartupScripts(getStartupScripts());
bean.setShutdownScripts(getShutdownScripts());
return bean;
}
/**
* Returns the {@link AddressTranslator}.
*
* @return the {@link AddressTranslator}; may be {@literal null}.
* @since 1.5
*/
@Nullable
protected AddressTranslator getAddressTranslator() {
return null;
}
/**
* Returns the {@link AuthProvider}.
*
* @return the {@link AuthProvider}, may be {@literal null}.
*/
@Nullable
protected AuthProvider getAuthProvider() {
return null;
}
/**
* Returns the {@link ClusterBuilderConfigurer}.
*
* @return the {@link ClusterBuilderConfigurer}; may be {@literal null}.
* @since 1.5
*/
@Nullable
protected ClusterBuilderConfigurer getClusterBuilderConfigurer() {
return null;
}
/**
* Returns the cluster name.
*
* @return the cluster name; may be {@literal null}.
* @since 1.5
*/
@Nullable
protected String getClusterName() {
return null;
}
/**
* Returns the {@link CompressionType}.
*
* @return the {@link CompressionType}, may be {@literal null}.
*/
@Nullable
protected CompressionType getCompressionType() {
return null;
}
/**
* Returns the Cassandra contact points. Defaults to {@code localhost}
*
* @return the Cassandra contact points
* @see CassandraClusterFactoryBean#DEFAULT_CONTACT_POINTS
*/
protected String getContactPoints() {
return CassandraClusterFactoryBean.DEFAULT_CONTACT_POINTS;
}
/**
* Returns the {@link LoadBalancingPolicy}.
*
* @return the {@link LoadBalancingPolicy}, may be {@literal null}.
*/
@Nullable
protected LoadBalancingPolicy getLoadBalancingPolicy() {
return null;
}
/**
* Returns the maximum schema agreement wait in seconds.
*
* @return the maximum schema agreement wait in seconds; default to {@literal 10} seconds.
*/
protected int getMaxSchemaAgreementWaitSeconds() {
return CassandraClusterFactoryBean.DEFAULT_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS;
}
/**
* Returns the whether to enable metrics. Defaults to {@literal true}
*
* @return {@literal true} to enable metrics.
* @see CassandraClusterFactoryBean#DEFAULT_METRICS_ENABLED
*/
protected boolean getMetricsEnabled() {
return CassandraClusterFactoryBean.DEFAULT_METRICS_ENABLED;
}
/**
* Returns the {@link NettyOptions}. Defaults to {@link NettyOptions#DEFAULT_INSTANCE}.
*
* @return the {@link NettyOptions} to customize netty behavior.
* @since 1.5
*/
protected NettyOptions getNettyOptions() {
return NettyOptions.DEFAULT_INSTANCE;
}
/**
* Returns the {@link PoolingOptions}.
*
* @return the {@link PoolingOptions}, may be {@literal null}.
*/
@Nullable
protected PoolingOptions getPoolingOptions() {
return null;
}
/**
* Returns the Cassandra port. Defaults to {@code 9042}.
*
* @return the Cassandra port
* @see CassandraClusterFactoryBean#DEFAULT_PORT
*/
protected int getPort() {
return CassandraClusterFactoryBean.DEFAULT_PORT;
}
/**
* Returns the {@link ProtocolVersion}. Defaults to {@link ProtocolVersion#NEWEST_SUPPORTED}.
*
* @return the {@link ProtocolVersion}.
* @see ProtocolVersion#NEWEST_SUPPORTED.
*/
protected ProtocolVersion getProtocolVersion() {
return ProtocolVersion.NEWEST_SUPPORTED;
}
/**
* Returns the {@link QueryOptions}.
*
* @return the {@link QueryOptions}, may be {@literal null}.
* @since 1.5
*/
@Nullable
protected QueryOptions getQueryOptions() {
return null;
}
/**
* Returns the {@link ReconnectionPolicy}.
*
* @return the {@link ReconnectionPolicy}, may be {@literal null}.
*/
@Nullable
protected ReconnectionPolicy getReconnectionPolicy() {
return null;
}
/**
* Returns the {@link RetryPolicy}.
*
* @return the {@link RetryPolicy}, may be {@literal null}.
*/
@Nullable
protected RetryPolicy getRetryPolicy() {
return null;
}
/**
* Returns the {@link SpeculativeExecutionPolicy}.
*
* @return the {@link SpeculativeExecutionPolicy}; may be {@literal null}.
* @since 1.5
*/
@Nullable
protected SpeculativeExecutionPolicy getSpeculativeExecutionPolicy() {
return null;
}
/**
* Returns the {@link SocketOptions}.
*
* @return the {@link SocketOptions}, may be {@literal null}.
*/
@Nullable
protected SocketOptions getSocketOptions() {
return null;
}
/**
* Returns the {@link TimestampGenerator}.
*
* @return the {@link TimestampGenerator}; may be {@literal null}.
* @since 1.5
*/
@Nullable
protected TimestampGenerator getTimestampGenerator() {
return null;
}
/**
* Returns the list of keyspace creations to be run right after {@link com.datastax.driver.core.Cluster}
* initialization.
*
* @return the list of keyspace creations, may be empty but never {@link null}
*/
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
return Collections.emptyList();
}
/**
* Returns the list of keyspace drops to be run before {@link com.datastax.driver.core.Cluster} shutdown.
*
* @return the list of keyspace drops, may be empty but never {@link null}
*/
protected List<DropKeyspaceSpecification> getKeyspaceDrops() {
return Collections.emptyList();
}
/**
* Returns the list of startup scripts to be run after {@link #getKeyspaceCreations() keyspace creations} and after
* {@link com.datastax.driver.core.Cluster} initialization.
*
* @return the list of startup scripts, may be empty but never {@link null}
* @deprecated Use {@link org.springframework.data.cassandra.core.cql.session.init.SessionFactoryInitializer}.
*/
@Deprecated
protected List<String> getStartupScripts() {
return Collections.emptyList();
}
/**
* Returns the list of shutdown scripts to be run after {@link #getKeyspaceDrops() keyspace drops} and right before
* {@link com.datastax.driver.core.Cluster} shutdown.
*
* @return the list of shutdown scripts, may be empty but never {@link null}
* @deprecated Use {@link org.springframework.data.cassandra.core.cql.session.init.SessionFactoryInitializer}.
*/
@Deprecated
protected List<String> getShutdownScripts() {
return Collections.emptyList();
}
}

View File

@@ -15,28 +15,20 @@
*/
package org.springframework.data.cassandra.config;
import org.springframework.context.annotation.Bean;
import org.springframework.data.cassandra.core.cql.CqlTemplate;
/**
* Abstract configuration class to create a {@link CqlTemplate} and inheriting {@link com.datastax.driver.core.Session}
* and {@link com.datastax.driver.core.Cluster} creation. This class is usually extended by user configuration classes.
* Abstract configuration class to create a {@link CqlTemplate} and inheriting
* {@link com.datastax.oss.driver.api.core.CqlSession} creation. This class is usually extended by user configuration
* classes.
*
* @author Matthew T. Adams
* @author Mark Paluch
* @see AbstractSessionConfiguration
* @see CqlTemplate
* @deprecated since 3.0, use {@link AbstractSessionConfiguration}.
*/
@Deprecated
public abstract class AbstractCqlTemplateConfiguration extends AbstractSessionConfiguration {
/**
* Creates a {@link CqlTemplate} configured with {@link #sessionFactory()}.
*
* @return the {@link CqlTemplate}.
* @see #sessionFactory()
*/
@Bean
public CqlTemplate cqlTemplate() {
return new CqlTemplate(sessionFactory());
}
}

View File

@@ -15,15 +15,19 @@
*/
package org.springframework.data.cassandra.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.data.cassandra.ReactiveSession;
import org.springframework.data.cassandra.ReactiveSessionFactory;
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
import org.springframework.data.cassandra.core.ReactiveCassandraTemplate;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.cql.ReactiveCqlOperations;
import org.springframework.data.cassandra.core.cql.ReactiveCqlTemplate;
import org.springframework.data.cassandra.core.cql.session.DefaultBridgedReactiveSession;
import org.springframework.data.cassandra.core.cql.session.DefaultReactiveSessionFactory;
import org.springframework.lang.Nullable;
/**
* Extension to {@link AbstractCassandraConfiguration} providing Spring Data Cassandra configuration for Spring Data's
@@ -34,6 +38,8 @@ import org.springframework.data.cassandra.core.cql.session.DefaultReactiveSessio
*/
public abstract class AbstractReactiveCassandraConfiguration extends AbstractCassandraConfiguration {
private @Nullable BeanFactory beanFactory;
/**
* Creates a {@link ReactiveSession} object. This wraps a {@link com.datastax.driver.core.Session} to expose Cassandra
* access in a reactive style.
@@ -57,7 +63,7 @@ public abstract class AbstractReactiveCassandraConfiguration extends AbstractCas
*/
@Bean
public ReactiveSessionFactory reactiveSessionFactory() {
return new DefaultReactiveSessionFactory(reactiveSession());
return new DefaultReactiveSessionFactory(beanFactory.getBean(ReactiveSession.class));
}
/**
@@ -69,7 +75,8 @@ public abstract class AbstractReactiveCassandraConfiguration extends AbstractCas
*/
@Bean
public ReactiveCassandraTemplate reactiveCassandraTemplate() {
return new ReactiveCassandraTemplate(reactiveSessionFactory(), cassandraConverter());
return new ReactiveCassandraTemplate(beanFactory.getBean(ReactiveSessionFactory.class),
beanFactory.getBean(CassandraConverter.class));
}
/**
@@ -80,6 +87,11 @@ public abstract class AbstractReactiveCassandraConfiguration extends AbstractCas
*/
@Bean
public ReactiveCqlTemplate reactiveCqlTemplate() {
return new ReactiveCqlTemplate(reactiveSessionFactory());
return new ReactiveCqlTemplate(beanFactory.getBean(ReactiveSessionFactory.class));
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}

View File

@@ -15,73 +15,39 @@
*/
package org.springframework.data.cassandra.config;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.SessionFactory;
import org.springframework.data.cassandra.core.cql.CqlTemplate;
import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.DropKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.Session;
import com.datastax.oss.driver.api.core.CqlSession;
/**
* Spring {@link @Configuration} class used to configure a Cassandra client application
* {@link com.datastax.driver.core.Session} connected to a Cassandra {@link com.datastax.driver.core.Cluster}. Enables a
* Cassandra Keyspace to be specified along with the ability to execute arbitrary CQL on startup as well as shutdown.
* Spring {@link @Configuration} class used to configure a Cassandra client application {@link CqlSession} connected to
* a Cassandra cluster. Enables a Cassandra Keyspace to be specified along with the ability to execute arbitrary CQL on
* startup as well as shutdown.
*
* @author Matthew T. Adams
* @author John Blum
* @author Mark Paluch
* @see AbstractClusterConfiguration
* @see org.springframework.context.annotation.Configuration
*/
@Configuration
public abstract class AbstractSessionConfiguration extends AbstractClusterConfiguration {
public abstract class AbstractSessionConfiguration implements BeanFactoryAware {
/**
* Returns the initialized {@link Session} instance.
*
* @return the {@link Session}.
* @throws IllegalStateException if the session factory is not initialized.
*/
protected Session getRequiredSession() {
CassandraCqlSessionFactoryBean factoryBean = session();
Assert.state(factoryBean.getObject() != null, "Session factory not initialized");
return factoryBean.getObject();
}
/**
* Creates a {@link CassandraCqlSessionFactoryBean} that provides a Cassandra
* {@link com.datastax.driver.core.Session}.
*
* @return the {@link CassandraCqlSessionFactoryBean}.
* @see #cluster()
* @see #getKeyspaceName()
*/
@Bean
public CassandraCqlSessionFactoryBean session() {
CassandraCqlSessionFactoryBean bean = new CassandraCqlSessionFactoryBean();
bean.setCluster(getRequiredCluster());
bean.setKeyspaceName(getKeyspaceName());
return bean;
}
/**
* Creates a {@link DefaultSessionFactory} using the configured {@link #session()} to be used with
* {@link CqlTemplate}.
*
* @return {@link SessionFactory} used to initialize the Template API.
* @since 2.0
*/
@Bean
public SessionFactory sessionFactory() {
return new DefaultSessionFactory(getRequiredSession());
}
private @Nullable BeanFactory beanFactory;
/**
* Return the name of the keyspace to connect to.
@@ -89,4 +55,169 @@ public abstract class AbstractSessionConfiguration extends AbstractClusterConfig
* @return must not be {@literal null}.
*/
protected abstract String getKeyspaceName();
/**
* Returns the initialized {@link CqlSession} instance.
*
* @return the {@link CqlSession}.
* @throws IllegalStateException if the session factory is not initialized.
*/
protected SessionFactory getRequiredSessionFactory() {
ObjectProvider<SessionFactory> beanProvider = beanFactory.getBeanProvider(SessionFactory.class);
return beanProvider.getIfAvailable(() -> new DefaultSessionFactory(beanFactory.getBean(CqlSession.class)));
}
/**
* Returns the {@link SessionBuilderConfigurer}.
*
* @return the {@link SessionBuilderConfigurer}; may be {@literal null}.
* @since 1.5
*/
@Nullable
protected SessionBuilderConfigurer getClusterBuilderConfigurer() {
return null;
}
/**
* Returns the cluster name.
*
* @return the cluster name; may be {@literal null}.
* @since 1.5
*/
@Nullable
protected String getClusterName() {
return null;
}
/**
* Returns the {@link CompressionType}.
*
* @return the {@link CompressionType}, may be {@literal null}.
*/
@Nullable
protected CompressionType getCompressionType() {
return null;
}
/**
* Returns the Cassandra contact points. Defaults to {@code localhost}
*
* @return the Cassandra contact points
* @see CqlSessionFactoryBean#DEFAULT_CONTACT_POINTS
*/
protected String getContactPoints() {
return CqlSessionFactoryBean.DEFAULT_CONTACT_POINTS;
}
/**
* Returns the Cassandra port. Defaults to {@code 9042}.
*
* @return the Cassandra port
* @see CqlSessionFactoryBean#DEFAULT_PORT
*/
protected int getPort() {
return CqlSessionFactoryBean.DEFAULT_PORT;
}
/**
* Returns the list of keyspace creations to be run right after initialization.
*
* @return the list of keyspace creations, may be empty but never {@link null}
*/
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
return Collections.emptyList();
}
/**
* Returns the list of keyspace drops to be run before shutdown.
*
* @return the list of keyspace drops, may be empty but never {@link null}
*/
protected List<DropKeyspaceSpecification> getKeyspaceDrops() {
return Collections.emptyList();
}
/**
* Returns the list of startup scripts to be run after {@link #getKeyspaceCreations() keyspace creations} and after
* initialization.
*
* @return the list of startup scripts, may be empty but never {@link null}
* @deprecated since 3.0, declare a
* {@link org.springframework.data.cassandra.core.cql.session.init.SessionFactoryInitializer} bean.
*/
@Deprecated
protected List<String> getStartupScripts() {
return Collections.emptyList();
}
/**
* Returns the list of shutdown scripts to be run after {@link #getKeyspaceDrops() keyspace drops} and right before
* shutdown.
*
* @return the list of shutdown scripts, may be empty but never {@link null}
* @deprecated since 3.0, declare a
* {@link org.springframework.data.cassandra.core.cql.session.init.SessionFactoryInitializer} bean.
*/
@Deprecated
protected List<String> getShutdownScripts() {
return Collections.emptyList();
}
/**
* Returns the initialized {@link CqlSession} instance.
*
* @return the {@link CqlSession}.
* @throws IllegalStateException if the session factory is not initialized.
*/
protected CqlSession getRequiredSession() {
Assert.state(beanFactory != null, "BeanFactory not initialized");
return beanFactory.getBean(CqlSession.class);
}
/**
* Creates a {@link CqlSessionFactoryBean} that provides a Cassandra {@link CqlSession}.
*
* @return the {@link CqlSessionFactoryBean}.
* @see #getKeyspaceName()
* @see #getStartupScripts()
* @see #getShutdownScripts()
*/
@Bean
public CqlSessionFactoryBean session() {
CqlSessionFactoryBean bean = new CqlSessionFactoryBean();
bean.setContactPoints(getContactPoints());
bean.setPort(getPort());
bean.setKeyspaceCreations(getKeyspaceCreations());
bean.setKeyspaceDrops(getKeyspaceDrops());
bean.setKeyspaceName(getKeyspaceName());
bean.setKeyspaceStartupScripts(getStartupScripts());
bean.setKeyspaceShutdownScripts(getShutdownScripts());
return bean;
}
/**
* Creates a {@link CqlTemplate} configured with {@link #getRequiredSessionFactory()}.
*
* @return the {@link CqlTemplate}.
* @see #getRequiredSession()
*/
@Bean
public CqlTemplate cqlTemplate() {
return new CqlTemplate(getRequiredSessionFactory());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}

View File

@@ -1,749 +0,0 @@
/*
* Copyright 2013-2020 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
*
* https://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.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.core.cql.CassandraExceptionTranslator;
import org.springframework.data.cassandra.core.cql.CqlTemplate;
import org.springframework.data.cassandra.core.cql.generator.AlterKeyspaceCqlGenerator;
import org.springframework.data.cassandra.core.cql.generator.CreateKeyspaceCqlGenerator;
import org.springframework.data.cassandra.core.cql.generator.DropKeyspaceCqlGenerator;
import org.springframework.data.cassandra.core.cql.keyspace.AlterKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.DropKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceActionSpecification;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.*;
import com.datastax.driver.core.Cluster.Builder;
import com.datastax.driver.core.ProtocolOptions.Compression;
import com.datastax.driver.core.policies.AddressTranslator;
import com.datastax.driver.core.policies.LoadBalancingPolicy;
import com.datastax.driver.core.policies.ReconnectionPolicy;
import com.datastax.driver.core.policies.RetryPolicy;
import com.datastax.driver.core.policies.SpeculativeExecutionPolicy;
/**
* {@link org.springframework.beans.factory.FactoryBean} for configuring a Cassandra {@link Cluster}.
* <p>
* This factory bean allows configuration and creation of {@link Cluster} bean. Most options default to {@literal null}.
* Unsupported options are configured via {@link ClusterBuilderConfigurer}.
* <p/>
* The factory bean initializes keyspaces, if configured, accoording to its lifecycle. Keyspaces can be created after
* {@link #afterPropertiesSet() initialization} and dropped when this factory is {@link #destroy() destroyed}. Keyspace
* actions can be configured via {@link #setKeyspaceActions(List) XML} and {@link #setKeyspaceCreations(List)
* programatically}. Additional {@link #getStartupScripts()} and {@link #getShutdownScripts()} are executed after
* running keyspace actions.
* <p/>
* <strong>XML configuration</strong>
*
* <pre class="code">
<cql:cluster contact-points="…"
port="${build.cassandra.native_transport_port}" compression="SNAPPY" netty-options-ref="nettyOptions">
<cql:local-pooling-options
min-simultaneous-requests="26" max-simultaneous-requests="101"
core-connections="3" max-connections="9"/>
<cql:remote-pooling-options
min-simultaneous-requests="25" max-simultaneous-requests="100"
core-connections="1" max-connections="2"/>
<cql:socket-options connect-timeout-millis="5000"
keep-alive="true" reuse-address="true" so-linger="60" tcp-no-delay="true"
receive-buffer-size="65536" send-buffer-size="65536"/>
<cql:keyspace name="${cassandra.keyspace}" action="CREATE_DROP"
durable-writes="true"/>
</cass:cluster>
* </pre>
*
* @author Alex Shvid
* @author Matthew T. Adams
* @author David Webb
* @author Kirk Clemens
* @author Jorge Davison
* @author John Blum
* @author Mark Paluch
* @author Stefan Birkner
* @see org.springframework.beans.factory.InitializingBean
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.beans.factory.FactoryBean
* @see com.datastax.driver.core.Cluster
*/
@SuppressWarnings("unused")
public class CassandraClusterFactoryBean
implements FactoryBean<Cluster>, InitializingBean, DisposableBean, BeanNameAware, PersistenceExceptionTranslator {
public static final boolean DEFAULT_JMX_REPORTING_ENABLED = true;
public static final boolean DEFAULT_METRICS_ENABLED = true;
public static final boolean DEFAULT_SSL_ENABLED = false;
public static final int DEFAULT_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS = 10;
public static final int DEFAULT_PORT = 9042;
public static final String DEFAULT_CONTACT_POINTS = "localhost";
protected static final Logger log = LoggerFactory.getLogger(CassandraCqlClusterFactoryBean.class);
private boolean jmxReportingEnabled = DEFAULT_JMX_REPORTING_ENABLED;
private boolean metricsEnabled = DEFAULT_METRICS_ENABLED;
private boolean sslEnabled = DEFAULT_SSL_ENABLED;
private int maxSchemaAgreementWaitSeconds = DEFAULT_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS;
private int port = DEFAULT_PORT;
private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
private @Nullable Cluster cluster;
private @Nullable ClusterBuilderConfigurer clusterBuilderConfigurer;
private @Nullable AddressTranslator addressTranslator;
private @Nullable AuthProvider authProvider;
private @Nullable CompressionType compressionType;
private @Nullable CodecRegistry codecRegistry;
private @Nullable Host.StateListener hostStateListener;
private @Nullable LatencyTracker latencyTracker;
private @Nullable LoadBalancingPolicy loadBalancingPolicy;
private NettyOptions nettyOptions = NettyOptions.DEFAULT_INSTANCE;
private @Nullable PoolingOptions poolingOptions;
private @Nullable ProtocolVersion protocolVersion;
private @Nullable QueryOptions queryOptions;
private @Nullable ReconnectionPolicy reconnectionPolicy;
private @Nullable RetryPolicy retryPolicy;
private @Nullable SpeculativeExecutionPolicy speculativeExecutionPolicy;
private @Nullable SocketOptions socketOptions;
private @Nullable SSLOptions sslOptions;
private @Nullable TimestampGenerator timestampGenerator;
private List<AlterKeyspaceSpecification> keyspaceAlterations = new ArrayList<>();
private List<CreateKeyspaceSpecification> keyspaceCreations = new ArrayList<>();
private List<DropKeyspaceSpecification> keyspaceDrops = new ArrayList<>();
private List<KeyspaceActions> keyspaceActions = new ArrayList<>();
private List<String> startupScripts = new ArrayList<>();
private List<String> shutdownScripts = new ArrayList<>();
private Set<KeyspaceActionSpecification> keyspaceSpecifications = new HashSet<>();
private @Nullable String beanName;
private @Nullable String clusterName;
private String contactPoints = DEFAULT_CONTACT_POINTS;
private @Nullable String password;
private @Nullable String username;
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
this.cluster = initializeCluster(withRegistrations(buildCluster()));
}
private @NonNull Cluster buildCluster() {
Assert.hasText(this.contactPoints, "At least one server is required");
Builder clusterBuilder = newClusterBuilder()
.addContactPoints(StringUtils.commaDelimitedListToStringArray(this.contactPoints))
.withMaxSchemaAgreementWaitSeconds(this.maxSchemaAgreementWaitSeconds).withPort(this.port);
Optional.ofNullable(this.addressTranslator).ifPresent(clusterBuilder::withAddressTranslator);
Optional.ofNullable(this.codecRegistry).ifPresent(clusterBuilder::withCodecRegistry);
Optional.ofNullable(this.loadBalancingPolicy).ifPresent(clusterBuilder::withLoadBalancingPolicy);
Optional.ofNullable(this.nettyOptions).ifPresent(clusterBuilder::withNettyOptions);
Optional.ofNullable(this.poolingOptions).ifPresent(clusterBuilder::withPoolingOptions);
Optional.ofNullable(this.protocolVersion).ifPresent(clusterBuilder::withProtocolVersion);
Optional.ofNullable(this.queryOptions).ifPresent(clusterBuilder::withQueryOptions);
Optional.ofNullable(this.reconnectionPolicy).ifPresent(clusterBuilder::withReconnectionPolicy);
Optional.ofNullable(this.retryPolicy).ifPresent(clusterBuilder::withRetryPolicy);
Optional.ofNullable(this.socketOptions).ifPresent(clusterBuilder::withSocketOptions);
Optional.ofNullable(this.speculativeExecutionPolicy).ifPresent(clusterBuilder::withSpeculativeExecutionPolicy);
Optional.ofNullable(this.timestampGenerator).ifPresent(clusterBuilder::withTimestampGenerator);
Optional.ofNullable(this.authProvider).map(clusterBuilder::withAuthProvider).orElseGet(
() -> StringUtils.hasText(this.username) ? clusterBuilder.withCredentials(this.username, this.password)
: clusterBuilder);
Optional.ofNullable(this.compressionType).map(CassandraClusterFactoryBean::convertCompressionType)
.ifPresent(clusterBuilder::withCompression);
if (!this.jmxReportingEnabled) {
clusterBuilder.withoutJMXReporting();
}
if (!this.metricsEnabled) {
clusterBuilder.withoutMetrics();
}
if (this.sslEnabled) {
Optional.ofNullable(this.sslOptions).map(clusterBuilder::withSSL).orElseGet(clusterBuilder::withSSL);
}
Optional.ofNullable(resolveClusterName()).filter(StringUtils::hasText).ifPresent(clusterBuilder::withClusterName);
if (this.clusterBuilderConfigurer != null) {
this.clusterBuilderConfigurer.configure(clusterBuilder);
}
return clusterBuilder.build();
}
private static Compression convertCompressionType(CompressionType type) {
switch (type) {
case NONE:
return Compression.NONE;
case SNAPPY:
return Compression.SNAPPY;
case LZ4:
return Compression.LZ4;
}
throw new IllegalArgumentException(String.format("Unknown compression type [%s]", type));
}
/*
* (non-Javadoc)
* @see com.datastax.driver.core.Cluster#builder()
*/
@NonNull
Cluster.Builder newClusterBuilder() {
return Cluster.builder();
}
private @Nullable String resolveClusterName() {
return StringUtils.hasText(this.clusterName) ? this.clusterName : this.beanName;
}
private @NonNull Cluster withRegistrations(@NonNull Cluster cluster) {
Optional.ofNullable(this.hostStateListener).ifPresent(cluster::register);
Optional.ofNullable(this.latencyTracker).ifPresent(cluster::register);
return cluster;
}
private @NonNull Cluster initializeCluster(@NonNull Cluster cluster) {
generateSpecificationsFromFactoryBeans();
List<KeyspaceActionSpecification> startupSpecifications = new ArrayList<>(
this.keyspaceCreations.size() + this.keyspaceAlterations.size());
startupSpecifications.addAll(this.keyspaceCreations);
startupSpecifications.addAll(this.keyspaceAlterations);
executeSpecsAndScripts(startupSpecifications, this.startupScripts, cluster);
return cluster;
}
private void executeSpecsAndScripts(List<? extends KeyspaceActionSpecification> keyspaceActionSpecifications,
List<String> scripts, Cluster cluster) {
if (!CollectionUtils.isEmpty(keyspaceActionSpecifications) || !CollectionUtils.isEmpty(scripts)) {
try (Session session = cluster.connect()) {
CqlTemplate template = new CqlTemplate(session);
keyspaceActionSpecifications
.forEach(keyspaceActionSpecification -> template.execute(toCql(keyspaceActionSpecification)));
scripts.forEach(template::execute);
}
}
}
/**
* Evaluates the contents of all the KeyspaceSpecificationFactoryBeans and generates the proper KeyspaceSpecification
* from them.
*/
private void generateSpecificationsFromFactoryBeans() {
generateSpecifications(this.keyspaceSpecifications);
this.keyspaceActions.forEach(actions -> generateSpecifications(actions.getActions()));
}
private void generateSpecifications(Collection<KeyspaceActionSpecification> specifications) {
specifications.forEach(keyspaceActionSpecification -> {
if (keyspaceActionSpecification instanceof AlterKeyspaceSpecification) {
this.keyspaceAlterations.add((AlterKeyspaceSpecification) keyspaceActionSpecification);
} else if (keyspaceActionSpecification instanceof CreateKeyspaceSpecification) {
this.keyspaceCreations.add((CreateKeyspaceSpecification) keyspaceActionSpecification);
} else if (keyspaceActionSpecification instanceof DropKeyspaceSpecification) {
this.keyspaceDrops.add((DropKeyspaceSpecification) keyspaceActionSpecification);
}
});
}
private String toCql(KeyspaceActionSpecification specification) {
if (specification instanceof AlterKeyspaceSpecification) {
return new AlterKeyspaceCqlGenerator((AlterKeyspaceSpecification) specification).toCql();
} else if (specification instanceof CreateKeyspaceSpecification) {
return new CreateKeyspaceCqlGenerator((CreateKeyspaceSpecification) specification).toCql();
} else if (specification instanceof DropKeyspaceSpecification) {
return new DropKeyspaceCqlGenerator((DropKeyspaceSpecification) specification).toCql();
}
throw new IllegalArgumentException(
"Unsupported specification type: " + ClassUtils.getQualifiedName(specification.getClass()));
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public Cluster getObject() {
return this.cluster;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<? extends Cluster> getObjectType() {
return this.cluster != null ? this.cluster.getClass() : Cluster.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
@Override
public void destroy() {
if (this.cluster != null) {
executeSpecsAndScripts(this.keyspaceDrops, this.shutdownScripts, this.cluster);
this.cluster.close();
}
}
/*
* (non-Javadoc)
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
*/
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException cause) {
return exceptionTranslator.translateExceptionIfPossible(cause);
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.BeanNameAware#setBeanName(String)
* @since 1.5
*/
@Override
public void setBeanName(@Nullable String beanName) {
this.beanName = beanName;
}
/**
* Set a comma-delimited string of the contact points (hosts) to connect to. Default is {@code localhost}; see
* {@link #DEFAULT_CONTACT_POINTS}.
*
* @param contactPoints the contact points used by the new cluster.
*/
public void setContactPoints(String contactPoints) {
this.contactPoints = contactPoints;
}
/**
* Set the port for the contact points. Default is {@code 9042}, see {@link #DEFAULT_PORT}.
*
* @param port the port used by the new cluster.
*/
public void setPort(int port) {
this.port = port;
}
/**
* Set the {@link CompressionType}. Default is uncompressed.
*
* @param compressionType the {@link CompressionType} used by the new cluster.
*/
public void setCompressionType(@Nullable CompressionType compressionType) {
this.compressionType = compressionType;
}
/**
* Set the {@link CodecRegistry}. Default uses {@link CodecRegistry#DEFAULT_INSTANCE}.
*
* @param codecRegistry the {@link CodecRegistry} used by the new cluster.
* @since 2.2
*/
public void setCodecRegistry(@Nullable CodecRegistry codecRegistry) {
this.codecRegistry = codecRegistry;
}
/**
* Set the {@link PoolingOptions} to configure the connection pooling behavior.
*
* @param poolingOptions the {@link PoolingOptions} used by the new cluster.
*/
public void setPoolingOptions(@Nullable PoolingOptions poolingOptions) {
this.poolingOptions = poolingOptions;
}
/**
* Set the {@link ProtocolVersion}.
*
* @param protocolVersion the {@link ProtocolVersion} used by the new cluster.
* @since 1.4
*/
public void setProtocolVersion(@Nullable ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion;
}
/**
* Set the {@link SocketOptions} containing low-level socket options.
*
* @param socketOptions the {@link SocketOptions} used by the new cluster.
*/
public void setSocketOptions(@Nullable SocketOptions socketOptions) {
this.socketOptions = socketOptions;
}
/**
* Set the {@link QueryOptions} to tune to defaults for individual queries.
*
* @param queryOptions the {@link QueryOptions} used by the new cluster.
*/
public void setQueryOptions(@Nullable QueryOptions queryOptions) {
this.queryOptions = queryOptions;
}
/**
* Set the {@link AuthProvider}. Default is unauthenticated.
*
* @param authProvider the {@link AuthProvider} used by the new cluster.
*/
public void setAuthProvider(@Nullable AuthProvider authProvider) {
this.authProvider = authProvider;
}
/**
* Set the {@link NettyOptions} used by a client to customize the driver's underlying Netty layer.
*
* @param nettyOptions the {@link NettyOptions} used by the new cluster.
* @since 1.5
*/
public void setNettyOptions(NettyOptions nettyOptions) {
this.nettyOptions = nettyOptions;
}
/**
* Set the {@link LoadBalancingPolicy} that decides which Cassandra hosts to contact for each new query.
*
* @param loadBalancingPolicy the {@link LoadBalancingPolicy} used by the new cluster.
*/
public void setLoadBalancingPolicy(@Nullable LoadBalancingPolicy loadBalancingPolicy) {
this.loadBalancingPolicy = loadBalancingPolicy;
}
/**
* Set the {@link ReconnectionPolicy} that decides how often the reconnection to a dead node is attempted.
*
* @param reconnectionPolicy the {@link ReconnectionPolicy} used by the new cluster.
*/
public void setReconnectionPolicy(@Nullable ReconnectionPolicy reconnectionPolicy) {
this.reconnectionPolicy = reconnectionPolicy;
}
/**
* Set the {@link RetryPolicy} that defines a default behavior to adopt when a request fails.
*
* @param retryPolicy the {@link RetryPolicy} used by the new cluster.
*/
public void setRetryPolicy(@Nullable RetryPolicy retryPolicy) {
this.retryPolicy = retryPolicy;
}
/**
* Set whether metrics are enabled. Default is {@literal true}, see {@link #DEFAULT_METRICS_ENABLED}.
*/
public void setMetricsEnabled(boolean metricsEnabled) {
this.metricsEnabled = metricsEnabled;
}
/**
* @return the {@link List} of {@link KeyspaceActions}.
*/
public List<KeyspaceActions> getKeyspaceActions() {
return Collections.unmodifiableList(this.keyspaceActions);
}
/**
* Set a {@link List} of {@link KeyspaceActions} to be executed on initialization. Keyspace actions may contain create
* and drop specifications.
*
* @param keyspaceActions the {@link List} of {@link KeyspaceActions}.
*/
public void setKeyspaceActions(List<KeyspaceActions> keyspaceActions) {
this.keyspaceActions = new ArrayList<>(keyspaceActions);
}
/**
* Set a {@link List} of {@link CreateKeyspaceSpecification create keyspace specifications} that are executed when
* this factory is {@link #afterPropertiesSet() initialized}. {@link CreateKeyspaceSpecification Create keyspace
* specifications} are executed on a system session with no keyspace set, before executing
* {@link #setStartupScripts(List)}.
*
* @param specifications the {@link List} of {@link CreateKeyspaceSpecification create keyspace specifications}.
*/
public void setKeyspaceCreations(List<CreateKeyspaceSpecification> specifications) {
this.keyspaceCreations = new ArrayList<>(specifications);
}
/**
* @return {@link List} of {@link CreateKeyspaceSpecification create keyspace specifications}.
*/
public List<CreateKeyspaceSpecification> getKeyspaceCreations() {
return Collections.unmodifiableList(keyspaceCreations);
}
/**
* Set a {@link List} of {@link DropKeyspaceSpecification drop keyspace specifications} that are executed when this
* factory is {@link #destroy() destroyed}. {@link DropKeyspaceSpecification Drop keyspace specifications} are
* executed on a system session with no keyspace set, before executing {@link #setShutdownScripts(List)}.
*
* @param specifications the {@link List} of {@link DropKeyspaceSpecification drop keyspace specifications}.
*/
public void setKeyspaceDrops(List<DropKeyspaceSpecification> specifications) {
this.keyspaceDrops = new ArrayList<>(specifications);
}
/**
* @return the {@link List} of {@link DropKeyspaceSpecification drop keyspace specifications}.
*/
public List<DropKeyspaceSpecification> getKeyspaceDrops() {
return Collections.unmodifiableList(keyspaceDrops);
}
/**
* Set a {@link List} of raw {@link String CQL statements} that are executed when this factory is
* {@link #afterPropertiesSet() initialized}. Scripts are executed on a system session with no keyspace set, after
* executing {@link #setKeyspaceCreations(List)}.
*
* @param scripts the scripts to execute on startup
*/
public void setStartupScripts(List<String> scripts) {
this.startupScripts = new ArrayList<>(scripts);
}
/**
* @return the startup scripts
*/
public List<String> getStartupScripts() {
return Collections.unmodifiableList(this.startupScripts);
}
/**
* Set a {@link List} of raw {@link String CQL statements} that are executed when this factory is {@link #destroy()
* destroyed}. {@link DropKeyspaceSpecification Drop keyspace specifications} are executed on a system session with no
* keyspace set, after executing {@link #setKeyspaceDrops(List)}.
*
* @param scripts the scripts to execute on shutdown
*/
public void setShutdownScripts(List<String> scripts) {
this.shutdownScripts = new ArrayList<>(scripts);
}
/**
* @return the shutdown scripts
*/
public List<String> getShutdownScripts() {
return Collections.unmodifiableList(this.shutdownScripts);
}
/**
* @param keyspaceSpecifications The {@link KeyspaceActionSpecification} to set.
*/
public void setKeyspaceSpecifications(Set<KeyspaceActionSpecification> keyspaceSpecifications) {
this.keyspaceSpecifications = new LinkedHashSet<>(keyspaceSpecifications);
}
/**
* @return the {@link KeyspaceActionSpecification} associated with this factory.
*/
public Set<KeyspaceActionSpecification> getKeyspaceSpecifications() {
return Collections.unmodifiableSet(this.keyspaceSpecifications);
}
/**
* Set the username to use with {@link com.datastax.driver.core.PlainTextAuthProvider}.
*
* @param username The username to set.
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Set the username to use with {@link com.datastax.driver.core.PlainTextAuthProvider}.
*
* @param password The password to set.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Set whether to use JMX reporting. Default is {@literal false}, see {@link #DEFAULT_JMX_REPORTING_ENABLED}.
*
* @param jmxReportingEnabled The jmxReportingEnabled to set.
*/
public void setJmxReportingEnabled(boolean jmxReportingEnabled) {
this.jmxReportingEnabled = jmxReportingEnabled;
}
/**
* Set whether to use SSL. Default is plain, see {@link #DEFAULT_SSL_ENABLED}.
*
* @param sslEnabled The sslEnabled to set.
*/
public void setSslEnabled(boolean sslEnabled) {
this.sslEnabled = sslEnabled;
}
/**
* @param sslOptions The sslOptions to set.
*/
public void setSslOptions(SSLOptions sslOptions) {
this.sslOptions = sslOptions;
}
/**
* @param hostStateListener The hostStateListener to set.
*/
public void setHostStateListener(Host.StateListener hostStateListener) {
this.hostStateListener = hostStateListener;
}
/**
* @param latencyTracker {@link LatencyTracker} to set.
*/
public void setLatencyTracker(LatencyTracker latencyTracker) {
this.latencyTracker = latencyTracker;
}
/**
* Configures the address translator used by the new cluster to translate IP addresses received from Cassandra nodes
* into locally query-able addresses.
*
* @param addressTranslator {@link AddressTranslator} used by the new cluster.
* @see com.datastax.driver.core.Cluster.Builder#withAddressTranslator(AddressTranslator)
* @see com.datastax.driver.core.policies.AddressTranslator
* @since 1.5
*/
public void setAddressTranslator(@Nullable AddressTranslator addressTranslator) {
this.addressTranslator = addressTranslator;
}
/**
* Sets the {@link ClusterBuilderConfigurer} used to apply additional configuration logic to the
* {@link com.datastax.driver.core.Cluster.Builder} object. {@link ClusterBuilderConfigurer} is invoked after all
* provided options are configured. The factory will {@link Builder#build()} the {@link Cluster} after applying
* {@link ClusterBuilderConfigurer}.
*
* @param clusterBuilderConfigurer {@link ClusterBuilderConfigurer} used to configure the
* {@link com.datastax.driver.core.Cluster.Builder}.
* @see org.springframework.data.cassandra.config.ClusterBuilderConfigurer
*/
public void setClusterBuilderConfigurer(@Nullable ClusterBuilderConfigurer clusterBuilderConfigurer) {
this.clusterBuilderConfigurer = clusterBuilderConfigurer;
}
/**
* An optional name for the cluster instance. This name appears in JMX metrics. Defaults to the bean name.
*
* @param clusterName optional name for the cluster.
* @see com.datastax.driver.core.Cluster.Builder#withClusterName(String)
* @since 1.5
*/
public void setClusterName(@Nullable String clusterName) {
this.clusterName = clusterName;
}
/**
* Sets the maximum time to wait for schema agreement before returning from a DDL query. The timeout is used to wait
* for all currently up hosts in the cluster to agree on the schema.
*
* @param seconds max schema agreement wait in seconds.
* @see com.datastax.driver.core.Cluster.Builder#withMaxSchemaAgreementWaitSeconds(int)
* @since 1.5
*/
public void setMaxSchemaAgreementWaitSeconds(int seconds) {
this.maxSchemaAgreementWaitSeconds = seconds;
}
/**
* Configures the speculative execution policy to use for the new cluster.
*
* @param speculativeExecutionPolicy {@link SpeculativeExecutionPolicy} to use with the new cluster.
* @see com.datastax.driver.core.Cluster.Builder#withSpeculativeExecutionPolicy(SpeculativeExecutionPolicy)
* @see com.datastax.driver.core.policies.SpeculativeExecutionPolicy
* @since 1.5
*/
public void setSpeculativeExecutionPolicy(@Nullable SpeculativeExecutionPolicy speculativeExecutionPolicy) {
this.speculativeExecutionPolicy = speculativeExecutionPolicy;
}
/**
* Configures the generator that will produce the client-side timestamp sent with each query.
*
* @param timestampGenerator {@link TimestampGenerator} used to produce a client-side timestamp sent with each query.
* @see com.datastax.driver.core.Cluster.Builder#withTimestampGenerator(TimestampGenerator)
* @see com.datastax.driver.core.TimestampGenerator
* @since 1.5
*/
public void setTimestampGenerator(@Nullable TimestampGenerator timestampGenerator) {
this.timestampGenerator = timestampGenerator;
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2013-2020 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
*
* https://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.config;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
/**
* Spring Data Cassandra XML namespace parser for the {@code cassandra:cluster} element.
*
* @author Matthew T. Adams
* @author Mark Paluch
*/
class CassandraClusterParser extends CassandraCqlClusterParser {
/* (non-Javadoc)
* @see org.springframework.cassandra.config.xml.CassandraCqlClusterParser#parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext);
return super.parseInternal(element, parserContext);
}
}

View File

@@ -1,289 +0,0 @@
/*
* Copyright 2013-2020 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
*
* https://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.config;
import static org.springframework.data.cassandra.config.ParsingUtils.*;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceActionSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceAttributes;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.SocketOptions;
/**
* Parses the {@literal <cluster>} element of the XML Configuration.
*
* @author Matthew T. Adams
* @author David Webb
* @author John Blum
* @author Mark Paluch
*/
class CassandraCqlClusterParser extends AbstractBeanDefinitionParser {
/* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : DefaultCqlBeanNames.CLUSTER;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CassandraClusterFactoryBean.class);
builder.setLazyInit(parserContext.isDefaultLazyInit());
builder.getRawBeanDefinition().setDestroyMethodName("destroy");
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
if (parserContext.isNested()) {
// inner bean definitions must have same scope as containing bean
builder.setScope(parserContext.getContainingBeanDefinition().getScope());
}
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
/**
* Parses cluster meta-data.
*
* @param element {@link Element} to parse.
* @param parserContext XML parser context and state.
* @param builder parent {@link BeanDefinitionBuilder}.
*/
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
addOptionalPropertyReference(builder, "addressTranslator", element, "address-translator-ref");
addOptionalPropertyReference(builder, "authProvider", element, "auth-info-provider-ref");
addOptionalPropertyReference(builder, "clusterBuilderConfigurer", element, "cluster-builder-configurer-ref");
addOptionalPropertyReference(builder, "hostStateListener", element, "host-state-listener-ref");
addOptionalPropertyReference(builder, "latencyTracker", element, "latency-tracker-ref");
addOptionalPropertyReference(builder, "loadBalancingPolicy", element, "load-balancing-policy-ref");
addOptionalPropertyReference(builder, "nettyOptions", element, "netty-options-ref");
addOptionalPropertyReference(builder, "reconnectionPolicy", element, "reconnection-policy-ref");
addOptionalPropertyReference(builder, "retryPolicy", element, "retry-policy-ref");
addOptionalPropertyReference(builder, "speculativeExecutionPolicy", element, "speculative-execution-policy-ref");
addOptionalPropertyReference(builder, "sslOptions", element, "ssl-options-ref");
addOptionalPropertyReference(builder, "timestampGenerator", element, "timestamp-generator-ref");
addOptionalPropertyValue(builder, "clusterName", element, "cluster-name");
addOptionalPropertyValue(builder, "contactPoints", element, "contact-points");
addOptionalPropertyValue(builder, "compressionType", element, "compression");
addOptionalPropertyValue(builder, "jmxReportingEnabled", element, "jmx-reporting-enabled");
addOptionalPropertyValue(builder, "maxSchemaAgreementWaitSeconds", element, "max-schema-agreement-wait-seconds");
addOptionalPropertyValue(builder, "metricsEnabled", element, "metrics-enabled");
addOptionalPropertyValue(builder, "password", element, "password");
addOptionalPropertyValue(builder, "port", element, "port");
addOptionalPropertyValue(builder, "sslEnabled", element, "ssl-enabled");
addOptionalPropertyValue(builder, "username", element, "username");
parseChildElements(element, parserContext, builder);
}
/**
* Parses child elements of cluster.
*
* @param element {@link Element} to parse.
* @param parserContext XML parser context and state.
* @param builder parent {@link BeanDefinitionBuilder}.
*/
protected void parseChildElements(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
ManagedSet<BeanDefinition> keyspaceActionSpecificationBeanDefinitions = new ManagedSet<>();
List<String> startupScripts = new ArrayList<>();
List<String> shutdownScripts = new ArrayList<>();
BeanDefinitionBuilder poolingOptionsBuilder = BeanDefinitionBuilder
.genericBeanDefinition(PoolingOptionsFactoryBean.class);
addOptionalPropertyReference(poolingOptionsBuilder, "initializationExecutor", element,
"initialization-executor-ref");
addOptionalPropertyValue(poolingOptionsBuilder, "heartbeatIntervalSeconds", element, "heartbeat-interval-seconds");
addOptionalPropertyValue(poolingOptionsBuilder, "idleTimeoutSeconds", element, "idle-timeout-seconds");
addOptionalPropertyValue(poolingOptionsBuilder, "poolTimeoutMilliseconds", element, "pool-timeout-milliseconds");
addOptionalPropertyValue(poolingOptionsBuilder, "maxQueueSize", element, "max-queue-size");
// parse child elements
for (Element subElement : DomUtils.getChildElements(element)) {
String name = subElement.getLocalName();
if ("keyspace".equals(name)) {
keyspaceActionSpecificationBeanDefinitions
.add(newKeyspaceActionSpecificationBeanDefinition(subElement, parserContext));
} else if ("local-pooling-options".equals(name)) {
parseLocalPoolingOptions(subElement, poolingOptionsBuilder);
} else if ("remote-pooling-options".equals(name)) {
parseRemotePoolingOptions(subElement, poolingOptionsBuilder);
} else if ("socket-options".equals(name)) {
builder.addPropertyValue("socketOptions", newSocketOptionsBeanDefinition(subElement, parserContext));
} else if ("startup-cql".equals(name)) {
startupScripts.add(parseScript(subElement));
} else if ("shutdown-cql".equals(name)) {
shutdownScripts.add(parseScript(subElement));
}
}
builder.addPropertyValue("keyspaceActions", keyspaceActionSpecificationBeanDefinitions);
builder.addPropertyValue("poolingOptions", getSourceBeanDefinition(poolingOptionsBuilder, parserContext, element));
builder.addPropertyValue("startupScripts", startupScripts);
builder.addPropertyValue("shutdownScripts", shutdownScripts);
}
/**
* Returns a {@link BeanDefinition} for a {@link KeyspaceActionSpecification} object.
*
* @param element Element being parsed.
* @param parserContext XML parser context and state.
* @return the {@link BeanDefinition} or {@literal null} if action is not given.
*/
private BeanDefinition newKeyspaceActionSpecificationBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(KeyspaceActionSpecificationFactoryBean.class);
// add required replication defaults
addRequiredPropertyValue(builder, "replicationStrategy", KeyspaceAttributes.DEFAULT_REPLICATION_STRATEGY.name());
addRequiredPropertyValue(builder, "replicationFactor",
String.valueOf(KeyspaceAttributes.DEFAULT_REPLICATION_FACTOR));
addRequiredPropertyValue(builder, "name", element, "name");
addOptionalPropertyValue(builder, "durableWrites", element, "durable-writes", "false");
addRequiredPropertyValue(builder, "action", element, "action");
parseReplication(DomUtils.getChildElementByTagName(element, "replication"), builder);
return getSourceBeanDefinition(builder, parserContext, element);
}
/**
* Parses the keyspace replication options and adds them to the supplied {@link BeanDefinitionBuilder}.
*
* @param element {@link Element} to parse.
* @param builder The {@link BeanDefinitionBuilder} to add the replication to
*/
private void parseReplication(@Nullable Element element, BeanDefinitionBuilder builder) {
ManagedList<String> networkTopologyDataCenters = new ManagedList<>();
ManagedList<String> networkTopologyReplicationFactors = new ManagedList<>();
if (element != null) {
addOptionalPropertyValue(builder, "replicationStrategy", element, "class",
KeyspaceAttributes.DEFAULT_REPLICATION_STRATEGY.name());
addOptionalPropertyValue(builder, "replicationFactor", element, "replication-factor",
String.valueOf(KeyspaceAttributes.DEFAULT_REPLICATION_FACTOR));
// DataCenters only apply to NetworkTopologyStrategy
for (Element dataCenter : DomUtils.getChildElementsByTagName(element, "data-center")) {
networkTopologyDataCenters.add(dataCenter.getAttribute("name"));
networkTopologyReplicationFactors.add(dataCenter.getAttribute("replication-factor"));
}
}
builder.addPropertyValue("networkTopologyDataCenters", networkTopologyDataCenters);
builder.addPropertyValue("networkTopologyReplicationFactors", networkTopologyReplicationFactors);
}
/**
* Parses local pooling options.
*
* @param element {@link Element} to parse.
* @param builder {@link BeanDefinitionBuilder} used to build a {@link PoolingOptions} {@link BeanDefinition}.
*/
void parseLocalPoolingOptions(Element element, BeanDefinitionBuilder builder) {
addOptionalPropertyValue(builder, "localCoreConnections", element, "core-connections", null);
addOptionalPropertyValue(builder, "localMaxConnections", element, "max-connections", null);
addOptionalPropertyValue(builder, "localMaxSimultaneousRequests", element, "max-simultaneous-requests", null);
addOptionalPropertyValue(builder, "localMinSimultaneousRequests", element, "min-simultaneous-requests", null);
}
/**
* Parses remote pooling options.
*
* @param element {@link Element} to parse.
* @param builder {@link BeanDefinitionBuilder} used to build a {@link PoolingOptions} {@link BeanDefinition}.
*/
void parseRemotePoolingOptions(Element element, BeanDefinitionBuilder builder) {
addOptionalPropertyValue(builder, "remoteCoreConnections", element, "core-connections", null);
addOptionalPropertyValue(builder, "remoteMaxConnections", element, "max-connections", null);
addOptionalPropertyValue(builder, "remoteMaxSimultaneousRequests", element, "max-simultaneous-requests", null);
addOptionalPropertyValue(builder, "remoteMinSimultaneousRequests", element, "min-simultaneous-requests", null);
}
/**
* Parse CQL script {@link Element}s.
*
* @param element {@link Element} to parse.
* @return return the contents of the {@link Element}, which should contain the CQL script.
*/
String parseScript(Element element) {
return element.getTextContent();
}
/**
* Returns a {@link BeanDefinition} for a {@link SocketOptions} object.
*
* @param element {@link Element} to parse.
* @param parserContext XML parser context and state.
* @return {@link BeanDefinition} for {@link SocketOptionsFactoryBean}.
*/
BeanDefinition newSocketOptionsBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SocketOptionsFactoryBean.class);
addOptionalPropertyValue(builder, "connectTimeoutMillis", element, "connect-timeout-millis");
addOptionalPropertyValue(builder, "keepAlive", element, "keep-alive");
addOptionalPropertyValue(builder, "readTimeoutMillis", element, "read-timeout-millis");
addOptionalPropertyValue(builder, "receiveBufferSize", element, "receive-buffer-size");
addOptionalPropertyValue(builder, "reuseAddress", element, "reuse-address");
addOptionalPropertyValue(builder, "sendBufferSize", element, "send-buffer-size");
addOptionalPropertyValue(builder, "soLinger", element, "so-linger");
addOptionalPropertyValue(builder, "tcpNoDelay", element, "tcp-no-delay");
return getSourceBeanDefinition(builder, parserContext, element);
}
}

View File

@@ -16,31 +16,14 @@
package org.springframework.data.cassandra.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.core.cql.CassandraExceptionTranslator;
import org.springframework.data.cassandra.core.cql.CqlOperations;
import org.springframework.data.cassandra.core.cql.CqlTemplate;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Session;
/**
* Factory for creating and configuring a Cassandra {@link Session}, which is a thread-safe singleton. As such, it is
* sufficient to have one {@link Session} per application and keyspace.
* Factory for creating and configuring a Cassandra {@link CqlSession}, which is a thread-safe singleton. As such, it is
* sufficient to have one {@link CqlSession} per application and keyspace.
*
* @author Alex Shvid
* @author Matthew T. Adams
@@ -52,216 +35,9 @@ import com.datastax.driver.core.Session;
* @see org.springframework.dao.support.PersistenceExceptionTranslator
* @see CqlTemplate
* @see CassandraExceptionTranslator
* @see com.datastax.driver.core.Cluster
* @see com.datastax.driver.core.Session
* @deprecated since 3.0, use {@link CqlSessionFactoryBean} directly.
*/
public class CassandraCqlSessionFactoryBean
implements FactoryBean<Session>, InitializingBean, DisposableBean, PersistenceExceptionTranslator {
@Deprecated
public class CassandraCqlSessionFactoryBean extends CqlSessionFactoryBean {
protected final Logger logger = LoggerFactory.getLogger(getClass());
private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
private @Nullable Cluster cluster;
private List<String> startupScripts = Collections.emptyList();
private List<String> shutdownScripts = Collections.emptyList();
private @Nullable Session session;
private @Nullable String keyspaceName;
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
this.session = connect(getKeyspaceName());
executeScripts(getStartupScripts());
}
/* (non-Javadoc) */
Session connect(@Nullable String keyspaceName) {
return StringUtils.hasText(keyspaceName) ? getCluster().connect(keyspaceName) : getCluster().connect();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public Session getObject() {
return this.session;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<? extends Session> getObjectType() {
return this.session != null ? this.session.getClass() : Session.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return true;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
@Override
public void destroy() throws Exception {
executeScripts(getShutdownScripts());
getSession().close();
}
/**
* Executes the given Cassandra CQL scripts. The {@link Session} must be connected when this method is called.
*/
protected void executeScripts(List<String> scripts) {
if (!CollectionUtils.isEmpty(scripts)) {
CqlOperations template = newCqlOperations(getSession());
scripts.forEach(script -> {
logger.info("executing raw CQL [{}]", script);
template.execute(script);
});
}
}
CqlOperations newCqlOperations(Session session) {
return new CqlTemplate(session);
}
/* (non-Javadoc)
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
*/
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException e) {
return this.exceptionTranslator.translateExceptionIfPossible(e);
}
/**
* Null-safe operation to determine whether the Cassandra {@link Session} is connected or not.
*
* @return a boolean value indicating whether the Cassandra {@link Session} is connected.
* @see com.datastax.driver.core.Session#isClosed()
* @see #getObject()
*/
public boolean isConnected() {
Session session = getObject();
return !(session == null || session.isClosed());
}
/**
* Sets a reference to the Cassandra {@link Cluster} to use.
*
* @param cluster a reference to the Cassandra {@link Cluster} used by this application.
* @throws IllegalArgumentException if the {@link Cluster} reference is null.
* @see com.datastax.driver.core.Cluster
* @see #getCluster()
*/
public void setCluster(Cluster cluster) {
Assert.notNull(cluster, "Cluster must not be null");
this.cluster = cluster;
}
/**
* Returns a reference to the configured Cassandra {@link Cluster} used by this application.
*
* @return a reference to the configured Cassandra {@link Cluster}.
* @throws IllegalStateException if the reference to the {@link Cluster} was not properly initialized.
* @see com.datastax.driver.core.Cluster
* @see #setCluster(Cluster)
*/
protected Cluster getCluster() {
Assert.state(this.cluster != null, "Cluster was not properly initialized");
return this.cluster;
}
/**
* Sets the name of the Cassandra Keyspace to connect to. Passing {@literal null}, an empty String, or whitespace will
* cause the Cassandra System Keyspace to be used.
*
* @param keyspaceName a String indicating the name of the Keyspace in which to connect.
* @see #getKeyspaceName()
*/
public void setKeyspaceName(@Nullable String keyspaceName) {
this.keyspaceName = keyspaceName;
}
/**
* Gets the name of the Cassandra Keyspace to connect to.
*
* @return the name of the Cassandra Keyspace to connect to as a String.
* @see #setKeyspaceName(String)
*/
@Nullable
protected String getKeyspaceName() {
return this.keyspaceName;
}
/**
* Returns a reference to the connected Cassandra {@link Session}.
*
* @return a reference to the connected Cassandra {@link Session}.
* @throws IllegalStateException if the Cassandra {@link Session} was not properly initialized.
* @see com.datastax.driver.core.Session
*/
protected Session getSession() {
Session session = getObject();
Assert.state(session != null, "Session was not properly initialized");
return session;
}
/**
* Sets CQL scripts to be executed immediately after the session is connected.
*/
public void setStartupScripts(@Nullable List<String> scripts) {
this.startupScripts = (scripts != null ? new ArrayList<>(scripts) : Collections.emptyList());
}
/**
* Returns an unmodifiable list of startup scripts.
*/
public List<String> getStartupScripts() {
return Collections.unmodifiableList(this.startupScripts);
}
/**
* Sets CQL scripts to be executed immediately before the session is shutdown.
*/
public void setShutdownScripts(@Nullable List<String> scripts) {
this.shutdownScripts = scripts != null ? new ArrayList<>(scripts) : Collections.emptyList();
}
/**
* Returns an unmodifiable list of shutdown scripts.
*/
public List<String> getShutdownScripts() {
return Collections.unmodifiableList(this.shutdownScripts);
}
}

View File

@@ -1,125 +0,0 @@
/*
* Copyright 2013-2020 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
*
* https://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.config;
import static org.springframework.data.cassandra.config.ParsingUtils.*;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
/**
* Parser for &lt;session&gt; definitions.
*
* @author David Webb
* @author Matthew T. Adams
* @author Mark Paluch
*/
class CassandraCqlSessionParser extends AbstractSingleBeanDefinitionParser {
/* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
*/
@Override
protected Class<?> getBeanClass(Element element) {
return CassandraCqlSessionFactoryBean.class;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : DefaultCqlBeanNames.SESSION;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
*/
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
setDefaultProperties(builder);
parseSessionAttributes(element, parserContext, builder);
parseSessionChildElements(element, parserContext, builder);
}
protected void setDefaultProperties(BeanDefinitionBuilder builder) {
addRequiredPropertyReference(builder, "cluster", DefaultCqlBeanNames.CLUSTER);
}
private void parseSessionAttributes(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
NamedNodeMap attributes = element.getAttributes();
int length = attributes.getLength();
for (int i = 0; i < length; i++) {
Attr attribute = (Attr) attributes.item(i);
if ("id".equals(attribute.getName())) {
continue;
}
String name = attribute.getName();
if ("keyspace-name".equals(name)) {
addRequiredPropertyValue(builder, "keyspaceName", attribute);
} else if ("cluster-ref".equals(name)) {
addOptionalPropertyReference(builder, "cluster", attribute, DefaultCqlBeanNames.CLUSTER);
} else {
parseUnhandledSessionElementAttribute(attribute, parserContext, builder);
}
}
}
private void parseSessionChildElements(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
for (Element child : DomUtils.getChildElements(element)) {
if ("startup-cql".equals(child.getLocalName())) {
builder.addPropertyValue("startupScripts", DomUtils.getTextValue(child));
} else if ("shutdown-cql".equals(child.getLocalName())) {
builder.addPropertyValue("shutdownScripts", DomUtils.getTextValue(child));
} else {
throw new IllegalStateException(String.format("encountered unhandled element [%s]", child.getLocalName()));
}
}
}
/**
* Parse the given session element attribute. This method is intended to be overridden by subclasses so that any
* attributes not known to this class can be properly parsed. The default implementation throws
* {@link IllegalStateException}.
*/
protected void parseUnhandledSessionElementAttribute(Attr attribute, ParserContext parserContext,
BeanDefinitionBuilder builder) {
throw new IllegalStateException(
String.format("encountered unhandled session element attribute [%s]", attribute.getName()));
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.Session;
import com.datastax.oss.driver.api.core.CqlSession;
/**
* Factory for configuring a {@link CqlTemplate}.
@@ -73,13 +73,13 @@ public class CassandraCqlTemplateFactoryBean implements FactoryBean<CqlTemplate>
}
/**
* Sets the Cassandra {@link Session} to use. The {@link CqlTemplate} will use the logged keyspace of the underlying
* {@link Session}. Don't change the keyspace using CQL but use multiple {@link Session} and {@link CqlTemplate}
* beans.
* Sets the Cassandra {@link CqlSession} to use. The {@link CqlTemplate} will use the logged keyspace of the
* underlying {@link CqlSession}. Don't change the keyspace using CQL but use multiple {@link CqlSession} and
* {@link CqlTemplate} beans.
*
* @param session must not be {@literal null}.
*/
public void setSession(Session session) {
public void setSession(CqlSession session) {
Assert.notNull(session, "Session must not be null");

View File

@@ -36,7 +36,7 @@ import org.springframework.data.cassandra.core.convert.MappingCassandraConverter
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.Session;
import com.datastax.oss.driver.api.core.session.Session;
/**
* {@link BeanFactoryPostProcessor} that does its best to register any missing Spring Data Cassandra beans that can be
@@ -57,9 +57,10 @@ import com.datastax.driver.core.Session;
* {@link CassandraMappingContext} definition present, then it will be used in the {@link CassandraMappingContext} bean
* definition.
* <p/>
* It requires that a single {@link Session} or {@link CassandraSessionFactoryBean} definition be present. As described
* above, multiple {@link Session} definitions, multiple {@link CassandraSessionFactoryBean} definitions, or both a
* {@link Session} and {@link CassandraSessionFactoryBean} will cause an {@link IllegalStateException} to be thrown.
* It requires that a single {@link CqlSession} or {@link CassandraSessionFactoryBean} definition be present. As
* described above, multiple {@link CqlSession} definitions, multiple {@link CassandraSessionFactoryBean} definitions,
* or both a {@link CqlSession} and {@link CassandraSessionFactoryBean} will cause an {@link IllegalStateException} to
* be thrown.
*
* @author Matthew T. Adams
* @author Mark Paluch

View File

@@ -15,15 +15,6 @@
*/
package org.springframework.data.cassandra.config;
import org.springframework.data.cassandra.core.CassandraAdminOperations;
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
import org.springframework.data.cassandra.core.CassandraPersistentEntitySchemaCreator;
import org.springframework.data.cassandra.core.CassandraPersistentEntitySchemaDropper;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Factory to create and configure a Cassandra {@link com.datastax.driver.core.Session} with support for executing CQL
* and initializing the database schema (a.k.a. keyspace).
@@ -34,155 +25,9 @@ import org.springframework.util.Assert;
* @author Mark Paluch
* @see com.datastax.driver.core.KeyspaceMetadata
* @see com.datastax.driver.core.TableMetadata
* @deprecated since 3.0, use {@link CqlSessionFactoryBean} directly.
*/
public class CassandraSessionFactoryBean extends CassandraCqlSessionFactoryBean {
@Deprecated
public class CassandraSessionFactoryBean extends CqlSessionFactoryBean {
protected static final boolean DEFAULT_CREATE_IF_NOT_EXISTS = false;
protected static final boolean DEFAULT_DROP_TABLES = false;
protected static final boolean DEFAULT_DROP_UNUSED_TABLES = false;
private @Nullable CassandraAdminOperations admin;
private @Nullable CassandraConverter converter;
private SchemaAction schemaAction = SchemaAction.NONE;
/*
* (non-Javadoc)
* @see org.springframework.cassandra.config.CassandraCqlSessionFactoryBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(this.converter != null, "Converter was not properly initialized");
super.afterPropertiesSet();
this.admin = new CassandraAdminTemplate(getSession(), this.converter);
performSchemaAction();
}
/**
* Perform the configure {@link SchemaAction} using {@link CassandraMappingContext} metadata.
*/
protected void performSchemaAction() {
boolean create = false;
boolean drop = DEFAULT_DROP_TABLES;
boolean dropUnused = DEFAULT_DROP_UNUSED_TABLES;
boolean ifNotExists = DEFAULT_CREATE_IF_NOT_EXISTS;
switch (this.schemaAction) {
case RECREATE_DROP_UNUSED:
dropUnused = true;
case RECREATE:
drop = true;
case CREATE_IF_NOT_EXISTS:
ifNotExists = SchemaAction.CREATE_IF_NOT_EXISTS.equals(this.schemaAction);
case CREATE:
create = true;
case NONE:
default:
// do nothing
}
if (create) {
createTables(drop, dropUnused, ifNotExists);
}
}
/**
* Set the {@link CassandraConverter} to use. Schema actions will derive table and user type information from the
* {@link CassandraMappingContext} inside {@code converter}.
*
* @param converter must not be {@literal null}.
*/
public void setConverter(CassandraConverter converter) {
Assert.notNull(converter, "CassandraConverter must not be null");
this.converter = converter;
}
/**
* @return the {@link CassandraConverter}.
*/
@Nullable
public CassandraConverter getConverter() {
return this.converter;
}
/**
* @return the {@link CassandraMappingContext}.
*/
protected CassandraMappingContext getMappingContext() {
CassandraConverter converter = getConverter();
Assert.state(converter != null, "CassandraConverter was not properly initialized");
return converter.getMappingContext();
}
/**
* Set the {@link SchemaAction}.
*
* @param schemaAction must not be {@literal null}.
*/
public void setSchemaAction(SchemaAction schemaAction) {
Assert.notNull(schemaAction, "SchemaAction must not be null");
this.schemaAction = schemaAction;
}
/**
* @return the {@link SchemaAction}.
*/
public SchemaAction getSchemaAction() {
return this.schemaAction;
}
/**
* Perform schema actions.
*
* @param drop {@literal true} to drop types/tables.
* @param dropUnused {@literal true} to drop unused types/tables (i.e. types/tables not know to be used by
* {@link CassandraMappingContext}).
* @param ifNotExists {@literal true} to perform creations fail-safe by adding {@code IF NOT EXISTS} to each creation
* statement.
*/
protected void createTables(boolean drop, boolean dropUnused, boolean ifNotExists) {
performSchemaActions(drop, dropUnused, ifNotExists);
}
private void performSchemaActions(boolean drop, boolean dropUnused, boolean ifNotExists) {
CassandraPersistentEntitySchemaCreator schemaCreator = new CassandraPersistentEntitySchemaCreator(
getMappingContext(), getCassandraAdminOperations());
if (drop) {
CassandraPersistentEntitySchemaDropper schemaDropper = new CassandraPersistentEntitySchemaDropper(
getMappingContext(), getCassandraAdminOperations());
schemaDropper.dropTables(dropUnused);
schemaDropper.dropUserTypes(dropUnused);
}
schemaCreator.createUserTypes(ifNotExists);
schemaCreator.createTables(ifNotExists);
schemaCreator.createIndexes(ifNotExists);
}
/**
* @return the {@link CassandraAdminOperations}.
*/
protected CassandraAdminOperations getCassandraAdminOperations() {
Assert.state(this.admin != null, "CassandraAdminOperations was not properly initialized");
return this.admin;
}
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright 2013-2020 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
*
* https://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.config;
import static org.springframework.data.cassandra.config.ParsingUtils.*;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
/**
* Spring Data Cassandra XML namespace parser for the {@code cassandra:session} element.
*
* @author Matthew T. Adams
* @author Mark Paluch
*/
class CassandraSessionParser extends CassandraCqlSessionParser {
/* (non-Javadoc)
* @see org.springframework.cassandra.config.xml.CassandraCqlSessionParser#getBeanClass(org.w3c.dom.Element)
*/
@Override
protected Class<?> getBeanClass(Element element) {
return CassandraSessionFactoryBean.class;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.config.xml.CassandraCqlSessionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
*/
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, parserContext, builder);
CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.config.xml.CassandraCqlSessionParser#parseUnhandledSessionElementAttribute(org.w3c.dom.Attr, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
*/
@Override
protected void parseUnhandledSessionElementAttribute(Attr attribute, ParserContext parserContext,
BeanDefinitionBuilder builder) {
String name = attribute.getName();
if ("cassandra-converter-ref".equals(name)) {
addOptionalPropertyReference(builder, "converter", attribute, DefaultBeanNames.CONVERTER);
} else if ("schema-action".equals(name)) {
addOptionalPropertyValue(builder, "schemaAction", attribute, SchemaAction.NONE.name());
} else {
super.parseUnhandledSessionElementAttribute(attribute, parserContext, builder);
}
}
/* (non-Javadoc)
* @see org.springframework.cassandra.config.xml.CassandraCqlSessionParser#setDefaultProperties(org.springframework.beans.factory.support.BeanDefinitionBuilder)
*/
@Override
protected void setDefaultProperties(BeanDefinitionBuilder builder) {
super.setDefaultProperties(builder);
addRequiredPropertyValue(builder, "schemaAction", SchemaAction.NONE.name());
addRequiredPropertyReference(builder, "converter", DefaultBeanNames.CONVERTER);
}
}

View File

@@ -26,7 +26,7 @@ import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.Session;
import com.datastax.oss.driver.api.core.CqlSession;
/**
* Factory for configuring a {@link CassandraTemplate}.
@@ -91,12 +91,12 @@ public class CassandraTemplateFactoryBean implements FactoryBean<CassandraTempla
}
/**
* Sets the Cassandra {@link Session} to use. The {@link CassandraTemplate} will use the logged keyspace of the
* underlying {@link Session}. Don't change the keyspace using CQL but use a {@link SessionFactory}.
* Sets the Cassandra {@link CqlSession} to use. The {@link CassandraTemplate} will use the logged keyspace of the
* underlying {@link CqlSession}. Don't change the keyspace using CQL but use a {@link SessionFactory}.
*
* @param session must not be {@literal null}.
*/
public void setSession(Session session) {
public void setSession(CqlSession session) {
Assert.notNull(session, "Session must not be null");
@@ -105,7 +105,7 @@ public class CassandraTemplateFactoryBean implements FactoryBean<CassandraTempla
/**
* Sets the Cassandra {@link SessionFactory} to use. The {@link CassandraTemplate} will use the logged keyspace of the
* underlying {@link Session}. Don't change the keyspace using CQL.
* underlying {@link CqlSession}. Don't change the keyspace using CQL.
*
* @param sessionFactory must not be {@literal null}.
* @since 2.0
@@ -119,7 +119,8 @@ public class CassandraTemplateFactoryBean implements FactoryBean<CassandraTempla
/**
* Sets the Cassandra {@link CqlOperations} to use. The {@link CassandraTemplate} will use the logged keyspace of the
* underlying {@link Session}. Don't change the keyspace using CQL but use {@link #setSessionFactory(SessionFactory)}.
* underlying {@link CqlSession}. Don't change the keyspace using CQL but use
* {@link #setSessionFactory(SessionFactory)}.
*
* @param cqlOperations must not be {@literal null}.
* @since 2.0

View File

@@ -54,8 +54,8 @@ import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
/**
* Factory for creating and configuring a Cassandra {@link Session}, which is a thread-safe singleton. As such, it is
* sufficient to have one {@link Session} per application and keyspace.
* Factory for creating and configuring a Cassandra {@link CqlSession}, which is a thread-safe singleton. As such, it is
* sufficient to have one {@link CqlSession} per application and keyspace.
*
* @author Alex Shvid
* @author Matthew T. Adams
@@ -83,6 +83,7 @@ public class CqlSessionFactoryBean implements FactoryBean<CqlSession>, Initializ
private @Nullable String username;
private @Nullable String keyspaceName;
private @Nullable String localDatacenter;
private List<KeyspaceActions> keyspaceActions = new ArrayList<>();
private Set<KeyspaceActionSpecification> keyspaceSpecifications = new HashSet<>();
@@ -101,6 +102,20 @@ public class CqlSessionFactoryBean implements FactoryBean<CqlSession>, Initializ
private SchemaAction schemaAction = SchemaAction.NONE;
/**
* Null-safe operation to determine whether the Cassandra {@link CqlSession} is connected or not.
*
* @return a boolean value indicating whether the Cassandra {@link CqlSession} is connected.
* @see Session#isClosed()
* @see #getObject()
*/
public boolean isConnected() {
CqlSession session = getObject();
return !(session == null || session.isClosed());
}
/**
* Set a comma-delimited string of the contact points (hosts) to connect to. Default is {@code localhost}; see
* {@link #DEFAULT_CONTACT_POINTS}.
@@ -160,36 +175,6 @@ public class CqlSessionFactoryBean implements FactoryBean<CqlSession>, Initializ
return this.keyspaceName;
}
/**
* Null-safe operation to determine whether the Cassandra {@link Session} is connected or not.
*
* @return a boolean value indicating whether the Cassandra {@link Session} is connected.
* @see Session#isClosed()
* @see #getObject()
*/
public boolean isConnected() {
CqlSession session = getObject();
return !(session == null || session.isClosed());
}
/**
* Returns a reference to the connected Cassandra {@link Session}.
*
* @return a reference to the connected Cassandra {@link Session}.
* @throws IllegalStateException if the Cassandra {@link Session} was not properly initialized.
* @see Session
*/
protected CqlSession getSession() {
CqlSession session = getObject();
Assert.state(session != null, "Session was not properly initialized");
return session;
}
/**
* @return the {@link List} of {@link KeyspaceActions}.
*/
@@ -278,6 +263,31 @@ public class CqlSessionFactoryBean implements FactoryBean<CqlSession>, Initializ
return Collections.unmodifiableSet(this.keyspaceSpecifications);
}
/**
* Sets the name of the local datacenter.
*
* @param localDatacenter a String indicating the name of the local datacenter.
*/
public void setLocalDatacenter(@Nullable String localDatacenter) {
this.localDatacenter = localDatacenter;
}
/**
* Returns a reference to the connected Cassandra {@link CqlSession}.
*
* @return a reference to the connected Cassandra {@link CqlSession}.
* @throws IllegalStateException if the Cassandra {@link CqlSession} was not properly initialized.
* @see Session
*/
protected CqlSession getSession() {
CqlSession session = getObject();
Assert.state(session != null, "Session was not properly initialized");
return session;
}
/**
* Sets CQL scripts to be executed immediately after the session is connected.
*
@@ -434,6 +444,10 @@ public class CqlSessionFactoryBean implements FactoryBean<CqlSession>, Initializ
builder.withAuthCredentials(this.username, this.password);
}
if (StringUtils.hasText(this.localDatacenter)) {
builder.withLocalDatacenter(this.localDatacenter);
}
return builder;
}
@@ -553,7 +567,7 @@ public class CqlSessionFactoryBean implements FactoryBean<CqlSession>, Initializ
}
/**
* Executes the given Cassandra CQL scripts. The {@link Session} must be connected when this method is called.
* Executes the given Cassandra CQL scripts. The {@link CqlSession} must be connected when this method is called.
*/
private void executeScripts(Stream<String> scripts, CqlSession session) {

View File

@@ -67,6 +67,7 @@ class CqlSessionParser extends AbstractSingleBeanDefinitionParser {
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
addOptionalPropertyValue(builder, "keyspaceName", element, "keyspace-name");
addOptionalPropertyValue(builder, "localDatacenter", element, "local-datacenter");
addOptionalPropertyValue(builder, "contactPoints", element, "contact-points");
addOptionalPropertyValue(builder, "password", element, "password");
addOptionalPropertyValue(builder, "port", element, "port");

View File

@@ -23,7 +23,10 @@ package org.springframework.data.cassandra.config;
*/
public interface DefaultCqlBeanNames {
String CLUSTER = "cassandraCluster";
/**
* @deprecated since 3.0. Cassandra driver 4 no longer provides a {@code Cluster} type.
*/
@Deprecated String CLUSTER = "cassandraCluster";
String SESSION = "cassandraSession";
String SESSION_FACTORY = "cassandraSessionFactory";
String TEMPLATE = "cqlTemplate";

View File

@@ -33,6 +33,8 @@ import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceOption;
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceOption.ReplicationStrategy;
import org.springframework.data.cassandra.core.cql.keyspace.Option;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Factory to create {@link CreateKeyspaceSpecification} and {@link DropKeyspaceSpecification}.
*
@@ -42,7 +44,7 @@ import org.springframework.data.cassandra.core.cql.keyspace.Option;
@RequiredArgsConstructor
class KeyspaceActionSpecificationFactory {
private final KeyspaceIdentifier name;
private final CqlIdentifier name;
private final List<DataCenterReplication> replications;
@@ -60,7 +62,7 @@ class KeyspaceActionSpecificationFactory {
* @return the new {@link KeyspaceActionSpecificationFactoryBuilder} for {@code keyspaceName}.
*/
public static KeyspaceActionSpecificationFactoryBuilder builder(String keyspaceName) {
return builder(KeyspaceIdentifier.of(keyspaceName));
return builder(CqlIdentifier.fromCql(keyspaceName));
}
/**
@@ -69,8 +71,22 @@ class KeyspaceActionSpecificationFactory {
*
* @param keyspaceName must not be {@literal null} or empty.
* @return the new {@link KeyspaceActionSpecificationFactoryBuilder} for {@code keyspaceName}.
* @deprecated since 3.0, use {@link #builder(CqlIdentifier)}.
*/
@Deprecated
public static KeyspaceActionSpecificationFactoryBuilder builder(KeyspaceIdentifier keyspaceName) {
return builder(keyspaceName.toCqlIdentifier());
}
/**
* Create a new {@link KeyspaceActionSpecificationFactoryBuilder} to configure a new
* {@link KeyspaceActionSpecificationFactory}.
*
* @param keyspaceName must not be {@literal null} or empty.
* @return the new {@link KeyspaceActionSpecificationFactoryBuilder} for {@code keyspaceName}.
* @since 3.0
*/
public static KeyspaceActionSpecificationFactoryBuilder builder(CqlIdentifier keyspaceName) {
return new KeyspaceActionSpecificationFactoryBuilder(keyspaceName);
}
@@ -170,7 +186,7 @@ class KeyspaceActionSpecificationFactory {
static class KeyspaceActionSpecificationFactoryBuilder {
private final KeyspaceIdentifier name;
private final CqlIdentifier name;
private final List<DataCenterReplication> replications = new ArrayList<>();
@@ -180,7 +196,7 @@ class KeyspaceActionSpecificationFactory {
private boolean durableWrites = false;
private KeyspaceActionSpecificationFactoryBuilder(KeyspaceIdentifier name) {
private KeyspaceActionSpecificationFactoryBuilder(CqlIdentifier name) {
this.name = name;
}

View File

@@ -1,683 +0,0 @@
/*
* Copyright 2013-2020 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
*
* https://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.config;
import static org.springframework.util.ReflectionUtils.*;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.concurrent.Executor;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import com.datastax.driver.core.HostDistance;
import com.datastax.driver.core.PoolingOptions;
/**
* Spring {@link FactoryBean} for the Cassandra Java driver {@link PoolingOptions}.
*
* @author Matthew T. Adams
* @author David Webb
* @author Mark Paluch
* @author John Blum
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.InitializingBean
* @see com.datastax.driver.core.PoolingOptions
*/
@SuppressWarnings({ "unused", "WeakerAccess" })
public class PoolingOptionsFactoryBean implements FactoryBean<PoolingOptions>, InitializingBean {
private static final PoolingOptions DEFAULT = new PoolingOptions();
// Compatibility between 3.1.1 and earlier Cassandra driver versions
private static final Optional<Method> SET_MAX_QUEUE_SIZE;
// Compatibility between 3.1.1 and earlier Cassandra driver versions
private static final Optional<Method> GET_MAX_QUEUE_SIZE;
static {
SET_MAX_QUEUE_SIZE = Optional
.ofNullable(ReflectionUtils.findMethod(PoolingOptions.class, "setMaxQueueSize", int.class));
GET_MAX_QUEUE_SIZE = Optional.ofNullable(ReflectionUtils.findMethod(PoolingOptions.class, "getMaxQueueSize"));
}
private @Nullable Executor initializationExecutor;
private int heartbeatIntervalSeconds;
private int idleTimeoutSeconds;
private @Nullable Integer localCoreConnections;
private @Nullable Integer localMaxConnections;
private @Nullable Integer localMaxSimultaneousRequests;
private @Nullable Integer localMinSimultaneousRequests;
// Deprecated since Cassandra Driver 3.1.1
private int poolTimeoutMilliseconds;
// Available since Cassandra Driver 3.1.1
private int maxQueueSize;
private @Nullable Integer remoteCoreConnections;
private @Nullable Integer remoteMaxConnections;
private @Nullable Integer remoteMaxSimultaneousRequests;
private @Nullable Integer remoteMinSimultaneousRequests;
private @Nullable PoolingOptions poolingOptions;
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
poolingOptions = configureRemoteHostDistancePoolingOptions(
configureLocalHostDistancePoolingOptions(newPoolingOptions()));
if (heartbeatIntervalSeconds != DEFAULT.getHeartbeatIntervalSeconds()) {
poolingOptions.setHeartbeatIntervalSeconds(heartbeatIntervalSeconds);
}
if (idleTimeoutSeconds != DEFAULT.getIdleTimeoutSeconds()) {
poolingOptions.setIdleTimeoutSeconds(idleTimeoutSeconds);
}
if (initializationExecutor != null) {
poolingOptions.setInitializationExecutor(initializationExecutor);
}
if (poolTimeoutMilliseconds != DEFAULT.getPoolTimeoutMillis()) {
poolingOptions.setPoolTimeoutMillis(poolTimeoutMilliseconds);
}
if (!isDefaultMaxQueueSize()) {
SET_MAX_QUEUE_SIZE.ifPresent(method -> invokeMethod(method, poolingOptions, maxQueueSize));
}
}
private boolean isDefaultMaxQueueSize() {
return GET_MAX_QUEUE_SIZE.map(method -> {
Integer defaultMaxQueueSize = (Integer) invokeMethod(method, poolingOptions);
return defaultMaxQueueSize != null && defaultMaxQueueSize == maxQueueSize;
}).orElse(false);
}
/*
* (non-Javadoc)
* @see com.datastax.driver.core.PoolingOptions
*/
PoolingOptions newPoolingOptions() {
return new PoolingOptions();
}
/**
* Constructs and returns a {@link PoolingOptionsFactoryBean.HostDistancePoolingOptions} instance initialized with the
* {@link HostDistance#LOCAL}-based {@link PoolingOptions} as configured on this {@link PoolingOptionsFactoryBean}.
*
* @return {@link PoolingOptionsFactoryBean.HostDistancePoolingOptions} initialized with this
* {@link PoolingOptionsFactoryBean}'s {@link HostDistance#LOCAL}-based {@link PoolingOptions}.
* @see com.datastax.driver.core.HostDistance#LOCAL
* @see com.datastax.driver.core.PoolingOptions
* @see PoolingOptionsFactoryBean.HostDistancePoolingOptions
* @see PoolingOptionsFactoryBean.LocalHostDistancePoolingOptions
*/
protected HostDistancePoolingOptions newLocalHostDistancePoolingOptions() {
return new LocalHostDistancePoolingOptions(getLocalCoreConnections(), getLocalMaxConnections(),
getLocalMaxSimultaneousRequests(), getLocalMinSimultaneousRequests());
}
/**
* Constructs and returns a {@link PoolingOptionsFactoryBean.HostDistancePoolingOptions} instance initialized with the
* {@link HostDistance#REMOTE}-based {@link PoolingOptions} as configured on this {@link PoolingOptionsFactoryBean}.
*
* @return {@link PoolingOptionsFactoryBean.HostDistancePoolingOptions} initialized with this
* {@link PoolingOptionsFactoryBean}'s {@link HostDistance#REMOTE}-based {@link PoolingOptions}.
* @see com.datastax.driver.core.HostDistance#REMOTE
* @see com.datastax.driver.core.PoolingOptions
* @see PoolingOptionsFactoryBean.HostDistancePoolingOptions
* @see PoolingOptionsFactoryBean.RemoteHostDistancePoolingOptions
*/
protected HostDistancePoolingOptions newRemoteHostDistancePoolingOptions() {
return new RemoteHostDistancePoolingOptions(getRemoteCoreConnections(), getRemoteMaxConnections(),
getRemoteMaxSimultaneousRequests(), getRemoteMinSimultaneousRequests());
}
/**
* Configures the {@link HostDistance#LOCAL} connection settings on the given {@link PoolingOptions}.
*
* @param poolingOptions the {@link PoolingOptions} to configure.
* @return the given {@link PoolingOptions}.
* @see com.datastax.driver.core.HostDistance#LOCAL
* @see com.datastax.driver.core.PoolingOptions
* @see #newLocalHostDistancePoolingOptions()
*/
protected PoolingOptions configureLocalHostDistancePoolingOptions(PoolingOptions poolingOptions) {
return newLocalHostDistancePoolingOptions().configure(poolingOptions);
}
/**
* Configures the {@link HostDistance#REMOTE} connection settings on the given {@link PoolingOptions}.
*
* @param poolingOptions the {@link PoolingOptions} to configure.
* @return the given {@link PoolingOptions}.
* @see com.datastax.driver.core.HostDistance#REMOTE
* @see com.datastax.driver.core.PoolingOptions
* @see #newRemoteHostDistancePoolingOptions()
*/
protected PoolingOptions configureRemoteHostDistancePoolingOptions(PoolingOptions poolingOptions) {
return newRemoteHostDistancePoolingOptions().configure(poolingOptions);
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public PoolingOptions getObject() throws Exception {
return poolingOptions;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return (poolingOptions != null ? poolingOptions.getClass() : PoolingOptions.class);
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return true;
}
/**
* Sets the heart beat interval, after which a message is sent on an idle connection to make sure it's still alive.
*
* @param heartbeatIntervalSeconds interval in seconds between heartbeat messages to keep idle connections alive.
*/
public void setHeartbeatIntervalSeconds(int heartbeatIntervalSeconds) {
this.heartbeatIntervalSeconds = heartbeatIntervalSeconds;
}
/**
* Gets the heart beat interval, after which a message is sent on an idle connection to make sure it's still alive.
*
* @return the {@code heartbeatIntervalSeconds}.
*/
public int getHeartbeatIntervalSeconds() {
return heartbeatIntervalSeconds;
}
/**
* Sets the timeout before an idle connection is removed.
*
* @param idleTimeoutSeconds idle timeout in seconds before a connection is removed.
*/
public void setIdleTimeoutSeconds(int idleTimeoutSeconds) {
this.idleTimeoutSeconds = idleTimeoutSeconds;
}
/**
* Get the timeout before an idle connection is removed.
*
* @return the {@code idleTimeoutSeconds}.
*/
public int getIdleTimeoutSeconds() {
return idleTimeoutSeconds;
}
/**
* Sets the {@link Executor} to use for connection initialization.
*
* @param initializationExecutor {@link Executor} used to initialize the connection.
*/
public void setInitializationExecutor(Executor initializationExecutor) {
this.initializationExecutor = initializationExecutor;
}
/**
* Gets the {@link Executor} to use for connection initialization.
*
* @return the {@code initializationExecutor}.
*/
@Nullable
public Executor getInitializationExecutor() {
return initializationExecutor;
}
/**
* Sets the timeout when trying to acquire a connection from a host's pool.
*
* @param poolTimeoutMilliseconds timeout in milliseconds used to acquire a connection from the host's pool.
*/
public void setPoolTimeoutMilliseconds(int poolTimeoutMilliseconds) {
this.poolTimeoutMilliseconds = poolTimeoutMilliseconds;
}
/**
* Gets the timeout when trying to acquire a connection from a host's pool.
*
* @return the {@code poolTimeoutMilliseconds}.
*/
public int getPoolTimeoutMilliseconds() {
return poolTimeoutMilliseconds;
}
/**
* Sets the maximum number of requests that get enqueued if no connection is available.
*
* @param maxQueueSize maximum number of requests that get enqueued if no connection is available.
*/
public void setMaxQueueSize(Integer maxQueueSize) {
this.maxQueueSize = maxQueueSize;
}
/**
* Gets the maximum number of requests that get enqueued if no connection is available.
*
* @return the {@code maxQueueSize}.
*/
public Integer getMaxQueueSize() {
return maxQueueSize;
}
/**
* Sets the core number of connections per host for the {@link HostDistance#LOCAL} scope.
*
* @param localCoreConnections core number of local connections per host.
*/
public void setLocalCoreConnections(@Nullable Integer localCoreConnections) {
this.localCoreConnections = localCoreConnections;
}
/**
* Gets the core number of connections per host for the {@link HostDistance#LOCAL} scope.
*
* @return the {@code localCoreConnections).
*/
@Nullable
public Integer getLocalCoreConnections() {
return localCoreConnections;
}
/**
* Sets the maximum number of connections per host for the {@link HostDistance#LOCAL} scope.
*
* @param localMaxConnections max number of local connections per host.
*/
public void setLocalMaxConnections(@Nullable Integer localMaxConnections) {
this.localMaxConnections = localMaxConnections;
}
/**
* Gets the maximum number of connections per host for the {@link HostDistance#LOCAL} scope.
*
* @return the {@code localMaxConnections}.
*/
@Nullable
public Integer getLocalMaxConnections() {
return localMaxConnections;
}
/**
* Sets the maximum number of requests per connection for the {@link HostDistance#LOCAL} scope.
*
* @param localMaxSimultaneousRequests max number of requests for local connections.
*/
public void setLocalMaxSimultaneousRequests(@Nullable Integer localMaxSimultaneousRequests) {
this.localMaxSimultaneousRequests = localMaxSimultaneousRequests;
}
/**
* Gets the maximum number of requests per connection for the {@link HostDistance#LOCAL} scope.
*
* @return the {@code localMaxSimultaneousRequests}.
*/
@Nullable
public Integer getLocalMaxSimultaneousRequests() {
return localMaxSimultaneousRequests;
}
/**
* Sets the threshold that triggers the creation of a new connection to a host for the {@link HostDistance#LOCAL}
* scope.
*
* @param localMinSimultaneousRequests threshold triggering the creation of local connections to a host.
*/
public void setLocalMinSimultaneousRequests(@Nullable Integer localMinSimultaneousRequests) {
this.localMinSimultaneousRequests = localMinSimultaneousRequests;
}
/**
* Gets the threshold that triggers the creation of a new connection to a host for the {@link HostDistance#LOCAL}
* scope.
*
* @return the {@code localMinSimultaneousRequests}.
*/
@Nullable
public Integer getLocalMinSimultaneousRequests() {
return localMinSimultaneousRequests;
}
/**
* Sets the core number of connections per host for the {@link HostDistance#REMOTE} scope.
*
* @param remoteCoreConnections core number of remote connections per host.
*/
public void setRemoteCoreConnections(@Nullable Integer remoteCoreConnections) {
this.remoteCoreConnections = remoteCoreConnections;
}
/**
* Gets the core number of connections per host for the {@link HostDistance#REMOTE} scope.
*
* @return the {@code remoteCoreConnections).
*/
@Nullable
public Integer getRemoteCoreConnections() {
return remoteCoreConnections;
}
/**
* Sets the maximum number of connections per host for the {@link HostDistance#REMOTE} scope.
*
* @param remoteMaxConnections max number of remote connections per host.
*/
public void setRemoteMaxConnections(@Nullable Integer remoteMaxConnections) {
this.remoteMaxConnections = remoteMaxConnections;
}
/**
* Gets the maximum number of connections per host for the {@link HostDistance#REMOTE} scope.
*
* @return the {@code remoteMaxConnections}.
*/
@Nullable
public Integer getRemoteMaxConnections() {
return remoteMaxConnections;
}
/**
* Sets the maximum number of requests per connection for the {@link HostDistance#REMOTE} scope.
*
* @param remoteMaxSimultaneousRequests max number of requests for local connections.
*/
public void setRemoteMaxSimultaneousRequests(@Nullable Integer remoteMaxSimultaneousRequests) {
this.remoteMaxSimultaneousRequests = remoteMaxSimultaneousRequests;
}
/**
* Gets the maximum number of requests per connection for the {@link HostDistance#REMOTE} scope.
*
* @return the {@code remoteMaxSimultaneousRequests}.
*/
@Nullable
public Integer getRemoteMaxSimultaneousRequests() {
return remoteMaxSimultaneousRequests;
}
/**
* Sets the threshold that triggers the creation of a new connection to a host for the {@link HostDistance#REMOTE}
* scope.
*
* @param remoteMinSimultaneousRequests threshold triggering the creation of remote connections to a host.
*/
public void setRemoteMinSimultaneousRequests(@Nullable Integer remoteMinSimultaneousRequests) {
this.remoteMinSimultaneousRequests = remoteMinSimultaneousRequests;
}
/**
* Gets the threshold that triggers the creation of a new connection to a host for the {@link HostDistance#REMOTE}
* scope.
*
* @return the {@code remoteMinSimultaneousRequests}.
*/
@Nullable
public Integer getRemoteMinSimultaneousRequests() {
return remoteMinSimultaneousRequests;
}
/**
* The HostDistancePoolingOptions class models the {@link PoolingOptions} state and connection settings for a
* particular {@link HostDistance}.
*
* @see com.datastax.driver.core.HostDistance
* @see com.datastax.driver.core.PoolingOptions
*/
protected static abstract class HostDistancePoolingOptions {
private final @Nullable Integer coreConnectionsPerHost;
private final @Nullable Integer maxConnectionsPerHost;
private final @Nullable Integer maxRequestsPerConnection;
private final @Nullable Integer newConnectionThreshold;
/**
* Constructs an instance of {@link HostDistancePoolingOptions} with {@link PoolingOptions} connection settings
* specific to a particular {@link HostDistance}.
*
* @param coreConnectionsPerHost core number of connections per host.
* @param maxConnectionsPerHost maximum number of connections per host.
* @param maxRequestsPerConnection maximum number of requests per connection.
* @param newConnectionThreshold threshold that triggers the creation of a new connection to a host.
*/
protected HostDistancePoolingOptions(@Nullable Integer coreConnectionsPerHost,
@Nullable Integer maxConnectionsPerHost, @Nullable Integer maxRequestsPerConnection,
@Nullable Integer newConnectionThreshold) {
this.coreConnectionsPerHost = coreConnectionsPerHost;
this.maxConnectionsPerHost = maxConnectionsPerHost;
this.maxRequestsPerConnection = maxRequestsPerConnection;
this.newConnectionThreshold = newConnectionThreshold;
}
/**
* Returns the {@link HostDistance} used to configure the specific {@link PoolingOptions} connection settings.
*
* @return a {@link HostDistance} used to configure the specific {@link PoolingOptions} connection settings.
* @see com.datastax.driver.core.HostDistance
*/
protected abstract HostDistance getHostDistance();
/*
* (non-Javadoc)
* @see com.datastax.driver.core.PoolingOptions#setCoreConnectionsPerHost(HostDistance, int)
*/
void setCoreConnectionsPerHost(PoolingOptions poolingOptions) {
if (coreConnectionsPerHost != null) {
poolingOptions.setCoreConnectionsPerHost(getHostDistance(), coreConnectionsPerHost);
}
}
/**
* Returns the core number of connections per host.
*
* @return {@code coreConnectionsPerHost}.
* @see com.datastax.driver.core.PoolingOptions#getCoreConnectionsPerHost(HostDistance)
* @see #getHostDistance()
*/
@Nullable
protected Integer getCoreConnectionsPerHost() {
return coreConnectionsPerHost;
}
/*
* (non-Javadoc)
* @see com.datastax.driver.core.PoolingOptions#setMaxConnectionsPerHost(HostDistance, int)
*/
void setMaxConnectionsPerHost(PoolingOptions poolingOptions) {
if (maxConnectionsPerHost != null) {
poolingOptions.setMaxConnectionsPerHost(getHostDistance(), maxConnectionsPerHost);
}
}
/**
* Returns the maximum number of connections per host.
*
* @return {@code maxConnectionsPerHost}.
* @see com.datastax.driver.core.PoolingOptions#getMaxConnectionsPerHost(HostDistance)
* @see #getHostDistance()
*/
@Nullable
protected Integer getMaxConnectionsPerHost() {
return maxConnectionsPerHost;
}
/*
* (non-Javadoc)
* @see com.datastax.driver.core.PoolingOptions#setMaxRequestsPerConnection(HostDistance, int)
*/
void setMaxRequestsPerConnection(PoolingOptions poolingOptions) {
if (maxRequestsPerConnection != null) {
poolingOptions.setMaxRequestsPerConnection(getHostDistance(), maxRequestsPerConnection);
}
}
/**
* Returns the maximum number of requests per connection.
*
* @return {@code maxRequestsPerConnection}.
* @see com.datastax.driver.core.PoolingOptions#getMaxRequestsPerConnection(HostDistance)
* @see #getHostDistance()
*/
@Nullable
protected Integer getMaxRequestsPerConnection() {
return maxRequestsPerConnection;
}
/*
* If the new min is greater than the current max, set the current max to the new min first.
* This is enforced by the DSE Driver so you cannot set a new min/max together if either one falls outside
* of the default 25-100 range.
*
* @see com.datastax.driver.core.PoolingOptions#setNewConnectionThreshold(HostDistance, int)
*/
void setNewConnectionThreshold(PoolingOptions poolingOptions) {
if (newConnectionThreshold != null) {
int currentNewConnectionThreshold = poolingOptions.getNewConnectionThreshold(getHostDistance());
if (currentNewConnectionThreshold < newConnectionThreshold) {
poolingOptions.setNewConnectionThreshold(getHostDistance(), newConnectionThreshold);
}
}
}
/**
* Returns the threshold that triggers the creation of a new connection to a host.
*
* @return {@code newConnectionThreshold}.
* @see com.datastax.driver.core.PoolingOptions#getNewConnectionThreshold(HostDistance)
* @see #getHostDistance()
*/
@Nullable
protected Integer getNewConnectionThreshold() {
return newConnectionThreshold;
}
/*
* (non-Javadoc)
* @see com.datastax.driver.core.PoolingOptions
*/
PoolingOptions configure(PoolingOptions poolingOptions) {
// order is important here; max properties must be set first
setMaxConnectionsPerHost(poolingOptions);
setCoreConnectionsPerHost(poolingOptions);
setMaxRequestsPerConnection(poolingOptions);
setNewConnectionThreshold(poolingOptions);
return poolingOptions;
}
}
/*
* (non-Javadoc)
* @see HostDistancePoolingOptions
* @see com.datastax.driver.core.PoolingOptions
* @see com.datastax.driver.core.HostDistance#LOCAL
*/
static class LocalHostDistancePoolingOptions extends HostDistancePoolingOptions {
/**
* Constructs an instance of {@link LocalHostDistancePoolingOptions} initialized with {@link PoolingOptions} based
* on {@link HostDistance#LOCAL}.
*
* @param coreConnectionsPerHost core number of connections per host.
* @param maxConnectionsPerHost maximum number of connections per host.
* @param maxRequestsPerConnection maximum number of requests per connection.
* @param newConnectionThreshold threshold that triggers the creation of a new connection to a host.
*/
LocalHostDistancePoolingOptions(@Nullable Integer coreConnectionsPerHost, @Nullable Integer maxConnectionsPerHost,
@Nullable Integer maxRequestsPerConnection, @Nullable Integer newConnectionThreshold) {
super(coreConnectionsPerHost, maxConnectionsPerHost, maxRequestsPerConnection, newConnectionThreshold);
}
/**
* Returns {@link HostDistance#LOCAL} to configure local-based {@link PoolingOptions} connection settings.
*
* @return {@link HostDistance#LOCAL} to configure local-based {@link PoolingOptions} connection settings.
* @see com.datastax.driver.core.HostDistance#LOCAL
*/
@Override
protected HostDistance getHostDistance() {
return HostDistance.LOCAL;
}
}
/*
* (non-Javadoc)
* @see HostDistancePoolingOptions
* @see com.datastax.driver.core.PoolingOptions
* @see com.datastax.driver.core.HostDistance#REMOTE
*/
static class RemoteHostDistancePoolingOptions extends HostDistancePoolingOptions {
/**
* Constructs an instance of {@link RemoteHostDistancePoolingOptions} initialized with {@link PoolingOptions} based
* on {@link HostDistance#REMOTE}.
*
* @param coreConnectionsPerHost core number of connections per host.
* @param maxConnectionsPerHost maximum number of connections per host.
* @param maxRequestsPerConnection maximum number of requests per connection.
* @param newConnectionThreshold threshold that triggers the creation of a new connection to a host.
*/
RemoteHostDistancePoolingOptions(@Nullable Integer coreConnectionsPerHost, @Nullable Integer maxConnectionsPerHost,
@Nullable Integer maxRequestsPerConnection, @Nullable Integer newConnectionThreshold) {
super(coreConnectionsPerHost, maxConnectionsPerHost, maxRequestsPerConnection, newConnectionThreshold);
}
/**
* Returns {@link HostDistance#REMOTE} to configure remote-based {@link PoolingOptions} connection settings.
*
* @return {@link HostDistance#REMOTE} to configure remote-based {@link PoolingOptions} connection settings.
* @see com.datastax.driver.core.HostDistance#REMOTE
*/
@Override
protected HostDistance getHostDistance() {
return HostDistance.REMOTE;
}
}
}

View File

@@ -15,25 +15,25 @@
*/
package org.springframework.data.cassandra.config;
import com.datastax.driver.core.Cluster;
import com.datastax.oss.driver.api.core.session.SessionBuilder;
/**
* Configuration callback class to allow a user to apply additional configuration logic to the {@link Cluster.Builder}.
* Configuration callback class to allow a user to apply additional configuration logic to the {@link SessionBuilder}.
*
* @author John Blum
* @author Mark Paluch
* @since 1.5
* @see com.datastax.driver.core.Cluster
* @see com.datastax.oss.driver.api.core.CqlSession
*/
@FunctionalInterface
public interface ClusterBuilderConfigurer {
public interface SessionBuilderConfigurer {
/**
* Apply addition configuration to the {@link com.datastax.driver.core.Cluster.Builder}.
* Apply addition configuration to the {@link SessionBuilder}.
*
* @param clusterBuilder {@link Cluster.Builder} to configure.
* @return the argument to the {@code clusterBuilder} parameter.
* @see com.datastax.driver.core.Cluster.Builder
* @param sessionBuilder {@link SessionBuilder} to configure.
* @return the argument to the {@code sessionBuilder} parameter.
* @see SessionBuilder
*/
Cluster.Builder configure(Cluster.Builder clusterBuilder);
SessionBuilder configure(SessionBuilder sessionBuilder);
}

View File

@@ -29,7 +29,7 @@ import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.Session;
import com.datastax.oss.driver.api.core.CqlSession;
/**
* Factory to create and configure a Cassandra {@link SessionFactory} with support for executing CQL and initializing
@@ -46,7 +46,7 @@ public class SessionFactoryFactoryBean extends AbstractFactoryBean<SessionFactor
protected static final boolean DEFAULT_DROP_TABLES = false;
protected static final boolean DEFAULT_DROP_UNUSED_TABLES = false;
private Session session;
private CqlSession session;
private @Nullable KeyspacePopulator keyspacePopulator;
@@ -57,11 +57,11 @@ public class SessionFactoryFactoryBean extends AbstractFactoryBean<SessionFactor
private SchemaAction schemaAction = SchemaAction.NONE;
/**
* Set the {@link Session} to use.
* Set the {@link CqlSession} to use.
*
* @param session must not be {@literal null}.
*/
public void setSession(Session session) {
public void setSession(CqlSession session) {
Assert.notNull(session, "Session must not be null");
@@ -74,7 +74,7 @@ public class SessionFactoryFactoryBean extends AbstractFactoryBean<SessionFactor
* @param keyspacePopulator the {@link KeyspacePopulator} to use during initialization.
* @see #setKeyspaceCleaner
*/
public void setKeyspacePopulator(KeyspacePopulator keyspacePopulator) {
public void setKeyspacePopulator(@Nullable KeyspacePopulator keyspacePopulator) {
this.keyspacePopulator = keyspacePopulator;
}
@@ -82,10 +82,10 @@ public class SessionFactoryFactoryBean extends AbstractFactoryBean<SessionFactor
* Set the {@link KeyspacePopulator} to execute during the bean destruction phase, cleaning up the keyspace and
* leaving it in a known state for others.
*
* @param keyspaceCleaner the {@link KeyspacePopulator} to use during destruction.
* @param keyspaceCleaner the {@link KeyspacePopulator} to use during cleanup.
* @see #setKeyspacePopulator
*/
public void setKeyspaceCleaner(KeyspacePopulator keyspaceCleaner) {
public void setKeyspaceCleaner(@Nullable KeyspacePopulator keyspaceCleaner) {
this.keyspaceCleaner = keyspaceCleaner;
}

View File

@@ -1,172 +0,0 @@
/*
* Copyright 2013-2020 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
*
* https://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.config;
import java.util.Optional;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable;
import com.datastax.driver.core.SocketOptions;
/**
* Socket Options Factory Bean.
*
* @author Matthew T. Adams
* @author David Webb
* @author Mark Paluch
*/
@SuppressWarnings({ "unused", "WeakerAccess" })
public class SocketOptionsFactoryBean implements FactoryBean<SocketOptions>, InitializingBean {
private @Nullable Integer connectTimeoutMillis;
private @Nullable Boolean keepAlive;
private @Nullable Integer readTimeoutMillis;
private @Nullable Boolean reuseAddress;
private @Nullable Integer soLinger;
private @Nullable Boolean tcpNoDelay;
private @Nullable Integer receiveBufferSize;
private @Nullable Integer sendBufferSize;
private @Nullable SocketOptions socketOptions;
/* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public SocketOptions getObject() throws Exception {
return socketOptions;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return SocketOptions.class;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
this.socketOptions = new SocketOptions();
Optional.ofNullable(this.connectTimeoutMillis).ifPresent(this.socketOptions::setConnectTimeoutMillis);
Optional.ofNullable(this.readTimeoutMillis).ifPresent(this.socketOptions::setReadTimeoutMillis);
Optional.ofNullable(this.keepAlive).ifPresent(this.socketOptions::setKeepAlive);
Optional.ofNullable(this.reuseAddress).ifPresent(this.socketOptions::setReuseAddress);
Optional.ofNullable(this.soLinger).ifPresent(this.socketOptions::setSoLinger);
Optional.ofNullable(this.tcpNoDelay).ifPresent(this.socketOptions::setTcpNoDelay);
Optional.ofNullable(this.receiveBufferSize).ifPresent(this.socketOptions::setReceiveBufferSize);
Optional.ofNullable(this.sendBufferSize).ifPresent(this.socketOptions::setSendBufferSize);
}
@Nullable
public Boolean getKeepAlive() {
return keepAlive;
}
public void setKeepAlive(@Nullable Boolean keepAlive) {
this.keepAlive = keepAlive;
}
@Nullable
public Boolean getReuseAddress() {
return reuseAddress;
}
public void setReuseAddress(@Nullable Boolean reuseAddress) {
this.reuseAddress = reuseAddress;
}
@Nullable
public Integer getSoLinger() {
return soLinger;
}
public void setSoLinger(@Nullable Integer soLinger) {
this.soLinger = soLinger;
}
@Nullable
public Boolean getTcpNoDelay() {
return tcpNoDelay;
}
public void setTcpNoDelay(@Nullable Boolean tcpNoDelay) {
this.tcpNoDelay = tcpNoDelay;
}
@Nullable
public Integer getReceiveBufferSize() {
return receiveBufferSize;
}
public void setReceiveBufferSize(@Nullable Integer receiveBufferSize) {
this.receiveBufferSize = receiveBufferSize;
}
@Nullable
public Integer getSendBufferSize() {
return sendBufferSize;
}
public void setSendBufferSize(@Nullable Integer sendBufferSize) {
this.sendBufferSize = sendBufferSize;
}
/**
* @return Returns the connectTimeoutMillis.
*/
@Nullable
public Integer getConnectTimeoutMillis() {
return connectTimeoutMillis;
}
/**
* @param connectTimeoutMillis The connectTimeoutMillis to set.
*/
public void setConnectTimeoutMillis(@Nullable Integer connectTimeoutMillis) {
this.connectTimeoutMillis = connectTimeoutMillis;
}
/**
* @return Returns the readTimeoutMillis.
*/
@Nullable
public Integer getReadTimeoutMillis() {
return readTimeoutMillis;
}
/**
* @param readTimeoutMillis The readTimeoutMillis to set.
*/
public void setReadTimeoutMillis(@Nullable Integer readTimeoutMillis) {
this.readTimeoutMillis = readTimeoutMillis;
}
}

View File

@@ -29,7 +29,7 @@ import org.springframework.data.cassandra.core.query.Update;
import org.springframework.data.domain.Slice;
import org.springframework.util.concurrent.ListenableFuture;
import com.datastax.driver.core.Statement;
import com.datastax.oss.driver.api.core.cql.Statement;
/**
* Interface specifying a basic set of asynchronous Cassandra operations. Implemented by {@link AsyncCassandraTemplate}.
@@ -99,7 +99,7 @@ public interface AsyncCassandraOperations {
<T> ListenableFuture<T> selectOne(String cql, Class<T> entityClass) throws DataAccessException;
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
// -------------------------------------------------------------------------
/**
@@ -110,7 +110,7 @@ public interface AsyncCassandraOperations {
* @return the converted results
* @throws DataAccessException if there is any problem executing the query.
*/
<T> ListenableFuture<List<T>> select(Statement statement, Class<T> entityClass) throws DataAccessException;
<T> ListenableFuture<List<T>> select(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
/**
* Execute a {@code SELECT} query with paging and convert the result set to a {@link Slice} of entities. A sliced
@@ -122,7 +122,7 @@ public interface AsyncCassandraOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see CassandraPageRequest
*/
<T> ListenableFuture<Slice<T>> slice(Statement statement, Class<T> entityClass) throws DataAccessException;
<T> ListenableFuture<Slice<T>> slice(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
/**
* Execute a {@code SELECT} query and convert the resulting items notifying {@link Consumer} for each entity.
@@ -134,7 +134,7 @@ public interface AsyncCassandraOperations {
* @return the completion handle
* @throws DataAccessException if there is any problem executing the query.
*/
<T> ListenableFuture<Void> select(Statement statement, Consumer<T> entityConsumer, Class<T> entityClass)
<T> ListenableFuture<Void> select(Statement<?> statement, Consumer<T> entityConsumer, Class<T> entityClass)
throws DataAccessException;
/**
@@ -145,7 +145,7 @@ public interface AsyncCassandraOperations {
* @return the converted object or {@literal null}.
* @throws DataAccessException if there is any problem executing the query.
*/
<T> ListenableFuture<T> selectOne(Statement statement, Class<T> entityClass) throws DataAccessException;
<T> ListenableFuture<T> selectOne(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
// -------------------------------------------------------------------------
// Methods dealing with org.springframework.data.cassandra.core.query.Query

View File

@@ -17,9 +17,12 @@ package org.springframework.data.cassandra.core;
import lombok.Value;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
@@ -32,19 +35,17 @@ import org.springframework.data.cassandra.SessionFactory;
import org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.convert.QueryMapper;
import org.springframework.data.cassandra.core.convert.UpdateMapper;
import org.springframework.data.cassandra.core.cql.AsyncCqlOperations;
import org.springframework.data.cassandra.core.cql.AsyncCqlTemplate;
import org.springframework.data.cassandra.core.cql.AsyncSessionCallback;
import org.springframework.data.cassandra.core.cql.CassandraAccessor;
import org.springframework.data.cassandra.core.cql.CqlExceptionTranslator;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.cql.CqlProvider;
import org.springframework.data.cassandra.core.cql.GuavaListenableFutureAdapter;
import org.springframework.data.cassandra.core.cql.QueryOptions;
import org.springframework.data.cassandra.core.cql.WriteOptions;
import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory;
import org.springframework.data.cassandra.core.cql.util.CassandraFutureAdapter;
import org.springframework.data.cassandra.core.cql.util.StatementBuilder;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.event.AfterConvertEvent;
import org.springframework.data.cassandra.core.mapping.event.AfterDeleteEvent;
@@ -65,19 +66,20 @@ import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
import com.datastax.driver.core.RegularStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Truncate;
import com.datastax.driver.core.querybuilder.Update;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
import com.datastax.oss.driver.api.querybuilder.delete.Delete;
import com.datastax.oss.driver.api.querybuilder.insert.RegularInsert;
import com.datastax.oss.driver.api.querybuilder.select.Select;
import com.datastax.oss.driver.api.querybuilder.truncate.Truncate;
import com.datastax.oss.driver.api.querybuilder.update.Update;
/**
* Primary implementation of {@link AsyncCassandraOperations}. It simplifies the use of asynchronous Cassandra usage and
@@ -85,11 +87,11 @@ import com.datastax.driver.core.querybuilder.Update;
* initiating iteration over {@link ResultSet} and catching Cassandra exceptions and translating them to the generic,
* more informative exception hierarchy defined in the {@code org.springframework.dao} package.
* <p>
* Can be used within a service implementation via direct instantiation with a {@link Session} reference, or get
* Can be used within a service implementation via direct instantiation with a {@link CqlSession} reference, or get
* prepared in an application context and given to services as bean reference.
* <p>
* Note: The {@link Session} should always be configured as a bean in the application context, in the first case given
* to the service directly, in the second case to the prepared template.
* Note: The {@link CqlSession} should always be configured as a bean in the application context, in the first case
* given to the service directly, in the second case to the prepared template.
*
* @author Mark Paluch
* @author John Blum
@@ -116,28 +118,28 @@ public class AsyncCassandraTemplate
private @Nullable EntityCallbacks entityCallbacks;
/**
* Creates an instance of {@link AsyncCassandraTemplate} initialized with the given {@link Session} and a default
* Creates an instance of {@link AsyncCassandraTemplate} initialized with the given {@link CqlSession} and a default
* {@link MappingCassandraConverter}.
*
* @param session {@link Session} used to interact with Cassandra; must not be {@literal null}.
* @param session {@link CqlSession} used to interact with Cassandra; must not be {@literal null}.
* @see CassandraConverter
* @see Session
*/
public AsyncCassandraTemplate(Session session) {
public AsyncCassandraTemplate(CqlSession session) {
this(session, newConverter());
}
/**
* Creates an instance of {@link AsyncCassandraTemplate} initialized with the given {@link Session} and
* Creates an instance of {@link AsyncCassandraTemplate} initialized with the given {@link CqlSession} and
* {@link CassandraConverter}.
*
* @param session {@link Session} used to interact with Cassandra; must not be {@literal null}.
* @param session {@link CqlSession} used to interact with Cassandra; must not be {@literal null}.
* @param converter {@link CassandraConverter} used to convert between Java and Cassandra types; must not be
* {@literal null}.
* @see CassandraConverter
* @see Session
*/
public AsyncCassandraTemplate(Session session, CassandraConverter converter) {
public AsyncCassandraTemplate(CqlSession session, CassandraConverter converter) {
this(new DefaultSessionFactory(session), converter);
}
@@ -175,7 +177,7 @@ public class AsyncCassandraTemplate
this.entityOperations = new EntityOperations(converter.getMappingContext());
this.exceptionTranslator = asyncCqlTemplate.getExceptionTranslator();
this.projectionFactory = new SpelAwareProxyProjectionFactory();
this.statementFactory = new StatementFactory(new QueryMapper(converter), new UpdateMapper(converter));
this.statementFactory = new StatementFactory(converter);
}
/* (non-Javadoc)
@@ -257,7 +259,7 @@ public class AsyncCassandraTemplate
* Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements.
*
* @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements.
* @see org.springframework.data.cassandra.core.StatementFactory
* @see StatementFactory
* @since 2.1
*/
protected StatementFactory getStatementFactory() {
@@ -280,7 +282,7 @@ public class AsyncCassandraTemplate
Assert.hasText(cql, "CQL must not be empty");
return select(new SimpleStatement(cql), entityClass);
return select(SimpleStatement.newInstance(cql), entityClass);
}
/* (non-Javadoc)
@@ -294,7 +296,7 @@ public class AsyncCassandraTemplate
Assert.notNull(entityConsumer, "Entity Consumer must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
return select(new SimpleStatement(cql), entityConsumer, entityClass);
return select(SimpleStatement.newInstance(cql), entityConsumer, entityClass);
}
/* (non-Javadoc)
@@ -306,18 +308,18 @@ public class AsyncCassandraTemplate
Assert.hasText(cql, "CQL must not be empty");
Assert.notNull(entityClass, "Entity type must not be null");
return selectOne(new SimpleStatement(cql), entityClass);
return selectOne(SimpleStatement.newInstance(cql), entityClass);
}
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
// -------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#select(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#select(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> ListenableFuture<List<T>> select(Statement statement, Class<T> entityClass) {
public <T> ListenableFuture<List<T>> select(Statement<?> statement, Class<T> entityClass) {
Assert.notNull(statement, "Statement must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
@@ -328,10 +330,10 @@ public class AsyncCassandraTemplate
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#select(com.datastax.driver.core.Statement, java.util.function.Consumer, java.lang.Class)
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#select(com.datastax.oss.driver.api.core.cql.Statement, java.util.function.Consumer, java.lang.Class)
*/
@Override
public <T> ListenableFuture<Void> select(Statement statement, Consumer<T> entityConsumer, Class<T> entityClass)
public <T> ListenableFuture<Void> select(Statement<?> statement, Consumer<T> entityConsumer, Class<T> entityClass)
throws DataAccessException {
Assert.notNull(statement, "Statement must not be null");
@@ -346,29 +348,29 @@ public class AsyncCassandraTemplate
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#selectOne(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#selectOne(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> ListenableFuture<T> selectOne(Statement statement, Class<T> entityClass) {
public <T> ListenableFuture<T> selectOne(Statement<?> statement, Class<T> entityClass) {
return new MappingListenableFutureAdapter<>(select(statement, entityClass),
list -> list.isEmpty() ? null : list.get(0));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#slice(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#slice(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> ListenableFuture<Slice<T>> slice(Statement statement, Class<T> entityClass) {
public <T> ListenableFuture<Slice<T>> slice(Statement<?> statement, Class<T> entityClass) {
Assert.notNull(statement, "Statement must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
ListenableFuture<ResultSet> resultSet = getAsyncCqlOperations().queryForResultSet(statement);
ListenableFuture<AsyncResultSet> resultSet = getAsyncCqlOperations().queryForResultSet(statement);
Function<Row, T> mapper = getMapper(entityClass, entityClass, EntityQueryUtils.getTableName(statement));
return new MappingListenableFutureAdapter<>(resultSet,
rs -> EntityQueryUtils.readSlice(rs, (row, rowNum) -> mapper.apply(row), 0, getEffectiveFetchSize(statement)));
rs -> EntityQueryUtils.readSlice(rs, (row, rowNum) -> mapper.apply(row), 0, getEffectivePageSize(statement)));
}
// -------------------------------------------------------------------------
@@ -384,7 +386,7 @@ public class AsyncCassandraTemplate
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
return select(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)), entityClass);
return select(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)).build(), entityClass);
}
/* (non-Javadoc)
@@ -398,7 +400,7 @@ public class AsyncCassandraTemplate
Assert.notNull(entityConsumer, "Entity Consumer must not be empty");
Assert.notNull(entityClass, "Entity type must not be null");
return select(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)), entityConsumer,
return select(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)).build(), entityConsumer,
entityClass);
}
@@ -411,7 +413,8 @@ public class AsyncCassandraTemplate
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
return selectOne(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)), entityClass);
return selectOne(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)).build(),
entityClass);
}
/* (non-Javadoc)
@@ -423,7 +426,7 @@ public class AsyncCassandraTemplate
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
return slice(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)), entityClass);
return slice(getStatementFactory().select(query, getRequiredPersistentEntity(entityClass)).build(), entityClass);
}
/* (non-Javadoc)
@@ -438,7 +441,7 @@ public class AsyncCassandraTemplate
Assert.notNull(entityClass, "Entity type must not be null");
return getAsyncCqlOperations()
.execute(getStatementFactory().update(query, update, getRequiredPersistentEntity(entityClass)));
.execute(getStatementFactory().update(query, update, getRequiredPersistentEntity(entityClass)).build());
}
/* (non-Javadoc)
@@ -455,12 +458,13 @@ public class AsyncCassandraTemplate
private ListenableFuture<Boolean> doDelete(Query query, Class<?> entityClass, CqlIdentifier tableName) {
RegularStatement delete = getStatementFactory().delete(query, getRequiredPersistentEntity(entityClass), tableName);
StatementBuilder<Delete> builder = getStatementFactory().delete(query, getRequiredPersistentEntity(entityClass),
tableName);
SimpleStatement delete = builder.build();
maybeEmitEvent(new BeforeDeleteEvent<>(delete, entityClass, tableName));
ListenableFuture<Boolean> future = getAsyncCqlOperations()
.execute(getStatementFactory().delete(query, getRequiredPersistentEntity(entityClass)));
ListenableFuture<Boolean> future = getAsyncCqlOperations().execute(delete);
future.addCallback(success -> maybeEmitEvent(new AfterDeleteEvent<>(delete, entityClass, tableName)), e -> {});
@@ -479,9 +483,7 @@ public class AsyncCassandraTemplate
Assert.notNull(entityClass, "Entity type must not be null");
Select select = QueryBuilder.select().countAll().from(getTableName(entityClass).toCql());
return getAsyncCqlOperations().queryForObject(select, Long.class);
return doCount(Query.empty(), entityClass, getTableName(entityClass));
}
/* (non-Javadoc)
@@ -493,9 +495,17 @@ public class AsyncCassandraTemplate
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
RegularStatement count = getStatementFactory().count(query, getRequiredPersistentEntity(entityClass));
return doCount(query, entityClass, getTableName(entityClass));
}
ListenableFuture<Long> result = getAsyncCqlOperations().queryForObject(count, Long.class);
ListenableFuture<Long> doCount(Query query, Class<?> entityClass, CqlIdentifier tableName) {
StatementBuilder<com.datastax.oss.driver.api.querybuilder.select.Select> countStatement = getStatementFactory()
.count(query, getRequiredPersistentEntity(entityClass), tableName);
SimpleStatement statement = countStatement.build();
ListenableFuture<Long> result = getAsyncCqlOperations().queryForObject(statement, Long.class);
return new MappingListenableFutureAdapter<>(result, it -> it != null ? it : 0L);
}
@@ -511,12 +521,11 @@ public class AsyncCassandraTemplate
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
Select select = QueryBuilder.select().from(entity.getTableName().toCql());
StatementBuilder<com.datastax.oss.driver.api.querybuilder.select.Select> select = getStatementFactory()
.selectOneById(id, (source, sink) -> getConverter().write(source, sink, entity), entity.getTableName());
getConverter().write(id, select.where(), entity);
return new MappingListenableFutureAdapter<>(getAsyncCqlOperations().queryForResultSet(select),
resultSet -> resultSet.iterator().hasNext());
return new MappingListenableFutureAdapter<>(getAsyncCqlOperations().queryForResultSet(select.build()),
resultSet -> resultSet.remaining() > 0);
}
/* (non-Javadoc)
@@ -528,10 +537,11 @@ public class AsyncCassandraTemplate
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
RegularStatement select = getStatementFactory().select(query.limit(1), getRequiredPersistentEntity(entityClass));
StatementBuilder<com.datastax.oss.driver.api.querybuilder.select.Select> select = getStatementFactory()
.select(query.limit(1), getRequiredPersistentEntity(entityClass), getTableName(entityClass));
return new MappingListenableFutureAdapter<>(getAsyncCqlOperations().queryForResultSet(select),
resultSet -> resultSet.iterator().hasNext());
return new MappingListenableFutureAdapter<>(getAsyncCqlOperations().queryForResultSet(select.build()),
resultSet -> resultSet.remaining() > 0);
}
/* (non-Javadoc)
@@ -544,15 +554,13 @@ public class AsyncCassandraTemplate
Assert.notNull(entityClass, "Entity type must not be null");
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
Select select = QueryBuilder.select().all().from(entity.getTableName().toCql());
getConverter().write(id, select.where(), entity);
Function<Row, T> mapper = getMapper(entityClass, entityClass, entity.getTableName());
CqlIdentifier tableName = entity.getTableName();
StatementBuilder<Select> select = getStatementFactory().selectOneById(id,
(source, sink) -> getConverter().write(source, sink, entity), tableName);
Function<Row, T> mapper = getMapper(entityClass, entityClass, tableName);
return new MappingListenableFutureAdapter<>(
getAsyncCqlOperations().query(select, (row, rowNum) -> mapper.apply(row)),
getAsyncCqlOperations().query(select.build(), (row, rowNum) -> mapper.apply(row)),
it -> it.isEmpty() ? null : it.get(0));
}
@@ -584,14 +592,14 @@ public class AsyncCassandraTemplate
T entityToUse = source.isVersionedEntity() ? source.initializeVersionProperty() : entity;
Insert insert = EntityQueryUtils.createInsertQuery(tableName.toCql(), entityToUse, options, getConverter(),
persistentEntity);
StatementBuilder<RegularInsert> builder = getStatementFactory().insert(entityToUse, options, persistentEntity,
tableName);
return source.isVersionedEntity() ? doInsertVersioned(insert.ifNotExists(), entityToUse, source, tableName)
: doInsert(insert, entityToUse, source, tableName);
return source.isVersionedEntity() ? doInsertVersioned(builder.build(), entityToUse, source, tableName)
: doInsert(builder.build(), entityToUse, source, tableName);
}
private <T> ListenableFuture<EntityWriteResult<T>> doInsertVersioned(Insert insert, T entity,
private <T> ListenableFuture<EntityWriteResult<T>> doInsertVersioned(Statement<?> insert, T entity,
AdaptibleEntity<T> source, CqlIdentifier tableName) {
return executeSave(entity, tableName, insert, result -> {
@@ -605,7 +613,7 @@ public class AsyncCassandraTemplate
}
@SuppressWarnings("unused")
private <T> ListenableFuture<EntityWriteResult<T>> doInsert(Insert insert, T entity, AdaptibleEntity<T> source,
private <T> ListenableFuture<EntityWriteResult<T>> doInsert(Statement<?> insert, T entity, AdaptibleEntity<T> source,
CqlIdentifier tableName) {
return executeSave(entity, tableName, insert);
@@ -645,9 +653,10 @@ public class AsyncCassandraTemplate
Number previousVersion = source.getVersion();
T toSave = source.incrementVersion();
Update update = getStatementFactory().update(toSave, options, getConverter(), persistentEntity, tableName);
StatementBuilder<Update> update = getStatementFactory().update(toSave, options, persistentEntity, tableName);
source.appendVersionCondition(update, previousVersion);
return executeSave(toSave, tableName, source.appendVersionCondition(update, previousVersion), result -> {
return executeSave(toSave, tableName, update.build(), result -> {
if (!result.wasApplied()) {
throw new OptimisticLockingFailureException(
@@ -660,9 +669,9 @@ public class AsyncCassandraTemplate
private <T> ListenableFuture<EntityWriteResult<T>> doUpdate(T entity, UpdateOptions options, CqlIdentifier tableName,
CassandraPersistentEntity<?> persistentEntity) {
Update update = getStatementFactory().update(entity, options, getConverter(), persistentEntity, tableName);
StatementBuilder<Update> update = getStatementFactory().update(entity, options, persistentEntity, tableName);
return executeSave(entity, tableName, update);
return executeSave(entity, tableName, update.build());
}
/* (non-Javadoc)
@@ -686,16 +695,17 @@ public class AsyncCassandraTemplate
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
CqlIdentifier tableName = persistentEntity.getTableName();
Delete delete = getStatementFactory().delete(entity, options, getConverter(), persistentEntity, tableName);
return source.isVersionedEntity() ? doDeleteVersioned(delete, entity, source, tableName)
: doDelete(delete, entity, tableName);
return source.isVersionedEntity() ? doDeleteVersioned(entity, options, source, tableName)
: doDelete(entity, options, tableName);
}
private ListenableFuture<WriteResult> doDeleteVersioned(Delete delete, Object entity, AdaptibleEntity<Object> source,
CqlIdentifier tableName) {
private ListenableFuture<WriteResult> doDeleteVersioned(Object entity, QueryOptions options,
AdaptibleEntity<Object> source, CqlIdentifier tableName) {
return executeDelete(entity, tableName, source.appendVersionCondition(delete), result -> {
StatementBuilder<Delete> delete = getStatementFactory().delete(entity, options, getConverter(), tableName);
source.appendVersionCondition(delete);
return executeDelete(entity, tableName, delete.build(), result -> {
if (!result.wasApplied()) {
throw new OptimisticLockingFailureException(
@@ -705,8 +715,11 @@ public class AsyncCassandraTemplate
});
}
private ListenableFuture<WriteResult> doDelete(Delete delete, Object entity, CqlIdentifier tableName) {
return executeDelete(entity, tableName, delete, result -> {});
private ListenableFuture<WriteResult> doDelete(Object entity, QueryOptions options, CqlIdentifier tableName) {
StatementBuilder<Delete> delete = getStatementFactory().delete(entity, options, getConverter(), tableName);
return executeDelete(entity, tableName, delete.build(), result -> {});
}
/* (non-Javadoc)
@@ -719,10 +732,11 @@ public class AsyncCassandraTemplate
Assert.notNull(entityClass, "Entity type must not be null");
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
CqlIdentifier tableName = entity.getTableName();
Delete delete = QueryBuilder.delete().from(tableName.toCql());
getConverter().write(id, delete.where(), entity);
StatementBuilder<Delete> builder = getStatementFactory().deleteById(id,
(source, sink) -> getConverter().write(source, sink, entity), tableName);
SimpleStatement delete = builder.build();
maybeEmitEvent(new BeforeDeleteEvent<>(delete, entityClass, tableName));
@@ -741,12 +755,13 @@ public class AsyncCassandraTemplate
Assert.notNull(entityClass, "Entity type must not be null");
CqlIdentifier tableName = getTableName(entityClass);
Truncate truncate = QueryBuilder.truncate(tableName.toCql());
Truncate truncate = QueryBuilder.truncate(tableName);
SimpleStatement statement = truncate.build();
maybeEmitEvent(new BeforeDeleteEvent<>(truncate, entityClass, tableName));
maybeEmitEvent(new BeforeDeleteEvent<>(statement, entityClass, tableName));
ListenableFuture<Boolean> future = getAsyncCqlOperations().execute(truncate);
future.addCallback(success -> maybeEmitEvent(new AfterDeleteEvent<>(truncate, entityClass, tableName)), e -> {});
ListenableFuture<Boolean> future = getAsyncCqlOperations().execute(statement);
future.addCallback(success -> maybeEmitEvent(new AfterDeleteEvent<>(statement, entityClass, tableName)), e -> {});
return new MappingListenableFutureAdapter<>(future, aBoolean -> null);
}
@@ -756,22 +771,24 @@ public class AsyncCassandraTemplate
// -------------------------------------------------------------------------
private <T> ListenableFuture<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName,
Statement statement) {
Statement<?> statement) {
return executeSave(entity, tableName, statement, ignore -> {});
}
private <T> ListenableFuture<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName, Statement statement,
Consumer<WriteResult> beforeAfterSaveEvent) {
private <T> ListenableFuture<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName,
Statement<?> statement, Consumer<WriteResult> beforeAfterSaveEvent) {
maybeEmitEvent(new BeforeSaveEvent<>(entity, tableName, statement));
T entityToSave = maybeCallBeforeSave(entity, tableName, statement);
ListenableFuture<ResultSet> result = getAsyncCqlOperations().execute(new AsyncStatementCallback(statement));
ListenableFuture<AsyncResultSet> result = getAsyncCqlOperations().execute(new AsyncStatementCallback(statement));
return new MappingListenableFutureAdapter<>(result, resultSet -> {
EntityWriteResult<T> writeResult = EntityWriteResult.of(resultSet, entityToSave);
EntityWriteResult<T> writeResult = new EntityWriteResult<>(
Collections.singletonList(resultSet.getExecutionInfo()), resultSet.wasApplied(), getFirstPage(resultSet),
entityToSave);
beforeAfterSaveEvent.accept(writeResult);
@@ -781,16 +798,17 @@ public class AsyncCassandraTemplate
});
}
private ListenableFuture<WriteResult> executeDelete(Object entity, CqlIdentifier tableName, Statement statement,
private ListenableFuture<WriteResult> executeDelete(Object entity, CqlIdentifier tableName, Statement<?> statement,
Consumer<WriteResult> resultConsumer) {
maybeEmitEvent(new BeforeDeleteEvent<>(statement, entity.getClass(), tableName));
ListenableFuture<ResultSet> result = getAsyncCqlOperations().execute(new AsyncStatementCallback(statement));
ListenableFuture<AsyncResultSet> result = getAsyncCqlOperations().execute(new AsyncStatementCallback(statement));
return new MappingListenableFutureAdapter<>(result, resultSet -> {
WriteResult writeResult = WriteResult.of(resultSet);
WriteResult writeResult = new WriteResult(Collections.singletonList(resultSet.getExecutionInfo()),
resultSet.wasApplied(), getFirstPage(resultSet));
resultConsumer.accept(writeResult);
@@ -800,14 +818,18 @@ public class AsyncCassandraTemplate
});
}
private int getConfiguredFetchSize(Session session) {
return session.getCluster().getConfiguration().getQueryOptions().getFetchSize();
private static List<Row> getFirstPage(AsyncResultSet resultSet) {
return StreamSupport.stream(resultSet.currentPage().spliterator(), false).collect(Collectors.toList());
}
private int getEffectiveFetchSize(Statement statement) {
private static int getConfiguredPageSize(CqlSession session) {
return session.getContext().getConfig().getDefaultProfile().getInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 5000);
}
if (statement.getFetchSize() > 0) {
return statement.getFetchSize();
private int getEffectivePageSize(Statement<?> statement) {
if (statement.getPageSize() > 0) {
return statement.getPageSize();
}
if (getAsyncCqlOperations() instanceof CassandraAccessor) {
@@ -820,7 +842,7 @@ public class AsyncCassandraTemplate
}
return getAsyncCqlOperations()
.execute((AsyncSessionCallback<Integer>) session -> AsyncResult.forValue(getConfiguredFetchSize(session)))
.execute((AsyncSessionCallback<Integer>) session -> AsyncResult.forValue(getConfiguredPageSize(session)))
.completable().join();
}
@@ -831,7 +853,7 @@ public class AsyncCassandraTemplate
return row -> {
maybeEmitEvent(new AfterLoadEvent(row, targetType, tableName));
maybeEmitEvent(new AfterLoadEvent<>(row, targetType, tableName));
Object source = getConverter().read(typeToRead, row);
@@ -874,7 +896,7 @@ public class AsyncCassandraTemplate
return object;
}
protected <T> T maybeCallBeforeSave(T object, CqlIdentifier tableName, Statement statement) {
protected <T> T maybeCallBeforeSave(T object, CqlIdentifier tableName, Statement<?> statement) {
if (null != entityCallbacks) {
return (T) entityCallbacks.callback(BeforeSaveCallback.class, object, tableName, statement);
@@ -903,11 +925,11 @@ public class AsyncCassandraTemplate
}
@Value
class AsyncStatementCallback implements AsyncSessionCallback<ResultSet>, CqlProvider {
class AsyncStatementCallback implements AsyncSessionCallback<AsyncResultSet>, CqlProvider {
@lombok.NonNull Statement statement;
@lombok.NonNull Statement<?> statement;
AsyncStatementCallback(Statement statement) {
AsyncStatementCallback(Statement<?> statement) {
this.statement = statement;
}
@@ -915,10 +937,10 @@ public class AsyncCassandraTemplate
* @see org.springframework.data.cassandra.core.cql.AsyncSessionCallback#doInSession(com.datastax.driver.core.Session)
*/
@Override
public ListenableFuture<ResultSet> doInSession(Session session) throws DriverException, DataAccessException {
return new GuavaListenableFutureAdapter<>(session.executeAsync(this.statement),
e -> e instanceof DriverException
? exceptionTranslator.translate("AsyncStatementCallback", getCql(), (DriverException) e)
public ListenableFuture<AsyncResultSet> doInSession(CqlSession session)
throws DriverException, DataAccessException {
return new CassandraFutureAdapter<>(session.executeAsync(this.statement),
e -> e instanceof DriverException ? exceptionTranslator.translate("AsyncStatementCallback", getCql(), e)
: exceptionTranslator.translateExceptionIfPossible(e));
}

View File

@@ -18,11 +18,11 @@ package org.springframework.data.cassandra.core;
import java.util.Map;
import java.util.Optional;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.mapping.Table;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.TableMetadata;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata;
import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
/**
* Operations for managing a Cassandra keyspace.
@@ -96,6 +96,17 @@ public interface CassandraAdminOperations extends CassandraOperations {
* @param tableName must not be {@literal null}.
* @return the {@link TableMetadata} or {@literal null}.
*/
Optional<TableMetadata> getTableMetadata(String keyspace, CqlIdentifier tableName);
default Optional<TableMetadata> getTableMetadata(String keyspace, CqlIdentifier tableName) {
return getTableMetadata(CqlIdentifier.fromCql(keyspace), tableName);
}
/**
* Lookup {@link TableMetadata}.
*
* @param keyspace must not be {@literal null}.
* @param tableName must not be {@literal null}.
* @return the {@link TableMetadata} or {@literal null}.
* @since 3.0
*/
Optional<TableMetadata> getTableMetadata(CqlIdentifier keyspace, CqlIdentifier tableName);
}

View File

@@ -20,7 +20,6 @@ import java.util.Optional;
import org.springframework.data.cassandra.SessionFactory;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.cql.CqlOperations;
import org.springframework.data.cassandra.core.cql.SessionCallback;
import org.springframework.data.cassandra.core.cql.generator.CreateTableCqlGenerator;
@@ -32,9 +31,10 @@ import org.springframework.data.cassandra.core.cql.keyspace.DropUserTypeSpecific
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.util.Assert;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.TableMetadata;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata;
import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
/**
* Default implementation of {@link CassandraAdminOperations}.
@@ -54,7 +54,7 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
* @param session must not be {@literal null}.
* @since 2.2
*/
public CassandraAdminTemplate(Session session) {
public CassandraAdminTemplate(CqlSession session) {
super(session);
}
@@ -64,7 +64,7 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
* @param session must not be {@literal null}.
* @param converter must not be {@literal null}.
*/
public CassandraAdminTemplate(Session session, CassandraConverter converter) {
public CassandraAdminTemplate(CqlSession session, CassandraConverter converter) {
super(session, converter);
}
@@ -145,16 +145,18 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraAdminOperations#getTableMetadata(java.lang.String, org.springframework.data.cassandra.core.cql.CqlIdentifier)
* @see org.springframework.data.cassandra.core.CassandraAdminOperations#getTableMetadata(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.CqlIdentifier)
*/
@Override
public Optional<TableMetadata> getTableMetadata(String keyspace, CqlIdentifier tableName) {
public Optional<TableMetadata> getTableMetadata(CqlIdentifier keyspace, CqlIdentifier tableName) {
Assert.hasText(keyspace, "Keyspace name must not be empty");
Assert.notNull(keyspace, "Keyspace name must not be null");
Assert.notNull(tableName, "Table name must not be null");
return Optional.ofNullable(getCqlOperations().execute((SessionCallback<TableMetadata>) session -> session
.getCluster().getMetadata().getKeyspace(keyspace).getTable(tableName.toCql())));
// noinspection ConstantConditions
return getCqlOperations().execute((SessionCallback<Optional<TableMetadata>>) session -> {
return session.getMetadata().getKeyspace(keyspace).flatMap(it -> it.getTable(tableName));
});
}
/* (non-Javadoc)
@@ -166,12 +168,9 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
// noinspection ConstantConditions
return getCqlOperations().execute((SessionCallback<KeyspaceMetadata>) session -> {
KeyspaceMetadata keyspaceMetadata = session.getCluster().getMetadata().getKeyspace(session.getLoggedKeyspace());
Assert.state(keyspaceMetadata != null,
String.format("Metadata for keyspace [%s] not available", session.getLoggedKeyspace()));
return keyspaceMetadata;
return session.getKeyspace().flatMap(it -> session.getMetadata().getKeyspace(it)).orElseThrow(() -> {
return new IllegalStateException("Metadata for keyspace not available");
});
});
}
}

View File

@@ -27,12 +27,10 @@ import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Batch;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Update;
import com.datastax.oss.driver.api.core.cql.BatchStatement;
import com.datastax.oss.driver.api.core.cql.BatchStatementBuilder;
import com.datastax.oss.driver.api.core.cql.BatchType;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
/**
* Default implementation for {@link CassandraBatchOperations}.
@@ -46,7 +44,7 @@ class CassandraBatchTemplate implements CassandraBatchOperations {
private final AtomicBoolean executed = new AtomicBoolean();
private final Batch batch = QueryBuilder.batch();
private final BatchStatementBuilder batch = BatchStatement.builder(BatchType.LOGGED);
private final CassandraConverter converter;
@@ -111,7 +109,7 @@ class CassandraBatchTemplate implements CassandraBatchOperations {
public WriteResult execute() {
if (this.executed.compareAndSet(false, true)) {
return WriteResult.of(this.operations.getCqlOperations().queryForResultSet(batch));
return WriteResult.of(this.operations.getCqlOperations().queryForResultSet(batch.build()));
}
throw new IllegalStateException("This Cassandra Batch was already executed");
@@ -125,7 +123,7 @@ class CassandraBatchTemplate implements CassandraBatchOperations {
assertNotExecuted();
this.batch.using(QueryBuilder.timestamp(timestamp));
this.batch.setQueryTimestamp(timestamp);
return this;
}
@@ -169,10 +167,10 @@ class CassandraBatchTemplate implements CassandraBatchOperations {
BasicCassandraPersistentEntity<?> persistentEntity = mappingContext
.getRequiredPersistentEntity(entity.getClass());
Insert insertQuery = EntityQueryUtils.createInsertQuery(persistentEntity.getTableName().toCql(), entity, options,
getConverter(), persistentEntity);
SimpleStatement insertQuery = getStatementFactory()
.insert(entity, options, persistentEntity, persistentEntity.getTableName()).build();
this.batch.add(insertQuery);
this.batch.addStatement(insertQuery);
}
return this;
@@ -214,10 +212,10 @@ class CassandraBatchTemplate implements CassandraBatchOperations {
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
Update update = getStatementFactory().update(entity, options, getConverter(), persistentEntity,
persistentEntity.getTableName());
SimpleStatement update = getStatementFactory()
.update(entity, options, persistentEntity, persistentEntity.getTableName()).build();
this.batch.add(update);
this.batch.addStatement(update);
}
return this;
@@ -259,10 +257,10 @@ class CassandraBatchTemplate implements CassandraBatchOperations {
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
Delete delete = getStatementFactory().delete(entity, options, this.converter, persistentEntity,
persistentEntity.getTableName());
SimpleStatement delete = getStatementFactory()
.delete(entity, options, this.getConverter(), persistentEntity.getTableName()).build();
this.batch.add(delete);
this.batch.addStatement(delete);
}
return this;

View File

@@ -21,7 +21,6 @@ import java.util.stream.Stream;
import org.springframework.dao.DataAccessException;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.cql.CqlOperations;
import org.springframework.data.cassandra.core.cql.QueryOptions;
import org.springframework.data.cassandra.core.cql.WriteOptions;
@@ -31,7 +30,8 @@ import org.springframework.data.cassandra.core.query.Update;
import org.springframework.data.domain.Slice;
import org.springframework.lang.Nullable;
import com.datastax.driver.core.Statement;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.cql.Statement;
/**
* Interface specifying a basic set of Cassandra operations. Implemented by {@link CassandraTemplate}. Not often used
@@ -120,7 +120,7 @@ public interface CassandraOperations extends FluentCassandraOperations {
<T> T selectOne(String cql, Class<T> entityClass) throws DataAccessException;
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
// -------------------------------------------------------------------------
/**
@@ -131,7 +131,7 @@ public interface CassandraOperations extends FluentCassandraOperations {
* @return the converted results
* @throws DataAccessException if there is any problem executing the query.
*/
<T> List<T> select(Statement statement, Class<T> entityClass) throws DataAccessException;
<T> List<T> select(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
/**
* Execute a {@code SELECT} query with paging and convert the result set to a {@link Slice} of entities. A sliced
@@ -143,7 +143,7 @@ public interface CassandraOperations extends FluentCassandraOperations {
* @throws DataAccessException if there is any problem executing the query.
* @since 2.0
*/
<T> Slice<T> slice(Statement statement, Class<T> entityClass) throws DataAccessException;
<T> Slice<T> slice(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
/**
* Execute a {@code SELECT} query and convert the resulting items to a {@link Iterator} of entities.
@@ -157,7 +157,7 @@ public interface CassandraOperations extends FluentCassandraOperations {
* @throws DataAccessException if there is any problem executing the query.
* @since 1.5
*/
<T> Stream<T> stream(Statement statement, Class<T> entityClass) throws DataAccessException;
<T> Stream<T> stream(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
/**
* Execute a {@code SELECT} query and convert the resulting item to an entity.
@@ -168,7 +168,7 @@ public interface CassandraOperations extends FluentCassandraOperations {
* @throws DataAccessException if there is any problem executing the query.
*/
@Nullable
<T> T selectOne(Statement statement, Class<T> entityClass) throws DataAccessException;
<T> T selectOne(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
// -------------------------------------------------------------------------
// Methods dealing with org.springframework.data.cassandra.core.query.Query

View File

@@ -25,7 +25,6 @@ import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.cql.generator.CreateIndexCqlGenerator;
import org.springframework.data.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.data.cassandra.core.cql.generator.CreateUserTypeCqlGenerator;
@@ -39,6 +38,8 @@ import org.springframework.data.cassandra.core.mapping.CassandraPersistentProper
import org.springframework.data.util.Streamable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Schema creation support for Cassandra based on {@link CassandraMappingContext} and {@link CassandraPersistentEntity}.
* This class generates CQL to create user types (UDT) and tables.

View File

@@ -25,18 +25,20 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import com.datastax.driver.core.AbstractTableMetadata;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.TupleType;
import com.datastax.driver.core.UserType;
import com.datastax.driver.core.UserType.Field;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.ListType;
import com.datastax.oss.driver.api.core.type.MapType;
import com.datastax.oss.driver.api.core.type.SetType;
import com.datastax.oss.driver.api.core.type.TupleType;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
/**
* Schema drop support for Cassandra based on {@link CassandraMappingContext} and {@link CassandraPersistentEntity}.
@@ -80,9 +82,10 @@ public class CassandraPersistentEntitySchemaDropper {
this.cassandraAdminOperations.getKeyspaceMetadata() //
.getTables() //
.values() //
.stream() //
.map(AbstractTableMetadata::getName) //
.map(CqlIdentifier::of).filter(table -> dropUnused || this.mappingContext.usesTable(table)) //
.map(RelationMetadata::getName) //
.filter(table -> dropUnused || this.mappingContext.usesTable(table)) //
.forEach(this.cassandraAdminOperations::dropTable);
}
@@ -98,7 +101,8 @@ public class CassandraPersistentEntitySchemaDropper {
Set<CqlIdentifier> canRecreate = this.mappingContext.getUserDefinedTypeEntities().stream()
.map(CassandraPersistentEntity::getTableName).collect(Collectors.toSet());
Collection<UserType> userTypes = this.cassandraAdminOperations.getKeyspaceMetadata().getUserTypes();
Collection<UserDefinedType> userTypes = this.cassandraAdminOperations.getKeyspaceMetadata().getUserDefinedTypes()
.values();
getUserTypesToDrop(userTypes) //
.stream() //
@@ -112,7 +116,7 @@ public class CassandraPersistentEntitySchemaDropper {
*
* @return {@link List} of {@link CqlIdentifier}.
*/
private List<CqlIdentifier> getUserTypesToDrop(Collection<UserType> knownUserTypes) {
private List<CqlIdentifier> getUserTypesToDrop(Collection<UserDefinedType> knownUserTypes) {
List<CqlIdentifier> toDrop = new ArrayList<>();
@@ -124,9 +128,7 @@ public class CassandraPersistentEntitySchemaDropper {
Set<CqlIdentifier> globalSeen = new LinkedHashSet<>();
knownUserTypes.forEach(userType -> {
CqlIdentifier typeName = CqlIdentifier.of(userType.getTypeName());
toDrop.addAll(dependencyGraph.getDropOrder(typeName, globalSeen::add));
toDrop.addAll(dependencyGraph.getDropOrder(userType.getName(), globalSeen::add));
});
return toDrop;
@@ -149,7 +151,7 @@ public class CassandraPersistentEntitySchemaDropper {
*
* @param userType must not be {@literal null.}
*/
void addUserType(UserType userType) {
void addUserType(UserDefinedType userType) {
Set<CqlIdentifier> seen = new LinkedHashSet<>();
visitTypes(userType, seen::add);
@@ -170,45 +172,68 @@ public class CassandraPersistentEntitySchemaDropper {
* @param userType
* @param typeFilter
*/
private void visitTypes(UserType userType, Predicate<CqlIdentifier> typeFilter) {
private void visitTypes(UserDefinedType userType, Predicate<CqlIdentifier> typeFilter) {
CqlIdentifier typeName = CqlIdentifier.of(userType.getTypeName());
CqlIdentifier typeName = userType.getName();
if (!typeFilter.test(typeName)) {
return;
}
for (Field field : userType) {
for (DataType fieldType : userType.getFieldTypes()) {
if (field.getType() instanceof UserType) {
if (fieldType instanceof UserDefinedType) {
addDependency((UserType) field.getType(), typeName, typeFilter);
addDependency((UserDefinedType) fieldType, typeName, typeFilter);
return;
}
doWithTypeArguments(field.getType(), it -> {
doWithTypeArguments(fieldType, it -> {
if (it instanceof UserType) {
addDependency((UserType) it, typeName, typeFilter);
if (it instanceof UserDefinedType) {
addDependency((UserDefinedType) it, typeName, typeFilter);
}
});
}
}
private void addDependency(UserType userType, CqlIdentifier requiredBy, Predicate<CqlIdentifier> typeFilter) {
private void addDependency(UserDefinedType userType, CqlIdentifier requiredBy,
Predicate<CqlIdentifier> typeFilter) {
dependencies.add(CqlIdentifier.of(userType.getTypeName()), requiredBy);
dependencies.add(userType.getName(), requiredBy);
visitTypes(userType, typeFilter);
}
private static void doWithTypeArguments(DataType type, Consumer<DataType> callback) {
type.getTypeArguments().forEach(nested -> {
callback.accept(nested);
doWithTypeArguments(nested, callback);
});
if (type instanceof MapType) {
MapType mapType = (MapType) type;
callback.accept(mapType.getKeyType());
doWithTypeArguments(mapType.getKeyType(), callback);
callback.accept(mapType.getValueType());
doWithTypeArguments(mapType.getValueType(), callback);
}
if (type instanceof ListType) {
ListType listType = (ListType) type;
callback.accept(listType.getElementType());
doWithTypeArguments(listType.getElementType(), callback);
}
if (type instanceof SetType) {
SetType setType = (SetType) type;
callback.accept(setType.getElementType());
doWithTypeArguments(setType.getElementType(), callback);
}
if (type instanceof TupleType) {

View File

@@ -37,7 +37,6 @@ import org.springframework.data.cassandra.core.convert.MappingCassandraConverter
import org.springframework.data.cassandra.core.convert.QueryMapper;
import org.springframework.data.cassandra.core.convert.UpdateMapper;
import org.springframework.data.cassandra.core.cql.CassandraAccessor;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.cql.CqlOperations;
import org.springframework.data.cassandra.core.cql.CqlProvider;
import org.springframework.data.cassandra.core.cql.CqlTemplate;
@@ -45,6 +44,7 @@ import org.springframework.data.cassandra.core.cql.QueryOptions;
import org.springframework.data.cassandra.core.cql.SessionCallback;
import org.springframework.data.cassandra.core.cql.WriteOptions;
import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory;
import org.springframework.data.cassandra.core.cql.util.StatementBuilder;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.core.mapping.event.AfterConvertEvent;
@@ -66,19 +66,20 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.RegularStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Truncate;
import com.datastax.driver.core.querybuilder.Update;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
import com.datastax.oss.driver.api.querybuilder.delete.Delete;
import com.datastax.oss.driver.api.querybuilder.insert.RegularInsert;
import com.datastax.oss.driver.api.querybuilder.select.Select;
import com.datastax.oss.driver.api.querybuilder.truncate.Truncate;
import com.datastax.oss.driver.api.querybuilder.update.Update;
/**
* Primary implementation of {@link CassandraOperations}. It simplifies the use of Cassandra usage and helps to avoid
@@ -86,11 +87,11 @@ import com.datastax.driver.core.querybuilder.Update;
* over {@link ResultSet} and catching Cassandra exceptions and translating them to the generic, more informative
* exception hierarchy defined in the {@code org.springframework.dao} package.
* <p>
* Can be used within a service implementation via direct instantiation with a {@link Session} reference, or get
* Can be used within a service implementation via direct instantiation with a {@link CqlSession} reference, or get
* prepared in an application context and given to services as bean reference.
* <p>
* Note: The {@link Session} should always be configured as a bean in the application context, in the first case given
* to the service directly, in the second case to the prepared template.
* Note: The {@link CqlSession} should always be configured as a bean in the application context, in the first case
* given to the service directly, in the second case to the prepared template.
*
* @author Mark Paluch
* @author John Blum
@@ -117,28 +118,28 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
private final StatementFactory statementFactory;
/**
* Creates an instance of {@link CassandraTemplate} initialized with the given {@link Session} and a default
* Creates an instance of {@link CassandraTemplate} initialized with the given {@link CqlSession} and a default
* {@link MappingCassandraConverter}.
*
* @param session {@link Session} used to interact with Cassandra; must not be {@literal null}.
* @param session {@link CqlSession} used to interact with Cassandra; must not be {@literal null}.
* @see CassandraConverter
* @see Session
*/
public CassandraTemplate(Session session) {
public CassandraTemplate(CqlSession session) {
this(session, newConverter());
}
/**
* Creates an instance of {@link CassandraTemplate} initialized with the given {@link Session} and
* Creates an instance of {@link CassandraTemplate} initialized with the given {@link CqlSession} and
* {@link CassandraConverter}.
*
* @param session {@link Session} used to interact with Cassandra; must not be {@literal null}.
* @param session {@link CqlSession} used to interact with Cassandra; must not be {@literal null}.
* @param converter {@link CassandraConverter} used to convert between Java and Cassandra types; must not be
* {@literal null}.
* @see CassandraConverter
* @see Session
*/
public CassandraTemplate(Session session, CassandraConverter converter) {
public CassandraTemplate(CqlSession session, CassandraConverter converter) {
this(new DefaultSessionFactory(session), converter);
}
@@ -293,7 +294,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
Assert.hasText(cql, "CQL must not be empty");
return select(new SimpleStatement(cql), entityClass);
return select(SimpleStatement.newInstance(cql), entityClass);
}
/* (non-Javadoc)
@@ -305,7 +306,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
Assert.hasText(cql, "CQL must not be empty");
Assert.notNull(entityClass, "Entity type must not be null");
return selectOne(new SimpleStatement(cql), entityClass);
return selectOne(SimpleStatement.newInstance(cql), entityClass);
}
/* (non-Javadoc)
@@ -317,18 +318,18 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
Assert.hasText(cql, "CQL must not be empty");
Assert.notNull(entityClass, "Entity type must not be null");
return stream(new SimpleStatement(cql), entityClass);
return stream(SimpleStatement.newInstance(cql), entityClass);
}
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
// -------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#select(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.CassandraOperations#select(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> List<T> select(Statement statement, Class<T> entityClass) {
public <T> List<T> select(Statement<?> statement, Class<T> entityClass) {
Assert.notNull(statement, "Statement must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
@@ -339,20 +340,20 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#selectOne(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.CassandraOperations#selectOne(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> T selectOne(Statement statement, Class<T> entityClass) {
public <T> T selectOne(Statement<?> statement, Class<T> entityClass) {
List<T> result = select(statement, entityClass);
return result.isEmpty() ? null : result.get(0);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#slice(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.CassandraOperations#slice(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> Slice<T> slice(Statement statement, Class<T> entityClass) {
public <T> Slice<T> slice(Statement<?> statement, Class<T> entityClass) {
Assert.notNull(statement, "Statement must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
@@ -362,14 +363,14 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
Function<Row, T> mapper = getMapper(entityClass, entityClass, EntityQueryUtils.getTableName(statement));
return EntityQueryUtils.readSlice(resultSet, (row, rowNum) -> mapper.apply(row), 0,
getEffectiveFetchSize(statement));
getEffectivePageSize(statement));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#stream(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.CassandraOperations#stream(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> Stream<T> stream(Statement statement, Class<T> entityClass) throws DataAccessException {
public <T> Stream<T> stream(Statement<?> statement, Class<T> entityClass) throws DataAccessException {
Assert.notNull(statement, "Statement must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
@@ -398,18 +399,17 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
<T> List<T> doSelect(Query query, Class<?> entityClass, CqlIdentifier tableName, Class<T> returnType) {
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entityClass);
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
Columns columns = getStatementFactory().computeColumnsForProjection(query.getColumns(), persistentEntity,
returnType);
Columns columns = getStatementFactory().computeColumnsForProjection(query.getColumns(), entity, returnType);
Query queryToUse = query.columns(columns);
RegularStatement select = getStatementFactory().select(queryToUse, persistentEntity, tableName);
StatementBuilder<Select> select = getStatementFactory().select(queryToUse, entity, tableName);
Function<Row, T> mapper = getMapper(entityClass, returnType, tableName);
return getCqlOperations().query(select, (row, rowNum) -> mapper.apply(row));
return getCqlOperations().query(select.build(), (row, rowNum) -> mapper.apply(row));
}
/* (non-Javadoc)
@@ -432,9 +432,9 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
RegularStatement select = getStatementFactory().select(query, getRequiredPersistentEntity(entityClass));
StatementBuilder<Select> select = getStatementFactory().select(query, getRequiredPersistentEntity(entityClass));
return slice(select, entityClass);
return slice(select.build(), entityClass);
}
/* (non-Javadoc)
@@ -451,12 +451,13 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
<T> Stream<T> doStream(Query query, Class<?> entityClass, CqlIdentifier tableName, Class<T> returnType) {
RegularStatement statement = getStatementFactory().select(query, getRequiredPersistentEntity(entityClass),
StatementBuilder<Select> select = getStatementFactory().select(query, getRequiredPersistentEntity(entityClass),
tableName);
ResultSet resultSet = getCqlOperations().queryForResultSet(statement);
ResultSet resultSet = getCqlOperations().queryForResultSet(select.build());
return StreamSupport.stream(resultSet.spliterator(), false).map(getMapper(entityClass, returnType, tableName));
Function<Row, T> mapper = getMapper(entityClass, returnType, tableName);
return StreamSupport.stream(resultSet.map(mapper).spliterator(), false);
}
/* (non-Javadoc)
@@ -470,19 +471,20 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
Assert.notNull(update, "Update must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
Statement updateStatement = getStatementFactory().update(query, update, getRequiredPersistentEntity(entityClass));
StatementBuilder<Update> updateStatement = getStatementFactory().update(query, update,
getRequiredPersistentEntity(entityClass));
return getCqlOperations().execute(updateStatement);
return getCqlOperations().execute(updateStatement.build());
}
@Nullable
WriteResult doUpdate(Query query, org.springframework.data.cassandra.core.query.Update update, Class<?> entityClass,
CqlIdentifier tableName) {
RegularStatement updateStatement = getStatementFactory().update(query, update,
StatementBuilder<Update> updateStatement = getStatementFactory().update(query, update,
getRequiredPersistentEntity(entityClass), tableName);
return getCqlOperations().execute(new StatementCallback(updateStatement));
return getCqlOperations().execute(new StatementCallback(updateStatement.build()));
}
/* (non-Javadoc)
@@ -502,13 +504,15 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
@Nullable
WriteResult doDelete(Query query, Class<?> entityClass, CqlIdentifier tableName) {
RegularStatement delete = getStatementFactory().delete(query, getRequiredPersistentEntity(entityClass), tableName);
StatementBuilder<Delete> delete = getStatementFactory().delete(query, getRequiredPersistentEntity(entityClass),
tableName);
SimpleStatement statement = delete.build();
maybeEmitEvent(new BeforeDeleteEvent<>(delete, entityClass, tableName));
maybeEmitEvent(new BeforeDeleteEvent<>(statement, entityClass, tableName));
WriteResult writeResult = getCqlOperations().execute(new StatementCallback(delete));
WriteResult writeResult = getCqlOperations().execute(new StatementCallback(statement));
maybeEmitEvent(new AfterDeleteEvent<>(delete, entityClass, tableName));
maybeEmitEvent(new AfterDeleteEvent<>(statement, entityClass, tableName));
return writeResult;
}
@@ -525,11 +529,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
Assert.notNull(entityClass, "Entity type must not be null");
Select select = QueryBuilder.select().countAll().from(getTableName(entityClass).toCql());
Long count = getCqlOperations().queryForObject(select, Long.class);
return count != null ? count : 0L;
return count(Query.empty(), entityClass);
}
/* (non-Javadoc)
@@ -546,10 +546,11 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
long doCount(Query query, Class<?> entityClass, CqlIdentifier tableName) {
RegularStatement countStatement = getStatementFactory().count(query, getRequiredPersistentEntity(entityClass),
tableName);
StatementBuilder<Select> countStatement = getStatementFactory().count(query,
getRequiredPersistentEntity(entityClass), tableName);
Long count = getCqlOperations().queryForObject(countStatement, Long.class);
SimpleStatement statement = countStatement.build();
Long count = getCqlOperations().queryForObject(statement, Long.class);
return count != null ? count : 0L;
}
@@ -565,11 +566,10 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
Select select = QueryBuilder.select().from(getTableName(entityClass).toCql());
StatementBuilder<Select> select = getStatementFactory().selectOneById(id,
(source, sink) -> getConverter().write(source, sink, entity), entity.getTableName());
getConverter().write(id, select.where(), entity);
return getCqlOperations().queryForResultSet(select).iterator().hasNext();
return getCqlOperations().queryForResultSet(select.build()).iterator().hasNext();
}
/* (non-Javadoc)
@@ -586,10 +586,10 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
boolean doExists(Query query, Class<?> entityClass, CqlIdentifier tableName) {
RegularStatement select = getStatementFactory().select(query.limit(1), getRequiredPersistentEntity(entityClass),
tableName);
StatementBuilder<Select> select = getStatementFactory().select(query.limit(1),
getRequiredPersistentEntity(entityClass), tableName);
return getCqlOperations().queryForResultSet(select).iterator().hasNext();
return getCqlOperations().queryForResultSet(select.build()).iterator().hasNext();
}
/* (non-Javadoc)
@@ -601,15 +601,12 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
Assert.notNull(id, "Id must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
CqlIdentifier tableName = getTableName(entityClass);
Select select = QueryBuilder.select().all().from(tableName.toCql());
getConverter().write(id, select.where(), getRequiredPersistentEntity(entityClass));
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
CqlIdentifier tableName = entity.getTableName();
StatementBuilder<Select> select = getStatementFactory().selectOneById(id,
(source, sink) -> getConverter().write(source, sink, entity), tableName);
Function<Row, T> mapper = getMapper(entityClass, entityClass, tableName);
List<T> result = getCqlOperations().query(select, (row, rowNum) -> mapper.apply(row));
List<T> result = getCqlOperations().query(select.build(), (row, rowNum) -> mapper.apply(row));
return result.isEmpty() ? null : result.get(0);
}
@@ -638,18 +635,18 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
AdaptibleEntity<T> source = getEntityOperations().forEntity(maybeCallBeforeConvert(entity, tableName),
getConverter().getConversionService());
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
T entityToUse = source.isVersionedEntity() ? source.initializeVersionProperty() : entity;
Insert insert = EntityQueryUtils.createInsertQuery(tableName.toCql(), entityToUse, options, getConverter(),
persistentEntity);
StatementBuilder<RegularInsert> builder = getStatementFactory().insert(entityToUse, options,
source.getPersistentEntity(), tableName);
SimpleStatement insert = builder.build();
return source.isVersionedEntity() ? doInsertVersioned(insert.ifNotExists(), entityToUse, source, tableName)
return source.isVersionedEntity() ? doInsertVersioned(insert, entityToUse, source, tableName)
: doInsert(insert, entityToUse, tableName);
}
private <T> EntityWriteResult<T> doInsertVersioned(Insert insert, T entity, AdaptibleEntity<T> source,
private <T> EntityWriteResult<T> doInsertVersioned(SimpleStatement insert, T entity, AdaptibleEntity<T> source,
CqlIdentifier tableName) {
return executeSave(entity, tableName, insert, result -> {
@@ -662,7 +659,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
});
}
private <T> EntityWriteResult<T> doInsert(Insert insert, T entity, CqlIdentifier tableName) {
private <T> EntityWriteResult<T> doInsert(SimpleStatement insert, T entity, CqlIdentifier tableName) {
return executeSave(entity, tableName, insert);
}
@@ -701,9 +698,11 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
Number previousVersion = source.getVersion();
T toSave = source.incrementVersion();
Update update = getStatementFactory().update(toSave, options, getConverter(), persistentEntity, tableName);
StatementBuilder<Update> builder = getStatementFactory().update(toSave, options, persistentEntity, tableName);
source.appendVersionCondition(builder, previousVersion);
SimpleStatement update = builder.build();
return executeSave(toSave, tableName, source.appendVersionCondition(update, previousVersion), result -> {
return executeSave(toSave, tableName, update, result -> {
if (!result.wasApplied()) {
throw new OptimisticLockingFailureException(
@@ -716,9 +715,9 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
private <T> EntityWriteResult<T> doUpdate(T entity, UpdateOptions options, CqlIdentifier tableName,
CassandraPersistentEntity<?> persistentEntity) {
Update update = getStatementFactory().update(entity, options, getConverter(), persistentEntity, tableName);
StatementBuilder<Update> builder = getStatementFactory().update(entity, options, persistentEntity, tableName);
return executeSave(entity, tableName, update);
return executeSave(entity, tableName, builder.build());
}
/* (non-Javadoc)
@@ -742,16 +741,18 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
CqlIdentifier tableName = persistentEntity.getTableName();
Delete delete = getStatementFactory().delete(entity, options, getConverter(), persistentEntity, tableName);
StatementBuilder<Delete> builder = getStatementFactory().delete(entity, options, getConverter(), tableName);
source.appendVersionCondition(builder);
SimpleStatement delete = builder.build();
return source.isVersionedEntity() ? doDeleteVersioned(delete, entity, source, tableName)
: doDelete(delete, entity, tableName);
}
private WriteResult doDeleteVersioned(Delete delete, Object entity, AdaptibleEntity<Object> source,
private WriteResult doDeleteVersioned(Statement<?> statement, Object entity, AdaptibleEntity<Object> source,
CqlIdentifier tableName) {
return executeDelete(entity, tableName, source.appendVersionCondition(delete), result -> {
return executeDelete(entity, tableName, statement, result -> {
if (!result.wasApplied()) {
throw new OptimisticLockingFailureException(
@@ -761,7 +762,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
});
}
private WriteResult doDelete(Delete delete, Object entity, CqlIdentifier tableName) {
private WriteResult doDelete(SimpleStatement delete, Object entity, CqlIdentifier tableName) {
return executeDelete(entity, tableName, delete, result -> {});
}
@@ -776,15 +777,16 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
CqlIdentifier tableName = entity.getTableName();
Delete delete = QueryBuilder.delete().from(tableName.toCql());
getConverter().write(id, delete.where(), entity);
StatementBuilder<Delete> delete = getStatementFactory().deleteById(id,
(source, sink) -> getConverter().write(source, sink, entity), tableName);
SimpleStatement statement = delete.build();
maybeEmitEvent(new BeforeDeleteEvent<>(delete, entityClass, tableName));
maybeEmitEvent(new BeforeDeleteEvent<>(statement, entityClass, tableName));
boolean result = getCqlOperations().execute(delete);
boolean result = getCqlOperations().execute(statement);
maybeEmitEvent(new AfterDeleteEvent<>(delete, entityClass, tableName));
maybeEmitEvent(new AfterDeleteEvent<>(statement, entityClass, tableName));
return result;
}
@@ -798,13 +800,14 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
Assert.notNull(entityClass, "Entity type must not be null");
CqlIdentifier tableName = getTableName(entityClass);
Truncate truncate = QueryBuilder.truncate(tableName.toCql());
Truncate truncate = QueryBuilder.truncate(tableName);
SimpleStatement statement = truncate.build();
maybeEmitEvent(new BeforeDeleteEvent<>(truncate, entityClass, tableName));
maybeEmitEvent(new BeforeDeleteEvent<>(statement, entityClass, tableName));
getCqlOperations().execute(truncate);
getCqlOperations().execute(statement);
maybeEmitEvent(new AfterDeleteEvent<>(truncate, entityClass, tableName));
maybeEmitEvent(new AfterDeleteEvent<>(statement, entityClass, tableName));
}
// -------------------------------------------------------------------------
@@ -847,11 +850,11 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
// Implementation hooks and utility methods
// -------------------------------------------------------------------------
private <T> EntityWriteResult<T> executeSave(T entity, CqlIdentifier tableName, Statement statement) {
private <T> EntityWriteResult<T> executeSave(T entity, CqlIdentifier tableName, Statement<?> statement) {
return executeSave(entity, tableName, statement, ignore -> {});
}
private <T> EntityWriteResult<T> executeSave(T entity, CqlIdentifier tableName, Statement statement,
private <T> EntityWriteResult<T> executeSave(T entity, CqlIdentifier tableName, Statement<?> statement,
Consumer<WriteResult> resultConsumer) {
maybeEmitEvent(new BeforeSaveEvent<>(entity, tableName, statement));
@@ -865,7 +868,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
return EntityWriteResult.of(result, entityToSave);
}
private WriteResult executeDelete(Object entity, CqlIdentifier tableName, Statement statement,
private WriteResult executeDelete(Object entity, CqlIdentifier tableName, Statement<?> statement,
Consumer<WriteResult> resultConsumer) {
maybeEmitEvent(new BeforeDeleteEvent<>(statement, entity.getClass(), tableName));
@@ -879,15 +882,15 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
return result;
}
private int getConfiguredFetchSize(Session session) {
return session.getCluster().getConfiguration().getQueryOptions().getFetchSize();
private int getConfiguredPageSize(CqlSession session) {
return session.getContext().getConfig().getDefaultProfile().getInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 5000);
}
@SuppressWarnings("ConstantConditions")
private int getEffectiveFetchSize(Statement statement) {
private int getEffectivePageSize(Statement<?> statement) {
if (statement.getFetchSize() > 0) {
return statement.getFetchSize();
if (statement.getPageSize() > 0) {
return statement.getPageSize();
}
if (getCqlOperations() instanceof CassandraAccessor) {
@@ -899,7 +902,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
}
}
return getCqlOperations().execute(this::getConfiguredFetchSize);
return getCqlOperations().execute(this::getConfiguredPageSize);
}
@SuppressWarnings("unchecked")
@@ -952,7 +955,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
return object;
}
protected <T> T maybeCallBeforeSave(T object, CqlIdentifier tableName, Statement statement) {
protected <T> T maybeCallBeforeSave(T object, CqlIdentifier tableName, Statement<?> statement) {
if (null != entityCallbacks) {
return (T) entityCallbacks.callback(BeforeSaveCallback.class, object, tableName, statement);
@@ -964,13 +967,13 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
@Value
static class StatementCallback implements SessionCallback<WriteResult>, CqlProvider {
@lombok.NonNull Statement statement;
@lombok.NonNull Statement<?> statement;
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.SessionCallback#doInSession(org.springframework.data.cassandra.Session)
*/
@Override
public WriteResult doInSession(Session session) throws DriverException, DataAccessException {
public WriteResult doInSession(CqlSession session) throws DriverException, DataAccessException {
return WriteResult.of(session.execute(this.statement));
}

View File

@@ -21,7 +21,7 @@ import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.cql.util.StatementBuilder;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
@@ -31,10 +31,10 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Update;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.querybuilder.condition.Condition;
import com.datastax.oss.driver.api.querybuilder.delete.Delete;
import com.datastax.oss.driver.api.querybuilder.update.Update;
/**
* Common data access operations performed on an entity using a {@link MappingContext} containing mapping metadata.
@@ -86,8 +86,8 @@ class EntityOperations {
* @return the {@link MappingContext} used by this entity data access operations class.
* @see org.springframework.data.cassandra.core.mapping.CassandraMappingContext
*/
CassandraPersistentEntity<?> getRequiredPersistentEntity(Class<?> entityType) {
return getMappingContext().getRequiredPersistentEntity(ClassUtils.getUserClass(entityType));
CassandraPersistentEntity<?> getRequiredPersistentEntity(Class<?> entityClass) {
return getMappingContext().getRequiredPersistentEntity(ClassUtils.getUserClass(entityClass));
}
/**
@@ -151,7 +151,7 @@ class EntityOperations {
* @param currentVersionNumber previous version number.
* @return the altered {@link Update} containing the {@code IF} condition for optimistic locking.
*/
Statement appendVersionCondition(Update update, Number currentVersionNumber);
void appendVersionCondition(StatementBuilder<Update> update, Number currentVersionNumber);
/**
* Appends a {@code IF} condition to an {@link Delete} statement for optimistic locking to perform the delete only
@@ -162,7 +162,7 @@ class EntityOperations {
* @return the altered {@link Delete} containing the {@code IF} condition for optimistic locking.
* @see #getVersion()
*/
Statement appendVersionCondition(Delete delete);
void appendVersionCondition(StatementBuilder<Delete> delete);
/**
* Initializes the version property of the of the current entity if available.
@@ -186,6 +186,14 @@ class EntityOperations {
*/
@Nullable
Number getVersion();
/**
* Returns the {@link CassandraPersistentEntity}.
*
* @return the {@link CassandraPersistentEntity}.
*/
CassandraPersistentEntity<?> getPersistentEntity();
}
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
@@ -262,21 +270,25 @@ class EntityOperations {
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity#appendVersionCondition(com.datastax.driver.core.querybuilder.Update, java.lang.Number)
* @see org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity#appendVersionCondition(com.datastax.oss.driver.api.querybuilder.update.Update, java.lang.Number)
*/
@Override
public Statement appendVersionCondition(com.datastax.driver.core.querybuilder.Update update,
Number currentVersionNumber) {
public void appendVersionCondition(StatementBuilder<Update> update, Number currentVersionNumber) {
return update.onlyIf(QueryBuilder.eq(getVersionColumnName().toCql(), currentVersionNumber));
update.bind((statement, factory) -> {
return statement.if_(Condition.column(getVersionColumnName()).isEqualTo(factory.create(currentVersionNumber)));
});
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity#appendVersionCondition(com.datastax.driver.core.querybuilder.Delete)
* @see org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity#appendVersionCondition(com.datastax.oss.driver.api.querybuilder.delete.Delete)
*/
@Override
public Statement appendVersionCondition(Delete delete) {
return delete.onlyIf(QueryBuilder.eq(getVersionColumnName().toCql(), getVersion()));
public void appendVersionCondition(StatementBuilder<Delete> delete) {
delete.bind((statement, factory) -> {
return statement.if_(Condition.column(getVersionColumnName()).isEqualTo(factory.create(getVersion())));
});
}
/* (non-Javadoc)
@@ -323,6 +335,14 @@ class EntityOperations {
return this.propertyAccessor.getProperty(versionProperty, Number.class);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity#getPersistentEntity()
*/
@Override
public CassandraPersistentEntity<?> getPersistentEntity() {
return this.entity;
}
private CqlIdentifier getVersionColumnName() {
return this.entity.getRequiredVersionProperty().getColumnName();
}

View File

@@ -15,43 +15,27 @@
*/
package org.springframework.data.cassandra.core;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.cql.QueryOptions;
import org.springframework.data.cassandra.core.cql.QueryOptionsUtil;
import org.springframework.data.cassandra.core.cql.RowMapper;
import org.springframework.data.cassandra.core.cql.WriteOptions;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.query.CassandraPageRequest;
import org.springframework.data.convert.EntityWriter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.PagingState;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Delete.Where;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Update;
import com.google.common.collect.Iterators;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.Statement;
/**
* Simple utility class for working with the QueryBuilder API using mapped entities.
@@ -66,107 +50,6 @@ class EntityQueryUtils {
private static final Pattern FROM_REGEX = Pattern.compile(" FROM ([\"]?[\\w]*[\\\\.]?[\\w]*[\"]?)[\\s]?",
Pattern.CASE_INSENSITIVE);
/**
* Creates a Query Object for an insert.
*
* @param tableName the table name, must not be empty and not {@literal null}.
* @param objectToInsert the object to save, must not be {@literal null}.
* @param options optional {@link WriteOptions} to apply to the {@link Insert} statement, may be {@literal null}.
* @param entityWriter the {@link EntityWriter} to write insert values.
* @param entity must not be {@literal null}.
* @return The Query object to run with session.execute();
*/
static Insert createInsertQuery(String tableName, Object objectToInsert, WriteOptions options,
CassandraConverter entityWriter, CassandraPersistentEntity<?> entity) {
Assert.hasText(tableName, "TableName must not be empty");
Assert.notNull(objectToInsert, "Object to insert must not be null");
Assert.notNull(entityWriter, "CassandraConverter must not be null");
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
Insert insert = addWriteOptions(QueryBuilder.insertInto(tableName), options);
boolean insertNulls = false;
if (options instanceof InsertOptions) {
InsertOptions insertOptions = (InsertOptions) options;
insertNulls = insertOptions.isInsertNulls();
}
if (insertNulls) {
Map<String, Object> toInsert = new LinkedHashMap<>();
entityWriter.write(objectToInsert, toInsert, entity);
for (Entry<String, Object> entry : toInsert.entrySet()) {
insert.value(entry.getKey(), entry.getValue());
}
} else {
entityWriter.write(objectToInsert, insert);
}
return insert;
}
/**
* Creates a Query Object for an Update. The {@link Update} uses the identity and values from the given
* {@code objectsToUpdate}.
*
* @param tableName the table name, must not be empty and not {@literal null}.
* @param objectToUpdate the object to update, must not be {@literal null}.
* @param options optional {@link WriteOptions} to apply to the {@link Update} statement.
* @param entityWriter the {@link EntityWriter} to write update assignments and where clauses.
* @return The Query object to run with session.execute();
*/
static Update createUpdateQuery(String tableName, Object objectToUpdate, WriteOptions options,
EntityWriter<Object, Object> entityWriter) {
Assert.hasText(tableName, "TableName must not be empty");
Assert.notNull(objectToUpdate, "Object to update must not be null");
Assert.notNull(entityWriter, "EntityWriter must not be null");
Update update = addWriteOptions(QueryBuilder.update(tableName), options);
entityWriter.write(objectToUpdate, update);
return update;
}
/**
* Creates a Delete Query Object from an annotated POJO. The {@link Delete} uses the identity from the given
* {@code objectToDelete}.
*
* @param tableName the table name, must not be empty and not {@literal null}.
* @param objectToDelete the object to delete, must not be {@literal null}.
* @param options optional {@link QueryOptions} to apply to the {@link Delete} statement.
* @param entityWriter the {@link EntityWriter} to write delete where clauses.
* @return The Query object to run with session.execute();
*/
static Delete createDeleteQuery(String tableName, Object objectToDelete, QueryOptions options,
EntityWriter<Object, Object> entityWriter) {
Assert.hasText(tableName, "TableName must not be empty");
Assert.notNull(objectToDelete, "Object to delete must not be null");
Assert.notNull(entityWriter, "EntityWriter must not be null");
Delete.Selection deleteSelection = QueryBuilder.delete();
Delete delete = deleteSelection.from(tableName);
if (options instanceof WriteOptions) {
addWriteOptions(delete, (WriteOptions) options);
} else {
QueryOptionsUtil.addQueryOptions(delete, options);
}
Where where = delete.where();
entityWriter.write(objectToDelete, where);
return delete;
}
/**
* Read a {@link Slice} of data from the {@link ResultSet} for a {@link Pageable}.
*
@@ -180,8 +63,24 @@ class EntityQueryUtils {
int toRead = resultSet.getAvailableWithoutFetching();
return readSlice(() -> Iterators.limit(resultSet.iterator(), toRead), resultSet.getExecutionInfo().getPagingState(),
mapper, page, pageSize);
return readSlice(() -> limit(resultSet.iterator(), toRead), resultSet.getExecutionInfo().getPagingState(), mapper,
page, pageSize);
}
/**
* Read a {@link Slice} of data from the {@link ResultSet} for a {@link Pageable}.
*
* @param resultSet must not be {@literal null}.
* @param mapper must not be {@literal null}.
* @param page
* @param pageSize
* @return the resulting {@link Slice}.
* @since 3.0
*/
static <T> Slice<T> readSlice(AsyncResultSet resultSet, RowMapper<T> mapper, int page, int pageSize) {
return readSlice(() -> limit(resultSet.currentPage().iterator(), resultSet.remaining()),
resultSet.getExecutionInfo().getPagingState(), mapper, page, pageSize);
}
/**
@@ -195,7 +94,7 @@ class EntityQueryUtils {
* @return the resulting {@link Slice}.
* @since 2.1
*/
static <T> Slice<T> readSlice(Iterable<Row> rows, @Nullable PagingState pagingState, RowMapper<T> mapper, int page,
static <T> Slice<T> readSlice(Iterable<Row> rows, @Nullable ByteBuffer pagingState, RowMapper<T> mapper, int page,
int pageSize) {
List<T> result = new ArrayList<>(pageSize);
@@ -220,19 +119,7 @@ class EntityQueryUtils {
* @return
* @since 2.1
*/
static CqlIdentifier getTableName(Statement statement) {
if (statement instanceof Select) {
Select select = (Select) statement;
DirectFieldAccessor accessor = new DirectFieldAccessor(select);
String table = (String) accessor.getPropertyValue("table");
if (table != null) {
return CqlIdentifier.isQuotedIdentifier(table) ? CqlIdentifier.quoted(unquote(table)) : CqlIdentifier.of(table);
}
}
static CqlIdentifier getTableName(Statement<?> statement) {
String cql = statement.toString();
Matcher matcher = FROM_REGEX.matcher(cql);
@@ -240,103 +127,52 @@ class EntityQueryUtils {
if (matcher.find()) {
String cqlTableName = matcher.group(1);
if (CqlIdentifier.isQuotedIdentifier(cqlTableName)) {
return CqlIdentifier.quoted(unquote(cqlTableName));
}
int separator = cqlTableName.indexOf('.');
if (separator != -1) {
return CqlIdentifier.of(cqlTableName.substring(separator + 1));
return CqlIdentifier.fromCql(cqlTableName.substring(separator + 1));
}
return CqlIdentifier.of(cqlTableName);
return CqlIdentifier.fromCql(cqlTableName);
}
return CqlIdentifier.of("unknown");
return CqlIdentifier.fromCql("unknown");
}
/**
* Add common {@link WriteOptions} options to {@link Insert} CQL statements.
* Returns a view containing the first {@code limitSize} elements of {@code iterator}. If {@code
* iterator} contains fewer than {@code limitSize} elements, the returned view contains all of its elements. The
* returned iterator supports {@code remove()} if {@code iterator} does.
*
* @param insert {@link Insert} CQL statement, must not be {@literal null}.
* @param writeOptions write options (e.g. consistency level) to add to the CQL statement.
* @return the given {@link Insert}.
* @see #addWriteOptions(Insert, WriteOptions)
* @since 2.1
* @param iterator the iterator to limit
* @param limitSize the maximum number of elements in the returned iterator
* @throws IllegalArgumentException if {@code limitSize} is negative
* @since 3.0
*/
static Insert addWriteOptions(Insert insert, WriteOptions writeOptions) {
private static <T> Iterator<T> limit(Iterator<T> iterator, int limitSize) {
Assert.notNull(insert, "Insert must not be null");
return new Iterator<T>() {
private int count;
if (writeOptions instanceof InsertOptions) {
InsertOptions insertOptions = (InsertOptions) writeOptions;
if (insertOptions.isIfNotExists()) {
insert = insert.ifNotExists();
@Override
public boolean hasNext() {
return count < limitSize && iterator.hasNext();
}
}
QueryOptionsUtil.addWriteOptions(insert, writeOptions);
return insert;
}
/**
* Add common {@link WriteOptions} options to {@link Update} CQL statements.
*
* @param update {@link Update} CQL statement, must not be {@literal null}.
* @param writeOptions write options (e.g. consistency level) to add to the CQL statement.
* @return the given {@link Update}.
* @see QueryOptionsUtil#addWriteOptions(Update, WriteOptions)
* @since 2.1
*/
static Update addWriteOptions(Update update, WriteOptions writeOptions) {
Assert.notNull(update, "Update must not be null");
QueryOptionsUtil.addWriteOptions(update, writeOptions);
if (writeOptions instanceof UpdateOptions) {
UpdateOptions updateOptions = (UpdateOptions) writeOptions;
if (updateOptions.isIfExists()) {
update.where().ifExists();
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
count++;
return iterator.next();
}
}
return update;
}
/**
* Add common {@link WriteOptions} options to {@link Delete} CQL statements.
*
* @param delete {@link Delete} CQL statement, must not be {@literal null}.
* @param writeOptions write options (e.g. consistency level) to add to the CQL statement.
* @return the given {@link Delete}.
* @since 2.1
*/
static Delete addWriteOptions(Delete delete, WriteOptions writeOptions) {
Assert.notNull(delete, "Delete must not be null");
QueryOptionsUtil.addWriteOptions(delete, writeOptions);
if (writeOptions instanceof DeleteOptions) {
DeleteOptions deleteOptions = (DeleteOptions) writeOptions;
if (deleteOptions.isIfExists()) {
delete.where().ifExists();
@Override
public void remove() {
iterator.remove();
}
}
return delete;
}
private static String unquote(String identifier) {
return identifier.substring(1, identifier.length() - 1);
};
}
}

View File

@@ -17,9 +17,9 @@ package org.springframework.data.cassandra.core;
import java.util.List;
import com.datastax.driver.core.ExecutionInfo;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.oss.driver.api.core.cql.ExecutionInfo;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
/**
* The result of a write operation for an entity.

View File

@@ -15,10 +15,11 @@
*/
package org.springframework.data.cassandra.core;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* {@link ExecutableDeleteOperation} allows creation and execution of Cassandra {@code DELETE} operations in a fluent
* API style.
@@ -72,7 +73,7 @@ public interface ExecutableDeleteOperation {
Assert.hasText(table, "Table name must not be null or empty");
return inTable(CqlIdentifier.of(table));
return inTable(CqlIdentifier.fromCql(table));
}
/**

View File

@@ -20,11 +20,12 @@ import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Implementation of {@link ExecutableDeleteOperation}.
*
@@ -49,13 +50,6 @@ class ExecutableDeleteOperationSupport implements ExecutableDeleteOperation {
return new ExecutableDeleteSupport(this.template, domainType, Query.empty(), null);
}
// TODO: rethink the implementation
// While the use of final fields and construction on mutation effectively makes this class Thread-safe,
// it is possible this implementation could generate a high-level of young-gen garbage on the JVM heap,
// particularly if the template delete(..) (and this class) are used inside of a loop for a large number
// of domain types. Of course, this assumption is highly contingent on the user's `Query`
// in addition to his/her application design.
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableDeleteSupport implements ExecutableDelete, TerminatingDelete {

View File

@@ -15,9 +15,10 @@
*/
package org.springframework.data.cassandra.core;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* {@link ExecutableInsertOperation} allows creation and execution of Cassandra {@code INSERT} insert operations in a
* fluent API style.
@@ -69,7 +70,7 @@ public interface ExecutableInsertOperation {
Assert.hasText(table, "Table name must not be null or empty");
return inTable(CqlIdentifier.of(table));
return inTable(CqlIdentifier.fromCql(table));
}
/**

View File

@@ -20,10 +20,11 @@ import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Implementation of {@link ExecutableInsertOperation}.
*
@@ -47,12 +48,6 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation {
return new ExecutableInsertSupport<>(this.template, domainType, InsertOptions.empty(), null);
}
// TODO: rethink the implementation
// While the use of final fields and construction on mutation effectively makes this class Thread-safe,
// it is possible this implementation could generate a high-level of young-gen garbage on the JVM heap,
// particularly if the template insert(..) (and this class) are used inside of a loop for a large number
// of domain types. Of course, this assumption is highly contingent on the user's application design.
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableInsertSupport<T> implements ExecutableInsert<T> {

View File

@@ -19,11 +19,12 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* The {@link ExecutableSelectOperation} interface allows creation and execution of Cassandra {@code SELECT} operations
* in a fluent API style.
@@ -85,7 +86,7 @@ public interface ExecutableSelectOperation {
Assert.hasText(table, "Table name must not be null or empty");
return inTable(CqlIdentifier.of(table));
return inTable(CqlIdentifier.fromCql(table));
}
/**

View File

@@ -24,12 +24,13 @@ import java.util.List;
import java.util.stream.Stream;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Implementation of {@link ExecutableSelectOperation}.
*
@@ -54,13 +55,6 @@ class ExecutableSelectOperationSupport implements ExecutableSelectOperation {
return new ExecutableSelectSupport<>(this.template, domainType, domainType, Query.empty(), null);
}
// TODO: rethink the implementation
// While the use of final fields and construction on mutation effectively makes this class Thread-safe,
// it is possible this implementation could generate a high-level of young-gen garbage on the JVM heap,
// particularly if the template query(..) (and this class) are used inside of a loop for a large number
// of domain types. Of course, this assumption is highly contingent on the user's `Query`
// in addition to his/her application design.
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableSelectSupport<T> implements ExecutableSelect<T> {

View File

@@ -15,11 +15,12 @@
*/
package org.springframework.data.cassandra.core;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* {@link ExecutableUpdateOperation} allows creation and execution of Cassandra {@code UPDATE} operation in a fluent API
* style.
@@ -78,7 +79,7 @@ public interface ExecutableUpdateOperation {
Assert.hasText(table, "Table name must not be null or empty");
return inTable(CqlIdentifier.of(table));
return inTable(CqlIdentifier.fromCql(table));
}
/**

View File

@@ -20,12 +20,13 @@ import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Implementation of {@link ExecutableUpdateOperation}.
*
@@ -51,13 +52,6 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation {
return new ExecutableUpdateSupport(this.template, domainType, Query.empty(), null);
}
// TODO: rethink the implementation
// While the use of final fields and construction on mutation effectively makes this class Thread-safe,
// it is possible this implementation could generate a high-level of young-gen garbage on the JVM heap,
// particularly if the template update(..) (and this class) are used inside of a loop for a large number
// of domain types. Of course, this assumption is highly contingent on the user's `Query`
// in addition to his/her application design.
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableUpdateSupport implements ExecutableUpdate, TerminatingUpdate {

View File

@@ -35,13 +35,11 @@ import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Batch;
import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Update;
import com.datastax.oss.driver.api.core.cql.BatchStatement;
import com.datastax.oss.driver.api.core.cql.BatchStatementBuilder;
import com.datastax.oss.driver.api.core.cql.BatchType;
import com.datastax.oss.driver.api.core.cql.BatchableStatement;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
/**
* Default implementation for {@link ReactiveCassandraBatchOperations}.
@@ -54,13 +52,13 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations
private final AtomicBoolean executed = new AtomicBoolean();
private final Batch batch = QueryBuilder.batch();
private final BatchStatementBuilder batch = BatchStatement.builder(BatchType.LOGGED);
private final CassandraConverter converter;
private final CassandraMappingContext mappingContext;
private final List<Mono<Collection<? extends BuiltStatement>>> batchMonos = new CopyOnWriteArrayList<>();
private final List<Mono<Collection<? extends BatchableStatement<?>>>> batchMonos = new CopyOnWriteArrayList<>();
private final ReactiveCassandraOperations operations;
@@ -78,7 +76,7 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations
this.operations = operations;
this.converter = operations.getConverter();
this.mappingContext = this.converter.getMappingContext();
this.statementFactory = new StatementFactory(new UpdateMapper(this.converter));
this.statementFactory = new StatementFactory(new UpdateMapper(converter));
}
private void assertNotExecuted() {
@@ -137,9 +135,9 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations
.collectList() //
.flatMap(statements -> {
statements.forEach(this.batch::add);
this.batch.addStatements(statements);
return this.operations.getReactiveCqlOperations().queryForResultSet(this.batch);
return this.operations.getReactiveCqlOperations().queryForResultSet(this.batch.build());
}).flatMap(resultSet -> resultSet.rows().collectList()
.map(rows -> new WriteResult(resultSet.getAllExecutionInfo(), resultSet.wasApplied(), rows)));
@@ -157,7 +155,7 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations
assertNotExecuted();
this.batch.using(QueryBuilder.timestamp(timestamp));
this.batch.setQueryTimestamp(timestamp);
return this;
}
@@ -221,11 +219,10 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations
return this;
}
private Collection<? extends BuiltStatement> doInsert(Iterable<?> entities, WriteOptions options) {
private Collection<SimpleStatement> doInsert(Iterable<?> entities, WriteOptions options) {
CassandraConverter converter = getConverter();
CassandraMappingContext mappingContext = getMappingContext();
List<Insert> insertQueries = new ArrayList<>();
List<SimpleStatement> insertQueries = new ArrayList<>();
for (Object entity : entities) {
@@ -234,8 +231,8 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations
BasicCassandraPersistentEntity<?> persistentEntity = mappingContext
.getRequiredPersistentEntity(entity.getClass());
Insert insertQuery = EntityQueryUtils.createInsertQuery(persistentEntity.getTableName().toCql(), entity, options,
converter, persistentEntity);
SimpleStatement insertQuery = getStatementFactory()
.insert(entity, options, persistentEntity, persistentEntity.getTableName()).build();
insertQueries.add(insertQuery);
}
@@ -302,10 +299,9 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations
return this;
}
private Collection<? extends BuiltStatement> doUpdate(Iterable<?> entities, WriteOptions options) {
private Collection<SimpleStatement> doUpdate(Iterable<?> entities, WriteOptions options) {
CassandraConverter converter = getConverter();
List<Update> updateQueries = new ArrayList<>();
List<SimpleStatement> updateQueries = new ArrayList<>();
for (Object entity : entities) {
@@ -313,8 +309,8 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
Update update = getStatementFactory().update(entity, options, converter, persistentEntity,
persistentEntity.getTableName());
SimpleStatement update = getStatementFactory()
.update(entity, options, persistentEntity, persistentEntity.getTableName()).build();
updateQueries.add(update);
}
@@ -381,10 +377,9 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations
return this;
}
private Collection<? extends BuiltStatement> doDelete(Iterable<?> entities, WriteOptions options) {
private Collection<SimpleStatement> doDelete(Iterable<?> entities, WriteOptions options) {
CassandraConverter converter = getConverter();
List<Delete> deleteQueries = new ArrayList<>();
List<SimpleStatement> deleteQueries = new ArrayList<>();
for (Object entity : entities) {
@@ -392,8 +387,8 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
Delete delete = getStatementFactory().delete(entity, options, converter, persistentEntity,
persistentEntity.getTableName());
SimpleStatement delete = getStatementFactory()
.delete(entity, options, getConverter(), persistentEntity.getTableName()).build();
deleteQueries.add(delete);
}

View File

@@ -28,7 +28,7 @@ import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
import org.springframework.data.domain.Slice;
import com.datastax.driver.core.Statement;
import com.datastax.oss.driver.api.core.cql.Statement;
/**
* Interface specifying a basic set of reactive Cassandra operations. Implemented by {@link ReactiveCassandraTemplate}.
@@ -98,7 +98,7 @@ public interface ReactiveCassandraOperations extends ReactiveFluentCassandraOper
<T> Mono<T> selectOne(String cql, Class<T> entityClass) throws DataAccessException;
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
// -------------------------------------------------------------------------
/**
@@ -109,7 +109,7 @@ public interface ReactiveCassandraOperations extends ReactiveFluentCassandraOper
* @return the result objects returned by the action.
* @throws DataAccessException if there is any problem issuing the execution.
*/
<T> Flux<T> select(Statement statement, Class<T> entityClass) throws DataAccessException;
<T> Flux<T> select(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
/**
* Execute a {@code SELECT} query with paging and convert the result set to a {@link Slice} of entities. A sliced
@@ -121,7 +121,7 @@ public interface ReactiveCassandraOperations extends ReactiveFluentCassandraOper
* @throws DataAccessException if there is any problem executing the query.
* @since 2.1
*/
<T> Mono<Slice<T>> slice(Statement statement, Class<T> entityClass) throws DataAccessException;
<T> Mono<Slice<T>> slice(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
/**
* Execute a {@code SELECT} query and convert the resulting item to an entity.
@@ -131,7 +131,7 @@ public interface ReactiveCassandraOperations extends ReactiveFluentCassandraOper
* @return the result object returned by the action or {@link Mono#empty()}
* @throws DataAccessException if there is any problem issuing the execution.
*/
<T> Mono<T> selectOne(Statement statement, Class<T> entityClass) throws DataAccessException;
<T> Mono<T> selectOne(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
// -------------------------------------------------------------------------
// Methods dealing with org.springframework.data.cassandra.core.query.Query

View File

@@ -39,10 +39,7 @@ import org.springframework.data.cassandra.ReactiveSessionFactory;
import org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.convert.QueryMapper;
import org.springframework.data.cassandra.core.convert.UpdateMapper;
import org.springframework.data.cassandra.core.cql.CassandraAccessor;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.cql.CqlProvider;
import org.springframework.data.cassandra.core.cql.QueryOptions;
import org.springframework.data.cassandra.core.cql.ReactiveCqlOperations;
@@ -51,6 +48,7 @@ import org.springframework.data.cassandra.core.cql.ReactiveSessionCallback;
import org.springframework.data.cassandra.core.cql.RowMapper;
import org.springframework.data.cassandra.core.cql.WriteOptions;
import org.springframework.data.cassandra.core.cql.session.DefaultReactiveSessionFactory;
import org.springframework.data.cassandra.core.cql.util.StatementBuilder;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.event.AfterConvertEvent;
import org.springframework.data.cassandra.core.mapping.event.AfterDeleteEvent;
@@ -72,18 +70,19 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.RegularStatement;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Truncate;
import com.datastax.driver.core.querybuilder.Update;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
import com.datastax.oss.driver.api.core.context.DriverContext;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
import com.datastax.oss.driver.api.querybuilder.delete.Delete;
import com.datastax.oss.driver.api.querybuilder.insert.RegularInsert;
import com.datastax.oss.driver.api.querybuilder.select.Select;
import com.datastax.oss.driver.api.querybuilder.truncate.Truncate;
import com.datastax.oss.driver.api.querybuilder.update.Update;
/**
* Primary implementation of {@link ReactiveCassandraOperations}. It simplifies the use of Reactive Cassandra usage and
@@ -180,7 +179,7 @@ public class ReactiveCassandraTemplate
this.cqlOperations = reactiveCqlOperations;
this.entityOperations = new EntityOperations(converter.getMappingContext());
this.projectionFactory = new SpelAwareProxyProjectionFactory();
this.statementFactory = new StatementFactory(new QueryMapper(converter), new UpdateMapper(converter));
this.statementFactory = new StatementFactory(converter);
}
/* (non-Javadoc)
@@ -278,7 +277,7 @@ public class ReactiveCassandraTemplate
}
CqlIdentifier getTableName(Class<?> entityClass) {
return getEntityOperations().getTableName(entityClass);
return getRequiredPersistentEntity(entityClass).getTableName();
}
// -------------------------------------------------------------------------
@@ -293,7 +292,7 @@ public class ReactiveCassandraTemplate
Assert.hasText(cql, "CQL must not be empty");
return select(new SimpleStatement(cql), entityClass);
return select(SimpleStatement.newInstance(cql), entityClass);
}
/* (non-Javadoc)
@@ -305,36 +304,36 @@ public class ReactiveCassandraTemplate
}
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
// -------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#select(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#select(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> Flux<T> select(Statement statement, Class<T> entityClass) {
public <T> Flux<T> select(Statement<?> statement, Class<T> entityClass) {
Assert.notNull(statement, "Statement must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
Function<Row, T> mapper = getMapper(entityClass, entityClass, EntityQueryUtils.getTableName(statement));
Function<Row, T> mapper = getMapper(entityClass, entityClass, null);
return getReactiveCqlOperations().query(statement, (row, rowNum) -> mapper.apply(row));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#selectOne(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#selectOne(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> Mono<T> selectOne(Statement statement, Class<T> entityClass) {
public <T> Mono<T> selectOne(Statement<?> statement, Class<T> entityClass) {
return select(statement, entityClass).next();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#slice(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.CassandraOperations#slice(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> Mono<Slice<T>> slice(Statement statement, Class<T> entityClass) {
public <T> Mono<Slice<T>> slice(Statement<?> statement, Class<T> entityClass) {
Assert.notNull(statement, "Statement must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
@@ -379,11 +378,11 @@ public class ReactiveCassandraTemplate
Query queryToUse = query.columns(columns);
RegularStatement select = getStatementFactory().select(queryToUse, persistentEntity, tableName);
StatementBuilder<Select> select = getStatementFactory().select(queryToUse, persistentEntity, tableName);
Function<Row, T> mapper = getMapper(entityClass, returnType, tableName);
return getReactiveCqlOperations().query(select, (row, rowNum) -> mapper.apply(row));
return getReactiveCqlOperations().query(select.build(), (row, rowNum) -> mapper.apply(row));
}
/* (non-Javadoc)
@@ -407,9 +406,9 @@ public class ReactiveCassandraTemplate
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
RegularStatement select = getStatementFactory().select(query, getRequiredPersistentEntity(entityClass));
StatementBuilder<Select> select = getStatementFactory().select(query, getRequiredPersistentEntity(entityClass));
return slice(select, entityClass);
return slice(select.build(), entityClass);
}
/* (non-Javadoc)
@@ -429,10 +428,10 @@ public class ReactiveCassandraTemplate
Mono<WriteResult> doUpdate(Query query, org.springframework.data.cassandra.core.query.Update update,
Class<?> entityClass, CqlIdentifier tableName) {
RegularStatement statement = getStatementFactory().update(query, update, getRequiredPersistentEntity(entityClass),
tableName);
StatementBuilder<Update> statement = getStatementFactory().update(query, update,
getRequiredPersistentEntity(entityClass), tableName);
return getReactiveCqlOperations().execute(new StatementCallback(statement)).next();
return getReactiveCqlOperations().execute(new StatementCallback(statement.build())).next();
}
/* (non-Javadoc)
@@ -449,7 +448,10 @@ public class ReactiveCassandraTemplate
Mono<WriteResult> doDelete(Query query, Class<?> entityClass, CqlIdentifier tableName) {
RegularStatement delete = getStatementFactory().delete(query, getRequiredPersistentEntity(entityClass), tableName);
StatementBuilder<Delete> builder = getStatementFactory().delete(query, getRequiredPersistentEntity(entityClass),
tableName);
SimpleStatement delete = builder.build();
Mono<WriteResult> writeResult = getReactiveCqlOperations().execute(new StatementCallback(delete))
.doOnSubscribe(it -> maybeEmitEvent(new BeforeDeleteEvent<>(delete, entityClass, tableName))).next();
@@ -469,9 +471,7 @@ public class ReactiveCassandraTemplate
Assert.notNull(entityClass, "Entity type must not be null");
Select select = QueryBuilder.select().countAll().from(getTableName(entityClass).toCql());
return getReactiveCqlOperations().queryForObject(select, Long.class);
return doCount(Query.empty(), entityClass, getTableName(entityClass));
}
/* (non-Javadoc)
@@ -488,9 +488,10 @@ public class ReactiveCassandraTemplate
Mono<Long> doCount(Query query, Class<?> entityClass, CqlIdentifier tableName) {
RegularStatement count = getStatementFactory().count(query, getRequiredPersistentEntity(entityClass), tableName);
StatementBuilder<Select> count = getStatementFactory().count(query, getRequiredPersistentEntity(entityClass),
tableName);
return getReactiveCqlOperations().queryForObject(count, Long.class).switchIfEmpty(Mono.just(0L));
return getReactiveCqlOperations().queryForObject(count.build(), Long.class).switchIfEmpty(Mono.just(0L));
}
/* (non-Javadoc)
@@ -502,13 +503,10 @@ public class ReactiveCassandraTemplate
Assert.notNull(id, "Id must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
StatementBuilder<Select> builder = getStatementFactory().selectOneById(id, getConverter(),
getTableName(entityClass));
Select select = QueryBuilder.select().from(entity.getTableName().toCql());
getConverter().write(id, select.where(), entity);
return getReactiveCqlOperations().queryForRows(select).hasElements();
return getReactiveCqlOperations().queryForRows(builder.build()).hasElements();
}
/* (non-Javadoc)
@@ -525,10 +523,10 @@ public class ReactiveCassandraTemplate
Mono<Boolean> doExists(Query query, Class<?> entityClass, CqlIdentifier tableName) {
RegularStatement select = getStatementFactory().select(query.limit(1), getRequiredPersistentEntity(entityClass),
tableName);
StatementBuilder<Select> builder = getStatementFactory().select(query.limit(1),
getRequiredPersistentEntity(entityClass), tableName);
return getReactiveCqlOperations().queryForRows(select).hasElements();
return getReactiveCqlOperations().queryForRows(builder.build()).hasElements();
}
/* (non-Javadoc)
@@ -540,13 +538,10 @@ public class ReactiveCassandraTemplate
Assert.notNull(id, "Id must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
StatementBuilder<Select> builder = getStatementFactory().selectOneById(id, getConverter(),
getTableName(entityClass));
Select select = QueryBuilder.select().all().from(entity.getTableName().toCql());
getConverter().write(id, select.where(), entity);
return selectOne(select, entityClass);
return selectOne(builder.build(), entityClass);
}
/* (non-Javadoc)
@@ -579,15 +574,16 @@ public class ReactiveCassandraTemplate
T entityToUse = source.isVersionedEntity() ? source.initializeVersionProperty() : entityToInsert;
Insert insert = EntityQueryUtils.createInsertQuery(tableName.toCql(), entityToUse, options, getConverter(),
persistentEntity);
StatementBuilder<RegularInsert> builder = getStatementFactory().insert(entityToUse, options, persistentEntity,
tableName);
SimpleStatement insert = builder.build();
return source.isVersionedEntity() ? doInsertVersioned(insert.ifNotExists(), entityToUse, source, tableName)
return source.isVersionedEntity() ? doInsertVersioned(insert, entityToUse, source, tableName)
: doInsert(insert, entityToUse, tableName);
});
}
private <T> Mono<EntityWriteResult<T>> doInsertVersioned(Insert insert, T entity, AdaptibleEntity<T> source,
private <T> Mono<EntityWriteResult<T>> doInsertVersioned(Statement<?> insert, T entity, AdaptibleEntity<T> source,
CqlIdentifier tableName) {
return executeSave(entity, tableName, insert, (result, sink) -> {
@@ -605,7 +601,7 @@ public class ReactiveCassandraTemplate
});
}
private <T> Mono<EntityWriteResult<T>> doInsert(Insert insert, T entity, CqlIdentifier tableName) {
private <T> Mono<EntityWriteResult<T>> doInsert(Statement<?> insert, T entity, CqlIdentifier tableName) {
return executeSave(entity, tableName, insert);
}
@@ -644,9 +640,12 @@ public class ReactiveCassandraTemplate
Number previousVersion = source.getVersion();
T toSave = source.incrementVersion();
Update update = getStatementFactory().update(toSave, options, getConverter(), persistentEntity, tableName);
StatementBuilder<Update> builder = getStatementFactory().update(toSave, options, persistentEntity, tableName);
return executeSave(toSave, tableName, source.appendVersionCondition(update, previousVersion), (result, sink) -> {
source.appendVersionCondition(builder, previousVersion);
SimpleStatement update = builder.build();
return executeSave(toSave, tableName, update, (result, sink) -> {
if (!result.wasApplied()) {
@@ -664,9 +663,9 @@ public class ReactiveCassandraTemplate
private <T> Mono<EntityWriteResult<T>> doUpdate(T entity, UpdateOptions options, CqlIdentifier tableName,
CassandraPersistentEntity<?> persistentEntity) {
Update update = getStatementFactory().update(entity, options, getConverter(), persistentEntity, tableName);
StatementBuilder<Update> builder = getStatementFactory().update(entity, options, persistentEntity, tableName);
return executeSave(entity, tableName, update);
return executeSave(entity, tableName, builder.build());
}
/* (non-Javadoc)
@@ -690,16 +689,17 @@ public class ReactiveCassandraTemplate
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
CqlIdentifier tableName = persistentEntity.getTableName();
Delete delete = getStatementFactory().delete(entity, options, getConverter(), persistentEntity, tableName);
StatementBuilder<Delete> builder = getStatementFactory().delete(entity, options, getConverter(), tableName);
SimpleStatement delete = builder.build();
return source.isVersionedEntity() ? doDeleteVersioned(delete, entity, source, tableName)
: doDelete(delete, entity, tableName);
}
private Mono<WriteResult> doDeleteVersioned(Delete delete, Object entity, AdaptibleEntity<Object> source,
private Mono<WriteResult> doDeleteVersioned(Statement<?> delete, Object entity, AdaptibleEntity<Object> source,
CqlIdentifier tableName) {
return executeDelete(entity, tableName, source.appendVersionCondition(delete), (result, sink) -> {
return executeDelete(entity, tableName, delete, (result, sink) -> {
if (!result.wasApplied()) {
@@ -714,7 +714,7 @@ public class ReactiveCassandraTemplate
});
}
private Mono<WriteResult> doDelete(Delete delete, Object entity, CqlIdentifier tableName) {
private Mono<WriteResult> doDelete(Statement<?> delete, Object entity, CqlIdentifier tableName) {
return executeDelete(entity, tableName, delete, (result, sink) -> sink.next(result));
}
@@ -729,9 +729,9 @@ public class ReactiveCassandraTemplate
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
CqlIdentifier tableName = entity.getTableName();
Delete delete = QueryBuilder.delete().from(tableName.toCql());
getConverter().write(id, delete.where(), entity);
StatementBuilder<Delete> builder = getStatementFactory().deleteById(id, getConverter(), tableName);
SimpleStatement delete = builder.build();
Mono<Boolean> result = getReactiveCqlOperations().execute(delete)
.doOnSubscribe(it -> maybeEmitEvent(new BeforeDeleteEvent<>(delete, entityClass, tableName)));
@@ -748,12 +748,13 @@ public class ReactiveCassandraTemplate
Assert.notNull(entityClass, "Entity type must not be null");
CqlIdentifier tableName = getTableName(entityClass);
Truncate truncate = QueryBuilder.truncate(tableName.toCql());
Truncate truncate = QueryBuilder.truncate(tableName);
SimpleStatement statement = truncate.build();
Mono<Boolean> result = getReactiveCqlOperations().execute(truncate)
.doOnSubscribe(it -> maybeEmitEvent(new BeforeDeleteEvent<>(truncate, entityClass, tableName)));
Mono<Boolean> result = getReactiveCqlOperations().execute(statement)
.doOnSubscribe(it -> maybeEmitEvent(new BeforeDeleteEvent<>(statement, entityClass, tableName)));
return result.doOnNext(it -> maybeEmitEvent(new AfterDeleteEvent<>(truncate, entityClass, tableName))).then();
return result.doOnNext(it -> maybeEmitEvent(new AfterDeleteEvent<>(statement, entityClass, tableName))).then();
}
// -------------------------------------------------------------------------
@@ -796,11 +797,11 @@ public class ReactiveCassandraTemplate
// Implementation hooks and utility methods
// -------------------------------------------------------------------------
private <T> Mono<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName, Statement statement) {
private <T> Mono<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName, Statement<?> statement) {
return executeSave(entity, tableName, statement, (writeResult, sink) -> sink.next(writeResult));
}
private <T> Mono<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName, Statement statement,
private <T> Mono<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName, Statement<?> statement,
BiConsumer<EntityWriteResult<T>, SynchronousSink<EntityWriteResult<T>>> handler) {
return Mono.defer(() -> {
@@ -817,7 +818,7 @@ public class ReactiveCassandraTemplate
}
private Mono<WriteResult> executeDelete(Object entity, CqlIdentifier tableName, Statement statement,
private Mono<WriteResult> executeDelete(Object entity, CqlIdentifier tableName, Statement<?> statement,
BiConsumer<WriteResult, SynchronousSink<WriteResult>> handler) {
maybeEmitEvent(new BeforeDeleteEvent<>(statement, entity.getClass(), tableName));
@@ -830,10 +831,14 @@ public class ReactiveCassandraTemplate
.next();
}
private Mono<Integer> getEffectiveFetchSize(Statement statement) {
private int getConfiguredPageSize(DriverContext context) {
return context.getConfig().getDefaultProfile().getInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 5000);
}
if (statement.getFetchSize() > 0) {
return Mono.just(statement.getFetchSize());
private Mono<Integer> getEffectiveFetchSize(Statement<?> statement) {
if (statement.getPageSize() > 0) {
return Mono.just(statement.getPageSize());
}
if (getReactiveCqlOperations() instanceof CassandraAccessor) {
@@ -843,8 +848,9 @@ public class ReactiveCassandraTemplate
}
}
return getReactiveCqlOperations().execute((ReactiveSessionCallback<Integer>) session -> Mono
.just(session.getCluster().getConfiguration().getQueryOptions().getFetchSize())).single();
return getReactiveCqlOperations()
.execute((ReactiveSessionCallback<Integer>) session -> Mono.just(getConfiguredPageSize(session.getContext())))
.single();
}
@SuppressWarnings("unchecked")
@@ -897,7 +903,7 @@ public class ReactiveCassandraTemplate
return Mono.just(object);
}
protected <T> Mono<T> maybeCallBeforeSave(T object, CqlIdentifier tableName, Statement statement) {
protected <T> Mono<T> maybeCallBeforeSave(T object, CqlIdentifier tableName, Statement<?> statement) {
if (null != entityCallbacks) {
return entityCallbacks.callback(ReactiveBeforeSaveCallback.class, object, tableName, statement);
@@ -909,7 +915,7 @@ public class ReactiveCassandraTemplate
@Value
static class StatementCallback implements ReactiveSessionCallback<WriteResult>, CqlProvider {
@lombok.NonNull Statement statement;
@lombok.NonNull Statement<?> statement;
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.ReactiveSessionCallback#doInSession(org.springframework.data.cassandra.ReactiveSession)

View File

@@ -17,10 +17,11 @@ package org.springframework.data.cassandra.core;
import reactor.core.publisher.Mono;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* The {@link ReactiveDeleteOperation} interface allows creation and execution of Cassandra {@code DELETE} operations in
* a fluent API style.
@@ -76,7 +77,7 @@ public interface ReactiveDeleteOperation {
Assert.hasText(table, "Table name must not be null or empty");
return inTable(CqlIdentifier.of(table));
return inTable(CqlIdentifier.fromCql(table));
}
/**

View File

@@ -21,11 +21,12 @@ import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Mono;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Implementation of {@link ReactiveDeleteOperation}.
*

View File

@@ -17,9 +17,10 @@ package org.springframework.data.cassandra.core;
import reactor.core.publisher.Mono;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* The {@link ReactiveInsertOperation} interface allows creation and execution of Cassandra {@code INSERT} operations in
* a fluent API style.
@@ -73,7 +74,7 @@ public interface ReactiveInsertOperation {
Assert.hasText(table, "Table must not be null or empty");
return inTable(CqlIdentifier.of(table));
return inTable(CqlIdentifier.fromCql(table));
}
/**

View File

@@ -21,10 +21,11 @@ import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Mono;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Implementation of {@link ReactiveInsertOperation}.
*

View File

@@ -18,10 +18,11 @@ package org.springframework.data.cassandra.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* The {@link ReactiveSelectOperation} interface allows creation and execution of Cassandra {@code SELECT} operations in
* a fluent API style.
@@ -83,7 +84,7 @@ public interface ReactiveSelectOperation {
Assert.hasText(table, "Table name must not be null or empty");
return inTable(CqlIdentifier.of(table));
return inTable(CqlIdentifier.fromCql(table));
}
/**

View File

@@ -23,11 +23,12 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Implementation of {@link ReactiveSelectOperation}.
*

View File

@@ -17,11 +17,12 @@ package org.springframework.data.cassandra.core;
import reactor.core.publisher.Mono;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* The {@link ReactiveUpdateOperation} interface allows creation and execution of Cassandra {@code UPDATE} operations in
* a fluent API style.
@@ -81,7 +82,7 @@ public interface ReactiveUpdateOperation {
Assert.hasText(table, "Table name must not be null or empty");
return inTable(CqlIdentifier.of(table));
return inTable(CqlIdentifier.fromCql(table));
}
/**

View File

@@ -21,12 +21,13 @@ import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Mono;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Implementation of {@link ReactiveUpdateOperation}.
*

View File

@@ -21,9 +21,9 @@ import java.util.List;
import org.springframework.util.Assert;
import com.datastax.driver.core.ExecutionInfo;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.oss.driver.api.core.cql.ExecutionInfo;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
/**
* The result of a write operation.
@@ -49,7 +49,7 @@ public class WriteResult {
WriteResult(ResultSet resultSet) {
this.executionInfo = resultSet.getAllExecutionInfo();
this.executionInfo = resultSet.getExecutionInfos();
this.wasApplied = resultSet.wasApplied();
int limit = resultSet.getAvailableWithoutFetching();

View File

@@ -15,14 +15,11 @@
*/
package org.springframework.data.cassandra.core.convert;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.Row;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* {@link CassandraValueProvider} to read property values from a {@link Row}.
@@ -31,12 +28,10 @@ import com.datastax.driver.core.Row;
* @author Matthew T. Adams
* @author David Webb
* @author Mark Paluch
* @deprecated since 3.0, use directly {@link RowValueProvider}.
*/
public class BasicCassandraRowValueProvider implements CassandraRowValueProvider {
private final ColumnReader reader;
private final SpELExpressionEvaluator evaluator;
@Deprecated
public class BasicCassandraRowValueProvider extends RowValueProvider {
/**
* Create a new {@link BasicCassandraRowValueProvider} with the given {@link Row}, {@link CodecRegistry} and
@@ -48,13 +43,7 @@ public class BasicCassandraRowValueProvider implements CassandraRowValueProvider
* @since 2.1
*/
public BasicCassandraRowValueProvider(Row source, CodecRegistry codecRegistry, SpELExpressionEvaluator evaluator) {
Assert.notNull(source, "Source Row must not be null");
Assert.notNull(codecRegistry, "CodecRegistry must not be null");
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null");
this.reader = new ColumnReader(source, codecRegistry);
this.evaluator = evaluator;
super(source, evaluator);
}
/**
@@ -67,35 +56,7 @@ public class BasicCassandraRowValueProvider implements CassandraRowValueProvider
*/
@Deprecated
public BasicCassandraRowValueProvider(Row source, DefaultSpELExpressionEvaluator evaluator) {
this(source, CodecRegistry.DEFAULT_INSTANCE, evaluator);
super(source, evaluator);
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@Nullable
@Override
@SuppressWarnings("unchecked")
public <T> T getPropertyValue(CassandraPersistentProperty property) {
String spelExpression = property.getSpelExpression();
return spelExpression != null ? this.evaluator.evaluate(spelExpression)
: (T) this.reader.get(property.getRequiredColumnName());
}
public Row getRow() {
return this.reader.getRow();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.convert.CassandraValueProvider#hasProperty(org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty)
*/
@Override
public boolean hasProperty(CassandraPersistentProperty property) {
Assert.notNull(property, "CassandraPersistentProperty must not be null");
return this.reader.contains(property.getRequiredColumnName());
}
}

View File

@@ -32,8 +32,6 @@ import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.datastax.driver.core.DataType.Name;
/**
* Value object to capture custom conversion. {@link CassandraCustomConversions} also act as factory for
* {@link SimpleTypeHolder}
@@ -75,7 +73,7 @@ public class CassandraCustomConversions extends org.springframework.data.convert
CassandraType annotation = AnnotatedElementUtils.getMergedAnnotation(it, CassandraType.class);
return annotation != null && annotation.type() == Name.TIME;
return annotation != null && annotation.type() == CassandraSimpleTypeHolder.Name.TIME;
}) //
.map(it -> {

View File

@@ -20,16 +20,15 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.cassandra.core.mapping.CassandraSimpleTypeHolder;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.util.ClassUtils;
import com.datastax.driver.core.DataType.Name;
/**
* Helper class to register JSR-310 specific {@link Converter} implementations to convert between Cassandra types in
* case the library is present on the classpath.
@@ -60,47 +59,12 @@ public abstract class CassandraJodaTimeConverters {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(CassandraLocalDateToLocalDateConverter.INSTANCE);
converters.add(LocalDateToCassandraLocalDateConverter.INSTANCE);
converters.add(MillisOfDayToLocalTimeConverter.INSTANCE);
converters.add(LocalTimeToMillisOfDayConverter.INSTANCE);
return converters;
}
/**
* Simple singleton to convert {@link com.datastax.driver.core.LocalDate}s to their {@link LocalDate} representation.
*
* @author Mark Paluch
*/
public enum CassandraLocalDateToLocalDateConverter
implements Converter<com.datastax.driver.core.LocalDate, LocalDate> {
INSTANCE;
@Override
public LocalDate convert(com.datastax.driver.core.LocalDate source) {
return new LocalDate(source.getYear(), source.getMonth(), source.getDay());
}
}
/**
* Simple singleton to convert {@link LocalDate}s to their {@link com.datastax.driver.core.LocalDate} representation.
*
* @author Mark Paluch
*/
public enum LocalDateToCassandraLocalDateConverter
implements Converter<LocalDate, com.datastax.driver.core.LocalDate> {
INSTANCE;
@Override
public com.datastax.driver.core.LocalDate convert(LocalDate source) {
return com.datastax.driver.core.LocalDate.fromYearMonthDay(source.getYear(), source.getMonthOfYear(),
source.getDayOfMonth());
}
}
/**
* Simple singleton to convert {@link Long}s to their {@link LocalTime} representation.
*
@@ -123,7 +87,7 @@ public abstract class CassandraJodaTimeConverters {
* @author Mark Paluch
*/
@WritingConverter
@CassandraType(type = Name.TIME)
@CassandraType(type = CassandraSimpleTypeHolder.Name.TIME)
public enum LocalTimeToMillisOfDayConverter implements Converter<LocalTime, Long> {
INSTANCE;

View File

@@ -15,21 +15,18 @@
*/
package org.springframework.data.cassandra.core.convert;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.cassandra.core.mapping.CassandraSimpleTypeHolder;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import com.datastax.driver.core.DataType.Name;
/**
* Helper class to register JodaTime specific {@link Converter} implementations in case the library is present on the
* classpath.
@@ -51,49 +48,12 @@ public abstract class CassandraJsr310Converters {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(CassandraLocalDateToLocalDateConverter.INSTANCE);
converters.add(LocalDateToCassandraLocalDateConverter.INSTANCE);
converters.add(MillisOfDayToLocalTimeConverter.INSTANCE);
converters.add(LocalTimeToMillisOfDayConverter.INSTANCE);
return converters;
}
/**
* Simple singleton to convert {@link com.datastax.driver.core.LocalDate}s to their {@link LocalDate} representation.
*
* @author Mark Paluch
*/
@ReadingConverter
public enum CassandraLocalDateToLocalDateConverter
implements Converter<com.datastax.driver.core.LocalDate, LocalDate> {
INSTANCE;
@Override
public LocalDate convert(com.datastax.driver.core.LocalDate source) {
return LocalDate.of(source.getYear(), source.getMonth(), source.getDay());
}
}
/**
* Simple singleton to convert {@link LocalDate}s to their {@link com.datastax.driver.core.LocalDate} representation.
*
* @author Mark Paluch
*/
@WritingConverter
public enum LocalDateToCassandraLocalDateConverter
implements Converter<LocalDate, com.datastax.driver.core.LocalDate> {
INSTANCE;
@Override
public com.datastax.driver.core.LocalDate convert(LocalDate source) {
return com.datastax.driver.core.LocalDate.fromYearMonthDay(source.getYear(), source.getMonthValue(),
source.getDayOfMonth());
}
}
/**
* Simple singleton to convert {@link Long}s to their {@link LocalTime} representation.
*
@@ -118,7 +78,7 @@ public abstract class CassandraJsr310Converters {
* @since 2.1
*/
@WritingConverter
@CassandraType(type = Name.TIME)
@CassandraType(type = CassandraSimpleTypeHolder.Name.TIME)
public enum LocalTimeToMillisOfDayConverter implements Converter<LocalTime, Long> {
INSTANCE;

View File

@@ -15,10 +15,8 @@
*/
package org.springframework.data.cassandra.core.convert;
import com.datastax.driver.core.Row;
/**
* {@link CassandraValueProvider} providing values based on a {@link Row}.
* {@link CassandraValueProvider} providing values based on a {@link com.datastax.oss.driver.api.core.cql.Row}.
*
* @author Matthew T. Adams
* @author Mark Paluch

View File

@@ -22,17 +22,16 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.cassandra.core.mapping.CassandraSimpleTypeHolder;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.ThreeTenBackPortConverters;
import org.springframework.data.convert.WritingConverter;
import org.springframework.util.ClassUtils;
import org.threeten.bp.LocalDate;
import org.threeten.bp.LocalTime;
import org.threeten.bp.temporal.ChronoField;
import com.datastax.driver.core.DataType.Name;
/**
* Helper class to register {@link Converter} implementations for the ThreeTen Backport project in case it's present on
* the classpath.
@@ -65,47 +64,12 @@ public abstract class CassandraThreeTenBackPortConverters {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(CassandraLocalDateToLocalDateConverter.INSTANCE);
converters.add(LocalDateToCassandraLocalDateConverter.INSTANCE);
converters.add(MillisOfDayToLocalTimeConverter.INSTANCE);
converters.add(LocalTimeToMillisOfDayConverter.INSTANCE);
return converters;
}
/**
* Simple singleton to convert {@link com.datastax.driver.core.LocalDate}s to their {@link LocalDate} representation.
*
* @author Mark Paluch
*/
public enum CassandraLocalDateToLocalDateConverter
implements Converter<com.datastax.driver.core.LocalDate, LocalDate> {
INSTANCE;
@Override
public LocalDate convert(com.datastax.driver.core.LocalDate source) {
return LocalDate.of(source.getYear(), source.getMonth(), source.getDay());
}
}
/**
* Simple singleton to convert {@link LocalDate}s to their {@link com.datastax.driver.core.LocalDate} representation.
*
* @author Mark Paluch
*/
public enum LocalDateToCassandraLocalDateConverter
implements Converter<LocalDate, com.datastax.driver.core.LocalDate> {
INSTANCE;
@Override
public com.datastax.driver.core.LocalDate convert(LocalDate source) {
return com.datastax.driver.core.LocalDate.fromYearMonthDay(source.getYear(), source.getMonthValue(),
source.getDayOfMonth());
}
}
/**
* Simple singleton to convert {@link Long}s to their {@link LocalTime} representation.
*
@@ -130,7 +94,7 @@ public abstract class CassandraThreeTenBackPortConverters {
* @since 2.1
*/
@WritingConverter
@CassandraType(type = Name.TIME)
@CassandraType(type = CassandraSimpleTypeHolder.Name.TIME)
public enum LocalTimeToMillisOfDayConverter implements Converter<LocalTime, Long> {
INSTANCE;

View File

@@ -15,28 +15,20 @@
*/
package org.springframework.data.cassandra.core.convert;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.TupleValue;
import com.datastax.oss.driver.api.core.data.TupleValue;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* {@link CassandraValueProvider} to read property values from a {@link TupleValue}.
*
* @author Mark Paluch
* @since 2.1
* @deprecated since 3.0, use {@link TupleValueProvider} directly.
*/
public class CassandraTupleValueProvider implements CassandraValueProvider {
private final CodecRegistry codecRegistry;
private final SpELExpressionEvaluator evaluator;
private final TupleValue tupleValue;
@Deprecated
public class CassandraTupleValueProvider extends TupleValueProvider {
/**
* Create a new {@link CassandraTupleValueProvider} with the given {@link TupleValue} and
@@ -48,40 +40,6 @@ public class CassandraTupleValueProvider implements CassandraValueProvider {
*/
public CassandraTupleValueProvider(TupleValue tupleValue, CodecRegistry codecRegistry,
SpELExpressionEvaluator evaluator) {
Assert.notNull(tupleValue, "TupleValue must not be null");
Assert.notNull(codecRegistry, "CodecRegistry must not be null");
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null");
this.tupleValue = tupleValue;
this.codecRegistry = codecRegistry;
this.evaluator = evaluator;
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@Nullable
@Override
public <T> T getPropertyValue(CassandraPersistentProperty property) {
String spelExpression = property.getSpelExpression();
if (spelExpression != null) {
return evaluator.evaluate(spelExpression);
}
int ordinal = property.getRequiredOrdinal();
DataType elementType = tupleValue.getType().getComponentTypes().get(ordinal);
return tupleValue.get(ordinal, codecRegistry.codecFor(elementType));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.convert.CassandraValueProvider#hasProperty(org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty)
*/
@Override
public boolean hasProperty(CassandraPersistentProperty property) {
return this.tupleValue.getType().getComponentTypes().size() >= property.getRequiredOrdinal();
super(tupleValue, evaluator);
}
}

View File

@@ -15,47 +15,32 @@
*/
package org.springframework.data.cassandra.core.convert;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.UDTValue;
import com.datastax.oss.driver.api.core.data.UdtValue;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* {@link CassandraValueProvider} to read property values from a {@link UDTValue}.
* {@link CassandraValueProvider} to read property values from a {@link UdtValue}.
*
* @author Mark Paluch
* @since 1.5
* @deprecated since 3.0, use {@link UdtValueProvider} directly.
*/
public class CassandraUDTValueProvider implements CassandraValueProvider {
private final UDTValue udtValue;
private final CodecRegistry codecRegistry;
private final SpELExpressionEvaluator evaluator;
@Deprecated
public class CassandraUDTValueProvider extends UdtValueProvider {
/**
* Create a new {@link CassandraUDTValueProvider} with the given {@link UDTValue} and {@link SpELExpressionEvaluator}.
* Create a new {@link CassandraUDTValueProvider} with the given {@link UdtValue} and {@link SpELExpressionEvaluator}.
*
* @param udtValue must not be {@literal null}.
* @param codecRegistry must not be {@literal null}.
* @param evaluator must not be {@literal null}.
* @since 2.1
*/
public CassandraUDTValueProvider(UDTValue udtValue, CodecRegistry codecRegistry, SpELExpressionEvaluator evaluator) {
Assert.notNull(udtValue, "UDTValue must not be null");
Assert.notNull(codecRegistry, "CodecRegistry must not be null");
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null");
this.udtValue = udtValue;
this.codecRegistry = codecRegistry;
this.evaluator = evaluator;
public CassandraUDTValueProvider(UdtValue udtValue, CodecRegistry codecRegistry, SpELExpressionEvaluator evaluator) {
super(udtValue, evaluator);
}
/**
@@ -65,38 +50,11 @@ public class CassandraUDTValueProvider implements CassandraValueProvider {
* @param udtValue must not be {@literal null}.
* @param codecRegistry must not be {@literal null}.
* @param evaluator must not be {@literal null}.
* @deprecated since 2.1, use {@link #CassandraUDTValueProvider(UDTValue, CodecRegistry, SpELExpressionEvaluator)}
* @deprecated since 2.1, use {@link #CassandraUDTValueProvider(UdtValue, CodecRegistry, SpELExpressionEvaluator)}
*/
@Deprecated
public CassandraUDTValueProvider(UDTValue udtValue, CodecRegistry codecRegistry,
public CassandraUDTValueProvider(UdtValue udtValue, CodecRegistry codecRegistry,
DefaultSpELExpressionEvaluator evaluator) {
this(udtValue, codecRegistry, (SpELExpressionEvaluator) evaluator);
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@Nullable
public <T> T getPropertyValue(CassandraPersistentProperty property) {
String spelExpression = property.getSpelExpression();
if (spelExpression != null) {
return this.evaluator.evaluate(spelExpression);
}
String name = property.getRequiredColumnName().toCql();
DataType fieldType = this.udtValue.getType().getFieldType(name);
return this.udtValue.get(name, this.codecRegistry.codecFor(fieldType));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.convert.CassandraValueProvider#hasProperty(org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty)
*/
@Override
public boolean hasProperty(CassandraPersistentProperty property) {
return this.udtValue.getType().contains(property.getRequiredColumnName().toCql());
super(udtValue, evaluator);
}
}

View File

@@ -60,20 +60,15 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.TupleType;
import com.datastax.driver.core.TupleValue;
import com.datastax.driver.core.TypeCodec;
import com.datastax.driver.core.UDTValue;
import com.datastax.driver.core.UserType;
import com.datastax.driver.core.querybuilder.Clause;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Update;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.data.TupleValue;
import com.datastax.oss.driver.api.core.data.UdtValue;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.TupleType;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* {@link CassandraConverter} that uses a {@link MappingContext} to do sophisticated mapping of domain objects to
@@ -178,11 +173,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
private <S> ConvertingPropertyAccessor<S> newConvertingPropertyAccessor(S source,
CassandraPersistentEntity<?> entity) {
PersistentPropertyAccessor propertyAccessor = source instanceof PersistentPropertyAccessor
PersistentPropertyAccessor<S> propertyAccessor = source instanceof PersistentPropertyAccessor
? (PersistentPropertyAccessor) source
: entity.getPropertyAccessor(source);
return new ConvertingPropertyAccessor<>(propertyAccessor, getConversionService());
return new ConvertingPropertyAccessor<S>(propertyAccessor, getConversionService());
}
private <S> PersistentEntityParameterValueProvider<CassandraPersistentProperty> newParameterValueProvider(
@@ -233,10 +228,6 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
return (R) row;
}
if (com.datastax.oss.driver.api.core.cql.Row.class.isAssignableFrom(rawType)) {
return (R) row;
}
if (getCustomConversions().hasCustomReadTarget(Row.class, rawType)
|| getConversionService().canConvert(Row.class, rawType)) {
@@ -256,32 +247,23 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
CassandraPersistentEntity<R> persistentEntity = (CassandraPersistentEntity<R>) getMappingContext()
.getRequiredPersistentEntity(typeInfo);
if (row instanceof Row) {
return readEntityFromRow(persistentEntity, row);
}
return readEntityFromRow(persistentEntity, row);
return readEntityFromRow(persistentEntity, (com.datastax.oss.driver.api.core.cql.Row) row);
}
private <S> S readEntityFromRow(CassandraPersistentEntity<S> entity, com.datastax.oss.driver.api.core.cql.Row row) {
return doReadEntity(entity, row, expressionEvaluator -> new RowValueProvider(row, expressionEvaluator));
}
private <S> S readEntityFromRow(CassandraPersistentEntity<S> entity, Row row) {
return doReadEntity(entity, row,
expressionEvaluator -> new BasicCassandraRowValueProvider(row, getCodecRegistry(), expressionEvaluator));
return doReadEntity(entity, row, expressionEvaluator -> new RowValueProvider(row, expressionEvaluator));
}
private <S> S readEntityFromTuple(CassandraPersistentEntity<S> entity, TupleValue tupleValue) {
return doReadEntity(entity, tupleValue,
expressionEvaluator -> new CassandraTupleValueProvider(tupleValue, getCodecRegistry(), expressionEvaluator));
expressionEvaluator -> new TupleValueProvider(tupleValue, expressionEvaluator));
}
private <S> S readEntityFromUdt(CassandraPersistentEntity<S> entity, UDTValue udtValue) {
private <S> S readEntityFromUdt(CassandraPersistentEntity<S> entity, UdtValue udtValue) {
return doReadEntity(entity, udtValue,
expressionEvaluator -> new CassandraUDTValueProvider(udtValue, getCodecRegistry(), expressionEvaluator));
return doReadEntity(entity, udtValue, expressionEvaluator -> new UdtValueProvider(udtValue, expressionEvaluator));
}
private <S> S doReadEntity(CassandraPersistentEntity<S> entity, Object value,
@@ -376,26 +358,20 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
throw new MappingException("No mapping metadata found for " + source.getClass());
}
if (sink instanceof Map) {
writeMapFromWrapper(newConvertingPropertyAccessor(source, entity), (Map<String, Object>) sink, entity);
} else if (sink instanceof Insert) {
writeInsertFromWrapper(newConvertingPropertyAccessor(source, entity), (Insert) sink, entity);
} else if (sink instanceof Update) {
writeUpdateFromWrapper(newConvertingPropertyAccessor(source, entity), (Update) sink, entity);
} else if (sink instanceof Select.Where) {
writeSelectWhereFromObject(source, (Select.Where) sink, entity);
} else if (sink instanceof Delete.Where) {
writeDeleteWhereFromObject(source, (Delete.Where) sink, entity);
if (sink instanceof Where) {
writeWhereFromObject(source, (Where) sink, entity);
} else if (sink instanceof Map) {
writeMapFromWrapper(newConvertingPropertyAccessor(source, entity), (Map<CqlIdentifier, Object>) sink, entity);
} else if (sink instanceof TupleValue) {
writeTupleValue(newConvertingPropertyAccessor(source, entity), (TupleValue) sink, entity);
} else if (sink instanceof UDTValue) {
writeUDTValue(newConvertingPropertyAccessor(source, entity), (UDTValue) sink, entity);
} else if (sink instanceof UdtValue) {
writeUDTValue(newConvertingPropertyAccessor(source, entity), (UdtValue) sink, entity);
} else {
throw new MappingException(String.format("Unknown write target [%s]", ObjectUtils.nullSafeClassName(sink)));
}
}
private void writeMapFromWrapper(ConvertingPropertyAccessor accessor, Map<String, Object> insert,
private void writeMapFromWrapper(ConvertingPropertyAccessor<?> accessor, Map<CqlIdentifier, Object> sink,
CassandraPersistentEntity<?> entity) {
for (CassandraPersistentProperty property : entity) {
@@ -406,46 +382,10 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
log.debug("doWithProperties Property.type {}, Property.value {}", property.getType().getName(), value);
}
if (property.isCompositePrimaryKey()) {
if (log.isDebugEnabled()) {
log.debug("Property is a compositeKey");
}
if (value == null) {
continue;
}
CassandraPersistentEntity<?> compositePrimaryKey = getMappingContext().getRequiredPersistentEntity(property);
writeMapFromWrapper(newConvertingPropertyAccessor(value, compositePrimaryKey), insert, compositePrimaryKey);
continue;
}
if (log.isDebugEnabled()) {
log.debug("Adding map.entry [{}] - [{}]", property.getRequiredColumnName().toCql(), value);
}
insert.put(property.getRequiredColumnName().toCql(), value);
}
}
private void writeInsertFromWrapper(ConvertingPropertyAccessor propertyAccessor, Insert insert,
CassandraPersistentEntity<?> entity) {
for (CassandraPersistentProperty property : entity) {
if (!property.isWritable()) {
continue;
}
Object value = getWriteValue(property, propertyAccessor);
if (log.isDebugEnabled()) {
log.debug("doWithProperties Property.type {}, Property.value {}", property.getType().getName(), value);
}
if (property.isCompositePrimaryKey()) {
if (log.isDebugEnabled()) {
@@ -458,88 +398,20 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
CassandraPersistentEntity<?> compositePrimaryKey = getMappingContext().getRequiredPersistentEntity(property);
writeInsertFromWrapper(newConvertingPropertyAccessor(value, compositePrimaryKey), insert, compositePrimaryKey);
writeMapFromWrapper(newConvertingPropertyAccessor(value, compositePrimaryKey), sink, compositePrimaryKey);
continue;
}
if (value == null) {
continue;
}
if (log.isDebugEnabled()) {
log.debug("Adding insert.value [{}] - [{}]", property.getRequiredColumnName().toCql(), value);
log.debug("Adding map.entry [{}] - [{}]", property.getRequiredColumnName(), value);
}
insert.value(property.getRequiredColumnName().toCql(), value);
sink.put(property.getRequiredColumnName(), value);
}
}
private void writeUpdateFromWrapper(ConvertingPropertyAccessor propertyAccessor, Update update,
CassandraPersistentEntity<?> entity) {
for (CassandraPersistentProperty property : entity) {
if (!property.isWritable()) {
continue;
}
Object value = getWriteValue(property, propertyAccessor);
if (property.isCompositePrimaryKey()) {
CassandraPersistentEntity<?> compositePrimaryKey = getMappingContext().getRequiredPersistentEntity(property);
if (value == null) {
continue;
}
writeUpdateFromWrapper(newConvertingPropertyAccessor(value, compositePrimaryKey), update, compositePrimaryKey);
continue;
}
if (isPrimaryKeyPart(property)) {
update.where(QueryBuilder.eq(property.getRequiredColumnName().toCql(), value));
} else {
update.with(QueryBuilder.set(property.getRequiredColumnName().toCql(), value));
}
}
}
/**
* Returns whether the property is part of the primary key.
*
* @param property {@link CassandraPersistentProperty} to evaluate.
* @return a boolean value indicating whether the given property is party of a primary key.
*/
private boolean isPrimaryKeyPart(CassandraPersistentProperty property) {
return property.isCompositePrimaryKey() || property.isPrimaryKeyColumn() || property.isIdProperty();
}
private void writeSelectWhereFromObject(Object object, Select.Where where, CassandraPersistentEntity<?> entity) {
getWhereClauses(object, entity).forEach(where::and);
}
private void writeDeleteWhereFromObject(Object object, Delete.Where where, CassandraPersistentEntity<?> entity) {
getWhereClauses(object, entity).forEach(where::and);
}
@Nullable
private Object extractId(Object source, CassandraPersistentEntity<?> entity) {
if (ClassUtils.isAssignableValue(entity.getType(), source)) {
return getId(source, entity);
} else if (source instanceof MapId) {
return source;
} else if (source instanceof MapIdentifiable) {
return ((MapIdentifiable) source).getMapId();
}
return source;
}
private Collection<Clause> getWhereClauses(Object source, CassandraPersistentEntity<?> entity) {
private void writeWhereFromObject(Object source, Where sink, CassandraPersistentEntity<?> entity) {
Assert.notNull(source, "Id source must not be null");
@@ -560,7 +432,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
? getMappingContext().getRequiredPersistentEntity(compositeIdProperty)
: entity;
return getWhereClauses(MapId.class.cast(id), whereEntity);
writeWhere(MapId.class.cast(id), sink, whereEntity);
return;
}
if (idProperty == null) {
@@ -578,21 +451,33 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
CassandraPersistentEntity<?> compositePrimaryKey = getMappingContext()
.getRequiredPersistentEntity(compositeIdProperty);
return getWhereClauses(newConvertingPropertyAccessor(id, compositePrimaryKey), compositePrimaryKey);
writeWhere(newConvertingPropertyAccessor(id, compositePrimaryKey), sink, compositePrimaryKey);
return;
}
Class<?> targetType = getTargetType(idProperty);
return Collections.singleton(QueryBuilder.eq(idProperty.getRequiredColumnName().toCql(),
getPotentiallyConvertedSimpleValue(id, targetType)));
sink.put(idProperty.getRequiredColumnName(), getPotentiallyConvertedSimpleValue(id, targetType));
}
private Collection<Clause> getWhereClauses(MapId id, CassandraPersistentEntity<?> entity) {
@Nullable
private Object extractId(Object source, CassandraPersistentEntity<?> entity) {
if (ClassUtils.isAssignableValue(entity.getType(), source)) {
return getId(source, entity);
} else if (source instanceof MapId) {
return source;
} else if (source instanceof MapIdentifiable) {
return ((MapIdentifiable) source).getMapId();
}
return source;
}
private void writeWhere(MapId id, Where sink, CassandraPersistentEntity<?> entity) {
Assert.notNull(id, "MapId must not be null");
Collection<Clause> clauses = new ArrayList<>();
for (Entry<String, Object> entry : id.entrySet()) {
CassandraPersistentProperty persistentProperty = entity.getPersistentProperty(entry.getKey());
@@ -604,29 +489,23 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
Object writeValue = getWriteValue(entry.getValue(), persistentProperty.getTypeInformation());
clauses.add(QueryBuilder.eq(persistentProperty.getRequiredColumnName().toCql(), writeValue));
sink.put(persistentProperty.getRequiredColumnName(), writeValue);
}
return clauses;
}
private Collection<Clause> getWhereClauses(ConvertingPropertyAccessor accessor, CassandraPersistentEntity<?> entity) {
private void writeWhere(ConvertingPropertyAccessor<?> accessor, Where sink, CassandraPersistentEntity<?> entity) {
Assert.isTrue(entity.isCompositePrimaryKey(),
String.format("Entity [%s] is not a composite primary key", entity.getName()));
Collection<Clause> clauses = new ArrayList<>();
for (CassandraPersistentProperty property : entity) {
TypeCodec<Object> codec = getCodec(property);
Object value = accessor.getProperty(property, codec.getJavaType().getRawType());
clauses.add(QueryBuilder.eq(property.getRequiredColumnName().toCql(), value));
sink.put(property.getRequiredColumnName(), value);
}
return clauses;
}
private void writeTupleValue(ConvertingPropertyAccessor propertyAccessor, TupleValue tupleValue,
private void writeTupleValue(ConvertingPropertyAccessor<?> propertyAccessor, TupleValue tupleValue,
CassandraPersistentEntity<?> entity) {
for (CassandraPersistentProperty property : entity) {
@@ -647,7 +526,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
}
private void writeUDTValue(ConvertingPropertyAccessor propertyAccessor, UDTValue udtValue,
private void writeUDTValue(ConvertingPropertyAccessor<?> propertyAccessor, UdtValue udtValue,
CassandraPersistentEntity<?> entity) {
for (CassandraPersistentProperty property : entity) {
@@ -664,12 +543,12 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
if (log.isDebugEnabled()) {
log.debug("Adding udt.value [{}] - [{}]", property.getRequiredColumnName().toCql(), value);
log.debug("Adding udt.value [{}] - [{}]", property.getRequiredColumnName(), value);
}
TypeCodec<Object> typeCodec = getCodec(property);
udtValue.set(property.getRequiredColumnName().toCql(), value, typeCodec);
udtValue.set(property.getRequiredColumnName().toString(), value, typeCodec);
}
}
@@ -680,7 +559,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
Assert.notNull(object, "Object instance must not be null");
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
ConvertingPropertyAccessor propertyAccessor = newConvertingPropertyAccessor(object, entity);
ConvertingPropertyAccessor<?> propertyAccessor = newConvertingPropertyAccessor(object, entity);
Assert.isTrue(entity.getType().isAssignableFrom(object.getClass()),
String.format("Given instance of type [%s] is not compatible with expected type [%s]",
@@ -739,7 +618,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
DataType dataType = getMappingContext().getDataType(property);
if (dataType instanceof UserType || dataType instanceof TupleType) {
if (dataType instanceof UserDefinedType || dataType instanceof TupleType) {
return property.getType();
}
@@ -830,7 +709,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
if (entity.isUserDefinedType()) {
UDTValue udtValue = entity.getUserType().newValue();
UdtValue udtValue = entity.getUserType().newValue();
write(value, udtValue, entity);
@@ -999,13 +878,13 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
}
if (value instanceof UDTValue) {
if (value instanceof UdtValue) {
BasicCassandraPersistentEntity<?> udtEntity = getMappingContext()
.getPersistentEntity(typeInformation.getRequiredActualType());
if (udtEntity != null && udtEntity.isUserDefinedType()) {
return readEntityFromUdt(udtEntity, (UDTValue) value);
return readEntityFromUdt(udtEntity, (UdtValue) value);
}
}
@@ -1039,7 +918,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
if (entity != null && entity.isUserDefinedType()) {
for (Object element : source) {
collection.add(readEntityFromUdt(entity, (UDTValue) element));
collection.add(readEntityFromUdt(entity, (UdtValue) element));
}
} else if (entity != null && entity.isTupleType()) {
@@ -1076,7 +955,6 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
* @param targetType must not be {@literal null}.
* @return the converted {@link Collection} or array, will never be {@literal null}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private Object readMapInternal(Map<Object, Object> source, TypeInformation<?> targetType) {
Assert.notNull(targetType, "Target type must not be null");

View File

@@ -23,7 +23,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
@@ -49,6 +48,8 @@ import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* Map {@link org.springframework.data.cassandra.core.query.Query} to CQL-specific data types.
*
@@ -85,7 +86,7 @@ public class QueryMapper {
* @return the configured {@link CassandraConverter}.
* @see org.springframework.data.cassandra.core.convert.CassandraConverter
*/
protected CassandraConverter getConverter() {
public CassandraConverter getConverter() {
return this.converter;
}
@@ -185,7 +186,7 @@ public class QueryMapper {
CassandraPersistentEntity<?> primaryKeyEntity = mappingContext.getRequiredPersistentEntity(property);
addColumns(primaryKeyEntity, selectors);
} else {
selectors.add(ColumnSelector.from(property.getRequiredColumnName().toCql()));
selectors.add(ColumnSelector.from(property.getRequiredColumnName()));
}
});
}
@@ -234,7 +235,7 @@ public class QueryMapper {
* @param entity must not be {@literal null}.
* @return the mapped column names.
*/
public List<String> getMappedColumnNames(Columns columns, CassandraPersistentEntity<?> entity) {
public List<CqlIdentifier> getMappedColumnNames(Columns columns, CassandraPersistentEntity<?> entity) {
Assert.notNull(columns, "Columns must not be null");
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
@@ -243,7 +244,7 @@ public class QueryMapper {
return Collections.emptyList();
}
List<String> columnNames = new ArrayList<>();
List<CqlIdentifier> columnNames = new ArrayList<>();
Set<PersistentProperty<?>> seen = new HashSet<>();
@@ -254,7 +255,7 @@ public class QueryMapper {
field.getProperty().ifPresent(seen::add);
columns.getSelector(column).filter(selector -> selector instanceof ColumnSelector).ifPresent(
columnSelector -> getCqlIdentifier(column, field).map(CqlIdentifier::toCql).ifPresent(columnNames::add));
columnSelector -> getCqlIdentifier(column, field).ifPresent(columnNames::add));
}
if (columns.isEmpty()) {
@@ -266,7 +267,7 @@ public class QueryMapper {
}
if (seen.add(property)) {
columnNames.add(property.getRequiredColumnName().toCql());
columnNames.add(property.getRequiredColumnName());
}
});
}
@@ -292,7 +293,7 @@ public class QueryMapper {
Field field = createPropertyField(entity, columnName);
Order mappedOrder = getCqlIdentifier(columnName, field)
.map(cqlIdentifier -> new Order(order.getDirection(), cqlIdentifier.toCql())).orElse(order);
.map(cqlIdentifier -> new Order(order.getDirection(), cqlIdentifier.toString())).orElse(order);
mappedOrders.add(mappedOrder);
}
@@ -317,7 +318,7 @@ public class QueryMapper {
}
if (column.getColumnName().isPresent()) {
return column.getColumnName().map(CqlIdentifier::of);
return column.getColumnName().map(CqlIdentifier::fromCql);
}
return column.getCqlIdentifier();

View File

@@ -15,38 +15,40 @@
*/
package org.springframework.data.cassandra.core.convert;
import java.util.List;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.DataType.Name;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.TypeCodec;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.cql.ColumnDefinition;
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.ListType;
import com.datastax.oss.driver.api.core.type.MapType;
import com.datastax.oss.driver.api.core.type.SetType;
import com.datastax.oss.driver.api.core.type.TupleType;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Helpful class to read a column's value from a row, with possible type conversion.
*
* @author Matthew T. Adams
* @author Antoine Toulme
* @author Mark Paluch
* @since 3.0
*/
public class ColumnReader {
class RowReader {
private final Row row;
private final com.datastax.oss.driver.api.core.cql.Row row;
private final CodecRegistry codecRegistry;
private final ColumnDefinitions columns;
public ColumnReader(Row row, CodecRegistry codecRegistry) {
public RowReader(Row row) {
this.row = row;
this.codecRegistry = codecRegistry;
this.codecRegistry = row.codecRegistry();
this.columns = row.getColumnDefinitions();
}
@@ -55,7 +57,7 @@ public class ColumnReader {
*/
@Nullable
public Object get(CqlIdentifier columnName) {
return get(columnName.toCql());
return get(columnName.toString());
}
/**
@@ -79,18 +81,18 @@ public class ColumnReader {
return null;
}
DataType type = columns.getType(columnIndex);
ColumnDefinition type = columns.get(columnIndex);
if (type.isCollection()) {
return getCollection(columnIndex, type);
if (type.getType() instanceof ListType || type.getType() instanceof SetType || type.getType() instanceof MapType) {
return getCollection(columnIndex, type.getType());
}
if (Name.TUPLE.equals(type.getName())) {
if (type instanceof TupleType) {
return row.getTupleValue(columnIndex);
}
if (Name.UDT.equals(type.getName())) {
return row.getUDTValue(columnIndex);
if (type instanceof UserDefinedType) {
return row.getUdtValue(columnIndex);
}
return row.getObject(columnIndex);
@@ -103,7 +105,7 @@ public class ColumnReader {
*/
@Nullable
public <T> T get(CqlIdentifier columnName, Class<T> requestedType) {
return get(columnName.toCql(), requestedType);
return get(columnName.toString(), requestedType);
}
/**
@@ -132,26 +134,27 @@ public class ColumnReader {
@Nullable
private Object getCollection(int index, DataType type) {
List<DataType> collectionTypes = type.getTypeArguments();
// List/Set
if (collectionTypes.size() == 1) {
DataType valueType = collectionTypes.get(0);
if (type instanceof ListType) {
ListType listType = (ListType) type;
DataType valueType = listType.getElementType();
TypeCodec<Object> typeCodec = codecRegistry.codecFor(valueType);
if (type.equals(DataType.list(valueType))) {
return row.getList(index, typeCodec.getJavaType().getRawType());
}
return row.getList(index, typeCodec.getJavaType().getRawType());
}
if (type.equals(DataType.set(valueType))) {
return row.getSet(index, typeCodec.getJavaType().getRawType());
}
if (type instanceof SetType) {
SetType setType = (SetType) type;
DataType valueType = setType.getElementType();
TypeCodec<Object> typeCodec = codecRegistry.codecFor(valueType);
return row.getList(index, typeCodec.getJavaType().getRawType());
}
// Map
if (type.getName() == Name.MAP) {
if (type instanceof MapType) {
return row.getObject(index);
}
@@ -160,7 +163,7 @@ public class ColumnReader {
private int getColumnIndex(String columnName) {
int index = columns.getIndexOf(columnName);
int index = columns.firstIndexOf(columnName);
Assert.isTrue(index > -1, String.format("Column [%s] does not exist in table", columnName));
@@ -172,6 +175,6 @@ public class ColumnReader {
}
public boolean contains(CqlIdentifier columnName) {
return row.getColumnDefinitions().contains(columnName.toCql());
return row.getColumnDefinitions().contains(columnName);
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2013-2019 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
*
* https://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.convert;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* {@link CassandraValueProvider} to read property values from a {@link Row}.
*
* @author Mark Paluch
* @since 3.0
*/
public class RowValueProvider implements CassandraValueProvider {
private final RowReader reader;
private final SpELExpressionEvaluator evaluator;
/**
* Create a new {@link RowValueProvider} with the given {@link Row}, {@link CodecRegistry} and
* {@link SpELExpressionEvaluator}.
*
* @param source must not be {@literal null}.
* @param codecRegistry must not be {@literal null}.
* @param evaluator must not be {@literal null}.
*/
public RowValueProvider(Row source, SpELExpressionEvaluator evaluator) {
Assert.notNull(source, "Source Row must not be null");
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null");
this.reader = new RowReader(source);
this.evaluator = evaluator;
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@Nullable
@Override
@SuppressWarnings("unchecked")
public <T> T getPropertyValue(CassandraPersistentProperty property) {
String spelExpression = property.getSpelExpression();
return spelExpression != null ? this.evaluator.evaluate(spelExpression)
: (T) this.reader.get(property.getRequiredColumnName());
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.convert.CassandraValueProvider#hasProperty(org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty)
*/
@Override
public boolean hasProperty(CassandraPersistentProperty property) {
Assert.notNull(property, "CassandraPersistentProperty must not be null");
return this.reader.contains(property.getRequiredColumnName());
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2018-2019 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
*
* https://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.convert;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.data.TupleValue;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* {@link CassandraValueProvider} to read property values from a {@link TupleValue}.
*
* @author Mark Paluch
* @since 3.0
*/
public class TupleValueProvider implements CassandraValueProvider {
private final CodecRegistry codecRegistry;
private final SpELExpressionEvaluator evaluator;
private final TupleValue tupleValue;
/**
* Create a new {@link TupleValueProvider} with the given {@link TupleValue} and {@link SpELExpressionEvaluator}.
*
* @param tupleValue must not be {@literal null}.
* @param codecRegistry must not be {@literal null}.
* @param evaluator must not be {@literal null}.
*/
public TupleValueProvider(TupleValue tupleValue, SpELExpressionEvaluator evaluator) {
Assert.notNull(tupleValue, "TupleValue must not be null");
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null");
this.tupleValue = tupleValue;
this.codecRegistry = tupleValue.codecRegistry();
this.evaluator = evaluator;
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@Nullable
@Override
public <T> T getPropertyValue(CassandraPersistentProperty property) {
String spelExpression = property.getSpelExpression();
if (spelExpression != null) {
return evaluator.evaluate(spelExpression);
}
int ordinal = property.getRequiredOrdinal();
DataType elementType = tupleValue.getType().getComponentTypes().get(ordinal);
return tupleValue.get(ordinal, codecRegistry.codecFor(elementType));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.convert.CassandraValueProvider#hasProperty(org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty)
*/
@Override
public boolean hasProperty(CassandraPersistentProperty property) {
return this.tupleValue.getType().getComponentTypes().size() >= property.getRequiredOrdinal();
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2016-2019 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
*
* https://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.convert;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.data.UdtValue;
/**
* {@link CassandraValueProvider} to read property values from a {@link UdtValue}.
*
* @author Mark Paluch
* @since 3.0
*/
public class UdtValueProvider implements CassandraValueProvider {
private final UdtValue udtValue;
private final SpELExpressionEvaluator evaluator;
/**
* Create a new {@link UdtValueProvider} with the given {@link UDTValue} and {@link SpELExpressionEvaluator}.
*
* @param udtValue must not be {@literal null}.
* @param evaluator must not be {@literal null}.
*/
public UdtValueProvider(UdtValue udtValue, SpELExpressionEvaluator evaluator) {
Assert.notNull(udtValue, "UDTValue must not be null");
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null");
this.udtValue = udtValue;
this.evaluator = evaluator;
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@Nullable
public <T> T getPropertyValue(CassandraPersistentProperty property) {
String spelExpression = property.getSpelExpression();
if (spelExpression != null) {
return this.evaluator.evaluate(spelExpression);
}
return (T) this.udtValue.getObject(property.getRequiredColumnName());
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.convert.CassandraValueProvider#hasProperty(org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty)
*/
@Override
public boolean hasProperty(CassandraPersistentProperty property) {
return this.udtValue.getType().contains(property.getRequiredColumnName());
}
}

View File

@@ -40,8 +40,10 @@ import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.DataType.Name;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.ListType;
import com.datastax.oss.driver.api.core.type.SetType;
import com.datastax.oss.protocol.internal.ProtocolConstants;
/**
* Map {@link org.springframework.data.cassandra.core.query.Update} to CQL-specific data types.
@@ -167,10 +169,10 @@ public class UpdateMapper extends QueryMapper {
if (collection.isEmpty()) {
DataType.Name dataType = field.getProperty().map(property -> getMappingContext().getDataType(property))
.map(DataType::getName).orElse(Name.LIST);
int protocolCode = field.getProperty().map(property -> getMappingContext().getDataType(property))
.map(DataType::getProtocolCode).orElse(ProtocolConstants.DataType.LIST);
if (dataType == Name.SET) {
if (protocolCode == ProtocolConstants.DataType.SET) {
return new SetOp(field.getMappedKey(), Collections.emptySet());
}
@@ -203,13 +205,13 @@ public class UpdateMapper extends QueryMapper {
DataType dataType = getMappingContext().getDataType(field.getProperty().get());
if (dataType.getName() == Name.SET && !(mappedValue instanceof Set)) {
if (dataType instanceof SetType && !(mappedValue instanceof Set)) {
Collection<Object> collection = new HashSet<>();
collection.addAll(mappedValue);
mappedValue = collection;
}
if (dataType.getName() == Name.LIST && !(mappedValue instanceof List)) {
if (dataType instanceof ListType && !(mappedValue instanceof List)) {
Collection<Object> collection = new ArrayList<>();
collection.addAll(mappedValue);
mappedValue = collection;

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2019 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
*
* https://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.convert;
import java.util.LinkedHashMap;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* @author Mark Paluch
*/
public class Where extends LinkedHashMap<CqlIdentifier, Object> {}

View File

@@ -17,9 +17,9 @@ package org.springframework.data.cassandra.core.cql;
import org.springframework.lang.Nullable;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.cql.BoundStatement;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
/**
* Simple adapter for {@link PreparedStatementBinder} that applies a given array of arguments.

View File

@@ -23,9 +23,10 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.lang.Nullable;
import org.springframework.util.concurrent.ListenableFuture;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Statement;
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Statement;
/**
* Interface specifying a basic set of CQL asynchronously executed operations. Exposes similar methods as
@@ -42,14 +43,14 @@ import com.datastax.driver.core.Statement;
public interface AsyncCqlOperations {
// -------------------------------------------------------------------------
// Methods dealing with a plain com.datastax.driver.core.Session
// Methods dealing with a plain com.datastax.oss.driver.api.core.CqlSession
// -------------------------------------------------------------------------
/**
* Execute a CQL data access operation, implemented as callback action working on a
* {@link com.datastax.driver.core.Session}. This allows for implementing arbitrary data access operations, within
* Spring's managed CQL environment: that is, converting CQL
* {@link com.datastax.driver.core.exceptions.DriverException}s into Spring's {@link DataAccessException} hierarchy.
* Spring's managed CQL environment: that is, converting CQL {@link com.datastax.oss.driver.api.core.DriverException}s
* into Spring's {@link DataAccessException} hierarchy.
* <p>
* The callback action can return a result object, for example a domain object or a collection of domain objects.
*
@@ -102,31 +103,30 @@ public interface AsyncCqlOperations {
* Execute a CQL data access operation, implemented as callback action working on a CQL {@link PreparedStatement}.
* This allows for implementing arbitrary data access operations on a single Statement, within Spring's managed CQL
* environment: that is, participating in Spring-managed transactions and converting CQL
* {@link com.datastax.driver.core.exceptions.DriverException}s into Spring's {@link DataAccessException} hierarchy.
* {@link com.datastax.oss.driver.api.core.DriverException}s into Spring's {@link DataAccessException} hierarchy.
* <p>
* The callback action can return a result object, for example a domain object or a collection of domain objects.
*
* @param cql static CQL to execute, must not be {@literal null} or empty.
* @param action callback object that specifies the action, must not be {@literal null}.
* @return a result object returned by the action, or {@literal null}
* @throws DataAccessException if there is any problem executing the query. TODO: Lambda-usage clashes with
* execute(cql, PreparedStatementBinder)
* @throws DataAccessException if there is any problem executing the query.
*/
<T> ListenableFuture<T> execute(String cql, PreparedStatementCallback<T> action) throws DataAccessException;
/**
* Execute a query given static CQL, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
* Execute a query given static CQL, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
* <p>
* Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
* {@link PreparedStatement}, use the overloaded {@code query} method with {@literal null} as argument array.
*
* @param cql static CQL to execute, must not be {@literal null} or empty.
* @param resultSetExtractor object that will extract all rows of results, must not be {@literal null}.
* @return an arbitrary result object, as returned by the ResultSetExtractor.
* @return an arbitrary result object, as returned by the AsyncResultSetExtractor.
* @throws DataAccessException if there is any problem executing the query.
* @see #query(String, ResultSetExtractor, Object...)
* @see #query(String, AsyncResultSetExtractor, Object...)
*/
<T> ListenableFuture<T> query(String cql, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
<T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
/**
* Execute a query given static CQL, reading the {@link ResultSet} on a per-row basis with a
@@ -158,16 +158,16 @@ public interface AsyncCqlOperations {
/**
* Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, reading the
* {@link ResultSet} with a {@link ResultSetExtractor}.
* {@link ResultSet} with a {@link AsyncResultSetExtractor}.
*
* @param cql static CQL to execute, must not be {@literal null} or empty.
* @param resultSetExtractor object that will extract results, must not be {@literal null}.
* @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding
* CQL type).
* @return an arbitrary result object, as returned by the {@link ResultSetExtractor}
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}
* @throws DataAccessException if there is any problem executing the query.
*/
<T> ListenableFuture<T> query(String cql, ResultSetExtractor<T> resultSetExtractor, Object... args)
<T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor, Object... args)
throws DataAccessException;
/**
@@ -197,18 +197,18 @@ public interface AsyncCqlOperations {
<T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper, Object... args) throws DataAccessException;
/**
* Query using a prepared statement, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
* Query using a prepared statement, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
*
* @param cql static CQL to execute, must not be {@literal null} or empty.
* @param psb object that knows how to set values on the prepared statement. If this is {@literal null}, the CQL will
* be assumed to contain no bind parameters. Even if there are no bind parameters, this object may be used to
* set fetch size and other performance options.
* @param resultSetExtractor object that will extract results, must not be {@literal null}.
* @return an arbitrary result object, as returned by the {@link ResultSetExtractor}.
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}.
* @throws DataAccessException if there is any problem executing the query.
*/
<T> ListenableFuture<T> query(String cql, @Nullable PreparedStatementBinder psb,
ResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
/**
* Query given CQL to create a prepared statement from CQL and a {@link PreparedStatementBinder} implementation that
@@ -434,7 +434,7 @@ public interface AsyncCqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForResultSet(String, Object[])
*/
ListenableFuture<ResultSet> queryForResultSet(String cql) throws DataAccessException;
ListenableFuture<AsyncResultSet> queryForResultSet(String cql) throws DataAccessException;
/**
* Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
@@ -449,10 +449,10 @@ public interface AsyncCqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForResultSet(String)
*/
ListenableFuture<ResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException;
ListenableFuture<AsyncResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException;
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
// -------------------------------------------------------------------------
/**
@@ -462,21 +462,21 @@ public interface AsyncCqlOperations {
* @return boolean value whether the statement was applied.
* @throws DataAccessException if there is any problem executing the query.
*/
ListenableFuture<Boolean> execute(Statement statement) throws DataAccessException;
ListenableFuture<Boolean> execute(Statement<?> statement) throws DataAccessException;
/**
* Execute a query given static CQL, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
* Execute a query given static CQL, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
* <p>
* Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
* {@link PreparedStatement}, use the overloaded {@code query} method with {@literal null} as argument array.
*
* @param statement static CQL {@link Statement}, must not be {@literal null}.
* @param resultSetExtractor object that will extract all rows of results, must not be {@literal null}.
* @return an arbitrary result object, as returned by the ResultSetExtractor.
* @return an arbitrary result object, as returned by the AsyncResultSetExtractor.
* @throws DataAccessException if there is any problem executing the query.
* @see #query(String, ResultSetExtractor, Object...)
* @see #query(String, AsyncResultSetExtractor, Object...)
*/
<T> ListenableFuture<T> query(Statement statement, ResultSetExtractor<T> resultSetExtractor)
<T> ListenableFuture<T> query(Statement<?> statement, AsyncResultSetExtractor<T> resultSetExtractor)
throws DataAccessException;
/**
@@ -491,7 +491,8 @@ public interface AsyncCqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #query(String, RowCallbackHandler, Object[])
*/
ListenableFuture<Void> query(Statement statement, RowCallbackHandler rowCallbackHandler) throws DataAccessException;
ListenableFuture<Void> query(Statement<?> statement, RowCallbackHandler rowCallbackHandler)
throws DataAccessException;
/**
* Execute a query given static CQL, mapping each row to a Java object via a {@link RowMapper}.
@@ -505,7 +506,7 @@ public interface AsyncCqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #query(String, RowMapper, Object[])
*/
<T> ListenableFuture<List<T>> query(Statement statement, RowMapper<T> rowMapper) throws DataAccessException;
<T> ListenableFuture<List<T>> query(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException;
/**
* Execute a query for a result {@link List}, given static CQL.
@@ -522,7 +523,7 @@ public interface AsyncCqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForList(String, Object[])
*/
ListenableFuture<List<Map<String, Object>>> queryForList(Statement statement) throws DataAccessException;
ListenableFuture<List<Map<String, Object>>> queryForList(Statement<?> statement) throws DataAccessException;
/**
* Execute a query for a result {@link List}, given static CQL.
@@ -541,7 +542,7 @@ public interface AsyncCqlOperations {
* @see #queryForList(String, Class, Object[])
* @see SingleColumnRowMapper
*/
<T> ListenableFuture<List<T>> queryForList(Statement statement, Class<T> elementType) throws DataAccessException;
<T> ListenableFuture<List<T>> queryForList(Statement<?> statement, Class<T> elementType) throws DataAccessException;
/**
* Execute a query for a result Map, given static CQL.
@@ -560,7 +561,7 @@ public interface AsyncCqlOperations {
* @see #queryForMap(String, Object[])
* @see ColumnMapRowMapper
*/
ListenableFuture<Map<String, Object>> queryForMap(Statement statement) throws DataAccessException;
ListenableFuture<Map<String, Object>> queryForMap(Statement<?> statement) throws DataAccessException;
/**
* Execute a query for a result object, given static CQL.
@@ -580,7 +581,7 @@ public interface AsyncCqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForObject(String, Class, Object[])
*/
<T> ListenableFuture<T> queryForObject(Statement statement, Class<T> requiredType) throws DataAccessException;
<T> ListenableFuture<T> queryForObject(Statement<?> statement, Class<T> requiredType) throws DataAccessException;
/**
* Execute a query given static CQL, mapping a single result row to a Java object via a {@link RowMapper}.
@@ -596,7 +597,7 @@ public interface AsyncCqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForObject(String, RowMapper, Object[])
*/
<T> ListenableFuture<T> queryForObject(Statement statement, RowMapper<T> rowMapper) throws DataAccessException;
<T> ListenableFuture<T> queryForObject(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException;
/**
* Execute a query for a ResultSet, given static CQL.
@@ -612,10 +613,10 @@ public interface AsyncCqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForResultSet(String, Object[])
*/
ListenableFuture<ResultSet> queryForResultSet(Statement statement) throws DataAccessException;
ListenableFuture<AsyncResultSet> queryForResultSet(Statement<?> statement) throws DataAccessException;
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.PreparedStatement
// Methods dealing with com.datastax.oss.driver.api.core.cql.PreparedStatement
// -------------------------------------------------------------------------
/**
@@ -626,14 +627,13 @@ public interface AsyncCqlOperations {
* @return boolean value whether the statement was applied.
* @throws DataAccessException if there is any problem executing the query.
*/
// TODO: Interferes with execute(session callback lambda)
ListenableFuture<Boolean> execute(AsyncPreparedStatementCreator preparedStatementCreator) throws DataAccessException;
/**
* Execute a CQL data access operation, implemented as callback action working on a CQL {@link PreparedStatement}.
* This allows for implementing arbitrary data access operations on a single {@link PreparedStatement}, within
* Spring's managed CQL environment: that is, participating in Spring-managed transactions and converting CQL
* {@link com.datastax.driver.core.exceptions.DriverException}s into Spring's {@link DataAccessException} hierarchy.
* {@link com.datastax.oss.driver.api.core.DriverException}s into Spring's {@link DataAccessException} hierarchy.
* <p>
* The callback action can return a result object, for example a domain object or a collection of domain objects.
*
@@ -647,16 +647,16 @@ public interface AsyncCqlOperations {
PreparedStatementCallback<T> action) throws DataAccessException;
/**
* Query using a prepared statement, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
* Query using a prepared statement, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
*
* @param preparedStatementCreator object that can create a {@link PreparedStatement} given a
* {@link com.datastax.driver.core.Session}, must not be {@literal null}.
* @param resultSetExtractor object that will extract results, must not be {@literal null}.
* @return an arbitrary result object, as returned by the {@link ResultSetExtractor}
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}
* @throws DataAccessException if there is any problem executing the query.
*/
<T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
ResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
/**
* Query using a prepared statement, reading the {@link ResultSet} on a per-row basis with a
@@ -684,7 +684,7 @@ public interface AsyncCqlOperations {
/**
* Query using a prepared statement and a {@link PreparedStatementBinder} implementation that knows how to bind values
* to the query, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
* to the query, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
*
* @param preparedStatementCreator object that can create a {@link PreparedStatement} given a
* {@link com.datastax.driver.core.Session}, must not be {@literal null}.
@@ -692,11 +692,11 @@ public interface AsyncCqlOperations {
* be assumed to contain no bind parameters. Even if there are no bind parameters, this object may be used to
* set fetch size and other performance options.
* @param resultSetExtractor object that will extract results, must not be {@literal null}.
* @return an arbitrary result object, as returned by the {@link ResultSetExtractor}.
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}.
* @throws DataAccessException if there is any problem executing the query.
*/
<T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
@Nullable PreparedStatementBinder psb, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
@Nullable PreparedStatementBinder psb, AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
/**
* Query using a prepared statement and a {@link PreparedStatementBinder} implementation that knows how to bind values

View File

@@ -15,10 +15,10 @@
*/
package org.springframework.data.cassandra.core.cql;
import io.netty.util.concurrent.ImmediateExecutor;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
@@ -26,21 +26,18 @@ import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.SessionFactory;
import org.springframework.data.cassandra.core.cql.util.CassandraFutureAdapter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.SettableListenableFuture;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.exceptions.DriverException;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Statement;
/**
* <b>This is the central class in the CQL core package for asynchronous Cassandra data access.</b> It simplifies the
@@ -51,13 +48,13 @@ import com.google.common.util.concurrent.Futures;
* <p>
* Code using this class need only implement callback interfaces, giving them a clearly defined contract. The
* {@link PreparedStatementCreator} callback interface creates a prepared statement given a Connection, providing CQL
* and any necessary parameters. The {@link ResultSetExtractor} interface extracts values from a {@link ResultSet}. See
* also {@link PreparedStatementBinder} and {@link RowMapper} for two popular alternative callback interfaces.
* and any necessary parameters. The {@link AsyncResultSetExtractor} interface extracts values from a {@link ResultSet}.
* See also {@link PreparedStatementBinder} and {@link RowMapper} for two popular alternative callback interfaces.
* <p>
* Can be used within a service implementation via direct instantiation with a {@link Session} reference, or get
* prepared in an application context and given to services as bean reference. Note: The {@link Session} should always
* be configured as a bean in the application context, in the first case given to the service directly, in the second
* case to the prepared template.
* Can be used within a service implementation via direct instantiation with a {@link CqlSession} reference, or get
* prepared in an application context and given to services as bean reference. Note: The {@link CqlSession} should
* always be configured as a bean in the application context, in the first case given to the service directly, in the
* second case to the prepared template.
* <p>
* Because this class is parameterizable by the callback interfaces and the
* {@link org.springframework.dao.support.PersistenceExceptionTranslator} interface, there should be no need to subclass
@@ -74,7 +71,7 @@ import com.google.common.util.concurrent.Futures;
* @see PreparedStatementCreator
* @see PreparedStatementBinder
* @see PreparedStatementCallback
* @see ResultSetExtractor
* @see AsyncResultSetExtractor
* @see RowCallbackHandler
* @see RowMapper
* @see org.springframework.dao.support.PersistenceExceptionTranslator
@@ -90,13 +87,12 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
public AsyncCqlTemplate() {}
/**
* Create a new {@link AsyncCqlTemplate} with the given {@link Session}.
* Create a new {@link AsyncCqlTemplate} with the given {@link CqlSession}.
*
* @param session the active Cassandra {@link Session}, must not be {@literal null}.
* @throws IllegalStateException if {@link Session} is {@literal null}.
* @see com.datastax.driver.core.Session
* @param session the active Cassandra {@link CqlSession}, must not be {@literal null}.
* @throws IllegalStateException if {@link CqlSession} is {@literal null}.
*/
public AsyncCqlTemplate(Session session) {
public AsyncCqlTemplate(CqlSession session) {
Assert.notNull(session, "Session must not be null");
@@ -150,32 +146,30 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
Assert.hasText(cql, "CQL must not be empty");
return new MappingListenableFutureAdapter<>(queryForResultSet(cql), ResultSet::wasApplied);
return new MappingListenableFutureAdapter<>(queryForResultSet(cql), AsyncResultSet::wasApplied);
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.ResultSetExtractor)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.AsyncResultSetExtractor)
*/
@Override
public <T> ListenableFuture<T> query(String cql, ResultSetExtractor<T> resultSetExtractor)
public <T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor)
throws DataAccessException {
Assert.hasText(cql, "CQL must not be empty");
Assert.notNull(resultSetExtractor, "ResultSetExtractor must not be null");
Assert.notNull(resultSetExtractor, "AsyncResultSetExtractor must not be null");
try {
if (logger.isDebugEnabled()) {
logger.debug("Executing CQL Statement [{}]", cql);
}
SimpleStatement simpleStatement = applyStatementSettings(new SimpleStatement(cql));
CompletionStage<T> results = getCurrentSession().executeAsync(applyStatementSettings(newStatement(cql)))
.thenApply(resultSetExtractor::extractData) //
.thenCompose(ListenableFuture::completable);
ResultSetFuture results = getCurrentSession().executeAsync(simpleStatement);
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(
new GuavaListenableFutureAdapter<>(results, ex -> translateExceptionIfPossible("Query", cql, ex)),
resultSetExtractor::extractData), getExceptionTranslator());
return new CassandraFutureAdapter<>(results, ex -> translateExceptionIfPossible("Query", cql, ex));
} catch (DriverException e) {
throw translateException("Query", cql, e);
}
@@ -188,10 +182,10 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
@Override
public ListenableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler) throws DataAccessException {
ListenableFuture<?> results = query(cql, newResultSetExtractor(rowCallbackHandler));
ListenableFuture<?> results = query(cql, newAsyncResultSetExtractor(rowCallbackHandler));
return new MappingListenableFutureAdapter<>(results, o -> null);
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
getExceptionTranslator());
}
/*
@@ -200,7 +194,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
*/
@Override
public <T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper) throws DataAccessException {
return query(cql, newResultSetExtractor(rowMapper));
return query(cql, newAsyncResultSetExtractor(rowMapper));
}
/*
@@ -209,7 +203,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
*/
@Override
public ListenableFuture<List<Map<String, Object>>> queryForList(String cql) throws DataAccessException {
return query(cql, newResultSetExtractor(newColumnMapRowMapper()));
return query(cql, newAsyncResultSetExtractor(newColumnMapRowMapper()));
}
/*
@@ -218,7 +212,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
*/
@Override
public <T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType) throws DataAccessException {
return query(cql, newResultSetExtractor(newSingleColumnRowMapper(elementType)));
return query(cql, newAsyncResultSetExtractor(newSingleColumnRowMapper(elementType)));
}
/*
@@ -246,7 +240,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
@Override
public <T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper) throws DataAccessException {
ListenableFuture<List<T>> results = query(cql, newResultSetExtractor(rowMapper));
ListenableFuture<List<T>> results = query(cql, newAsyncResultSetExtractor(rowMapper));
return new ExceptionTranslatingListenableFutureAdapter<>(
new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator());
@@ -257,50 +251,49 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForResultSet(java.lang.String)
*/
@Override
public ListenableFuture<ResultSet> queryForResultSet(String cql) throws DataAccessException {
return query(cql, rs -> rs);
public ListenableFuture<AsyncResultSet> queryForResultSet(String cql) throws DataAccessException {
return query(cql, AsyncCqlTemplate::toResultSet);
}
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
// -------------------------------------------------------------------------
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#execute(com.datastax.driver.core.Statement)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#execute(com.datastax.oss.driver.api.core.cql.Statement)
*/
@Override
public ListenableFuture<Boolean> execute(Statement statement) throws DataAccessException {
public ListenableFuture<Boolean> execute(Statement<?> statement) throws DataAccessException {
Assert.notNull(statement, "CQL Statement must not be null");
return new MappingListenableFutureAdapter<>(queryForResultSet(statement), ResultSet::wasApplied);
return new MappingListenableFutureAdapter<>(queryForResultSet(statement), AsyncResultSet::wasApplied);
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(com.datastax.driver.core.Statement, org.springframework.data.cassandra.core.cql.ResultSetExtractor)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(com.datastax.oss.driver.api.core.cql.Statement, org.springframework.data.cassandra.core.cql.AsyncResultSetExtractor)
*/
@Override
public <T> ListenableFuture<T> query(Statement statement, ResultSetExtractor<T> resultSetExtractor)
public <T> ListenableFuture<T> query(Statement<?> statement, AsyncResultSetExtractor<T> resultSetExtractor)
throws DataAccessException {
Assert.notNull(statement, "CQL Statement must not be null");
Assert.notNull(resultSetExtractor, "ResultSetExtractor must not be null");
Assert.notNull(resultSetExtractor, "AsyncResultSetExtractor must not be null");
try {
if (logger.isDebugEnabled()) {
logger.debug("Executing CQL Statement [{}]", statement);
}
ResultSetFuture results = getCurrentSession().executeAsync(applyStatementSettings(statement));
CompletionStage<T> results = getCurrentSession() //
.executeAsync(applyStatementSettings(statement)) //
.thenApply(resultSetExtractor::extractData) //
.thenCompose(ListenableFuture::completable);
return new ExceptionTranslatingListenableFutureAdapter<>(
new MappingListenableFutureAdapter<>(
new GuavaListenableFutureAdapter<>(results,
ex -> translateExceptionIfPossible("Query", statement.toString(), ex)),
resultSetExtractor::extractData),
getExceptionTranslator());
return new CassandraFutureAdapter<>(results,
ex -> translateExceptionIfPossible("Query", statement.toString(), ex));
} catch (DriverException e) {
throw translateException("Query", statement.toString(), e);
}
@@ -308,13 +301,13 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(com.datastax.driver.core.Statement, org.springframework.data.cassandra.core.cql.RowCallbackHandler)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(com.datastax.oss.driver.api.core.cql.Statement, org.springframework.data.cassandra.core.cql.RowCallbackHandler)
*/
@Override
public ListenableFuture<Void> query(Statement statement, RowCallbackHandler rowCallbackHandler)
public ListenableFuture<Void> query(Statement<?> statement, RowCallbackHandler rowCallbackHandler)
throws DataAccessException {
ListenableFuture<?> result = query(statement, newResultSetExtractor(rowCallbackHandler));
ListenableFuture<?> result = query(statement, newAsyncResultSetExtractor(rowCallbackHandler));
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(result, o -> null),
getExceptionTranslator());
@@ -322,59 +315,61 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(com.datastax.driver.core.Statement, org.springframework.data.cassandra.core.cql.RowMapper)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(com.datastax.oss.driver.api.core.cql.Statement, org.springframework.data.cassandra.core.cql.RowMapper)
*/
@Override
public <T> ListenableFuture<List<T>> query(Statement statement, RowMapper<T> rowMapper) throws DataAccessException {
return query(statement, newResultSetExtractor(rowMapper));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForList(com.datastax.driver.core.Statement)
*/
@Override
public ListenableFuture<List<Map<String, Object>>> queryForList(Statement statement) throws DataAccessException {
return query(statement, newResultSetExtractor(newColumnMapRowMapper()));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForList(com.datastax.driver.core.Statement, java.lang.Class)
*/
@Override
public <T> ListenableFuture<List<T>> queryForList(Statement statement, Class<T> elementType)
public <T> ListenableFuture<List<T>> query(Statement<?> statement, RowMapper<T> rowMapper)
throws DataAccessException {
return query(statement, newResultSetExtractor(newSingleColumnRowMapper(elementType)));
return query(statement, newAsyncResultSetExtractor(rowMapper));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForMap(com.datastax.driver.core.Statement)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForList(com.datastax.oss.driver.api.core.cql.Statement)
*/
@Override
public ListenableFuture<Map<String, Object>> queryForMap(Statement statement) throws DataAccessException {
public ListenableFuture<List<Map<String, Object>>> queryForList(Statement<?> statement) throws DataAccessException {
return query(statement, newAsyncResultSetExtractor(newColumnMapRowMapper()));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForList(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> ListenableFuture<List<T>> queryForList(Statement<?> statement, Class<T> elementType)
throws DataAccessException {
return query(statement, newAsyncResultSetExtractor(newSingleColumnRowMapper(elementType)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForMap(com.datastax.oss.driver.api.core.cql.Statement)
*/
@Override
public ListenableFuture<Map<String, Object>> queryForMap(Statement<?> statement) throws DataAccessException {
return queryForObject(statement, newColumnMapRowMapper());
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForObject(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForObject(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> ListenableFuture<T> queryForObject(Statement statement, Class<T> requiredType) throws DataAccessException {
public <T> ListenableFuture<T> queryForObject(Statement<?> statement, Class<T> requiredType)
throws DataAccessException {
return queryForObject(statement, newSingleColumnRowMapper(requiredType));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForObject(com.datastax.driver.core.Statement, org.springframework.data.cassandra.core.cql.RowMapper)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForObject(com.datastax.oss.driver.api.core.cql.Statement, org.springframework.data.cassandra.core.cql.RowMapper)
*/
@Override
public <T> ListenableFuture<T> queryForObject(Statement statement, RowMapper<T> rowMapper)
public <T> ListenableFuture<T> queryForObject(Statement<?> statement, RowMapper<T> rowMapper)
throws DataAccessException {
ListenableFuture<List<T>> results = query(statement, newResultSetExtractor(rowMapper));
ListenableFuture<List<T>> results = query(statement, newAsyncResultSetExtractor(rowMapper));
return new ExceptionTranslatingListenableFutureAdapter<>(
new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator());
@@ -382,11 +377,11 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForResultSet(com.datastax.driver.core.Statement)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForResultSet(com.datastax.oss.driver.api.core.cql.Statement)
*/
@Override
public ListenableFuture<ResultSet> queryForResultSet(Statement statement) throws DataAccessException {
return query(statement, rs -> rs);
public ListenableFuture<AsyncResultSet> queryForResultSet(Statement<?> statement) throws DataAccessException {
return query(statement, AsyncCqlTemplate::toResultSet);
}
// -------------------------------------------------------------------------
@@ -401,7 +396,8 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
public ListenableFuture<Boolean> execute(AsyncPreparedStatementCreator preparedStatementCreator)
throws DataAccessException {
return query(preparedStatementCreator, ResultSet::wasApplied);
return new MappingListenableFutureAdapter<>(query(preparedStatementCreator, AsyncCqlTemplate::toResultSet),
AsyncResultSet::wasApplied);
}
/*
@@ -421,7 +417,8 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
public ListenableFuture<Boolean> execute(String cql, @Nullable PreparedStatementBinder psb)
throws DataAccessException {
return query(newAsyncPreparedStatementCreator(cql), psb, ResultSet::wasApplied);
return new MappingListenableFutureAdapter<>(
query(newAsyncPreparedStatementCreator(cql), psb, AsyncCqlTemplate::toResultSet), AsyncResultSet::wasApplied);
}
/*
@@ -451,11 +448,11 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
logger.debug("Preparing statement [{}] using {}", toCql(preparedStatementCreator), preparedStatementCreator);
}
Session currentSession = getCurrentSession();
CqlSession currentSession = getCurrentSession();
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(
preparedStatementCreator.createPreparedStatement(currentSession), preparedStatement -> {
try {
return action.doInPreparedStatement(currentSession, applyStatementSettings(preparedStatement));
return action.doInPreparedStatement(currentSession, preparedStatement);
} catch (DriverException e) {
throw translateException(exceptionTranslator, e);
}
@@ -468,11 +465,11 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(org.springframework.data.cassandra.core.cql.AsyncPreparedStatementCreator, org.springframework.data.cassandra.core.cql.ResultSetExtractor)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(org.springframework.data.cassandra.core.cql.AsyncPreparedStatementCreator, org.springframework.data.cassandra.core.cql.AsyncResultSetExtractor)
*/
@Override
public <T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
ResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
return query(preparedStatementCreator, null, resultSetExtractor);
}
@@ -485,7 +482,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
public ListenableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
RowCallbackHandler rowCallbackHandler) throws DataAccessException {
ListenableFuture<?> results = query(preparedStatementCreator, null, newResultSetExtractor(rowCallbackHandler));
ListenableFuture<?> results = query(preparedStatementCreator, null, newAsyncResultSetExtractor(rowCallbackHandler));
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
getExceptionTranslator());
@@ -499,19 +496,19 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
public <T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
RowMapper<T> rowMapper) throws DataAccessException {
return query(preparedStatementCreator, null, newResultSetExtractor(rowMapper));
return query(preparedStatementCreator, null, newAsyncResultSetExtractor(rowMapper));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(org.springframework.data.cassandra.core.cql.AsyncPreparedStatementCreator, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.ResultSetExtractor)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(org.springframework.data.cassandra.core.cql.AsyncPreparedStatementCreator, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.AsyncResultSetExtractor)
*/
@Override
public <T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
@Nullable PreparedStatementBinder psb, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
@Nullable PreparedStatementBinder psb, AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
Assert.notNull(preparedStatementCreator, "AsyncPreparedStatementCreator must not be null");
Assert.notNull(resultSetExtractor, "ResultSetExtractor object must not be null");
Assert.notNull(resultSetExtractor, "AsyncResultSetExtractor object must not be null");
PersistenceExceptionTranslator exceptionTranslator = ex -> translateExceptionIfPossible("Query",
toCql(preparedStatementCreator), ex);
@@ -522,9 +519,9 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
logger.debug("Preparing statement [{}] using {}", toCql(preparedStatementCreator), preparedStatementCreator);
}
Session session = getCurrentSession();
CqlSession session = getCurrentSession();
ListenableFuture<BoundStatement> statementFuture = new MappingListenableFutureAdapter<>(
ListenableFuture<Statement<?>> statementFuture = new MappingListenableFutureAdapter<>(
preparedStatementCreator.createPreparedStatement(session), preparedStatement -> {
if (logger.isDebugEnabled()) {
logger.debug("Executing prepared statement [{}]", preparedStatement);
@@ -533,37 +530,12 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
return applyStatementSettings(psb != null ? psb.bindValues(preparedStatement) : preparedStatement.bind());
});
SettableListenableFuture<T> settableListenableFuture = new SettableListenableFuture<>();
statementFuture.addCallback(
boundStatement -> Futures.addCallback(session.executeAsync(boundStatement), new FutureCallback<ResultSet>() {
@Override
public void onSuccess(@Nullable ResultSet result) {
try {
settableListenableFuture.set(result != null ? resultSetExtractor.extractData(result) : null);
} catch (DriverException e) {
settableListenableFuture.setException(translateException(exceptionTranslator, e));
}
}
@Override
public void onFailure(Throwable ex) {
if (ex instanceof DriverException) {
settableListenableFuture.setException(translateException(exceptionTranslator, (DriverException) ex));
} else {
settableListenableFuture.setException(ex);
}
}
}, ImmediateExecutor.INSTANCE), ex -> {
if (ex instanceof DriverException) {
settableListenableFuture.setException(translateException(exceptionTranslator, (DriverException) ex));
} else {
settableListenableFuture.setException(ex);
}
});
return settableListenableFuture;
CompletableFuture<T> result = statementFuture.completable() //
.thenCompose(session::executeAsync) //
.thenApply(resultSetExtractor::extractData) //
.thenCompose(ListenableFuture::completable);
return new CassandraFutureAdapter<>(result, exceptionTranslator);
} catch (DriverException e) {
throw translateException(exceptionTranslator, e);
}
@@ -577,7 +549,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
public ListenableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
@Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException {
ListenableFuture<?> results = query(preparedStatementCreator, psb, newResultSetExtractor(rowCallbackHandler));
ListenableFuture<?> results = query(preparedStatementCreator, psb, newAsyncResultSetExtractor(rowCallbackHandler));
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
getExceptionTranslator());
@@ -590,15 +562,15 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
@Override
public <T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
@Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper) throws DataAccessException {
return query(preparedStatementCreator, psb, newResultSetExtractor(rowMapper));
return query(preparedStatementCreator, psb, newAsyncResultSetExtractor(rowMapper));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.ResultSetExtractor, java.lang.Object[])
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.AsyncResultSetExtractor, java.lang.Object[])
*/
@Override
public <T> ListenableFuture<T> query(String cql, ResultSetExtractor<T> resultSetExtractor, Object... args)
public <T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor, Object... args)
throws DataAccessException {
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), resultSetExtractor);
}
@@ -612,7 +584,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
throws DataAccessException {
ListenableFuture<?> results = query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
newResultSetExtractor(rowCallbackHandler));
newAsyncResultSetExtractor(rowCallbackHandler));
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
getExceptionTranslator());
@@ -626,16 +598,16 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
public <T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper, Object... args)
throws DataAccessException {
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
newResultSetExtractor(rowMapper));
newAsyncResultSetExtractor(rowMapper));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.ResultSetExtractor)
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.AsyncResultSetExtractor)
*/
@Override
public <T> ListenableFuture<T> query(String cql, @Nullable PreparedStatementBinder psb,
ResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
return query(newAsyncPreparedStatementCreator(cql), psb, resultSetExtractor);
}
@@ -649,7 +621,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
RowCallbackHandler rowCallbackHandler) throws DataAccessException {
ListenableFuture<?> results = query(newAsyncPreparedStatementCreator(cql), psb,
newResultSetExtractor(rowCallbackHandler));
newAsyncResultSetExtractor(rowCallbackHandler));
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
getExceptionTranslator());
@@ -663,7 +635,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
public <T> ListenableFuture<List<T>> query(String cql, @Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper)
throws DataAccessException {
return query(newAsyncPreparedStatementCreator(cql), psb, newResultSetExtractor(rowMapper));
return query(newAsyncPreparedStatementCreator(cql), psb, newAsyncResultSetExtractor(rowMapper));
}
/*
@@ -675,7 +647,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
throws DataAccessException {
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
newResultSetExtractor(newColumnMapRowMapper()));
newAsyncResultSetExtractor(newColumnMapRowMapper()));
}
/*
@@ -687,7 +659,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
throws DataAccessException {
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
newResultSetExtractor(newSingleColumnRowMapper(elementType)));
newAsyncResultSetExtractor(newSingleColumnRowMapper(elementType)));
}
/*
@@ -719,7 +691,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
throws DataAccessException {
ListenableFuture<List<T>> results = query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
newResultSetExtractor(rowMapper, 1));
newAsyncResultSetExtractor(rowMapper));
return new ExceptionTranslatingListenableFutureAdapter<>(
new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator());
@@ -730,26 +702,14 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
* @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#queryForResultSet(java.lang.String, java.lang.Object[])
*/
@Override
public ListenableFuture<ResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException {
return query(cql, rs -> rs, args);
public ListenableFuture<AsyncResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException {
return query(cql, AsyncCqlTemplate::toResultSet, args);
}
// -------------------------------------------------------------------------
// Implementation hooks and helper methods
// -------------------------------------------------------------------------
/**
* Create a new CQL-based {@link AsyncPreparedStatementCreator} using the CQL passed in. By default, we'll create an
* {@link SimpleAsyncPreparedStatementCreator}. This method allows for the creation to be overridden by subclasses.
*
* @param cql static CQL to execute, must not be empty or {@literal null}.
* @return the new {@link AsyncPreparedStatementCreator} to use
*/
protected AsyncPreparedStatementCreator newAsyncPreparedStatementCreator(String cql) {
return new SimpleAsyncPreparedStatementCreator(cql,
ex -> translateExceptionIfPossible("PrepareStatement", cql, ex));
}
/**
* Translate the given {@link DriverException} into a generic {@link DataAccessException}.
*
@@ -774,10 +734,51 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
*/
@Nullable
protected DataAccessException translateExceptionIfPossible(String task, @Nullable String cql, RuntimeException ex) {
return (ex instanceof DriverException ? translate(task, cql, (DriverException) ex) : null);
return translate(task, cql, ex);
}
private Session getCurrentSession() {
/**
* Create a new CQL-based {@link AsyncPreparedStatementCreator} using the CQL passed in. By default, we'll create an
* {@link SimpleAsyncPreparedStatementCreator}. This method allows for the creation to be overridden by subclasses.
*
* @param cql static CQL to execute, must not be empty or {@literal null}.
* @return the new {@link AsyncPreparedStatementCreator} to use
*/
protected AsyncPreparedStatementCreator newAsyncPreparedStatementCreator(String cql) {
return new SimpleAsyncPreparedStatementCreator(cql,
ex -> translateExceptionIfPossible("PrepareStatement", cql, ex));
}
/**
* Constructs a new instance of the {@link ResultSetExtractor} initialized with and adapting the given
* {@link RowCallbackHandler}.
*
* @param rowCallbackHandler {@link RowCallbackHandler} to adapt as a {@link ResultSetExtractor}.
* @return a {@link ResultSetExtractor} implementation adapting an instance of the {@link RowCallbackHandler}.
* @see AsyncRowCallbackHandlerResultSetExtractor
* @see ResultSetExtractor
* @see RowCallbackHandler
*/
protected AsyncRowCallbackHandlerResultSetExtractor newAsyncResultSetExtractor(
RowCallbackHandler rowCallbackHandler) {
return new AsyncRowCallbackHandlerResultSetExtractor(rowCallbackHandler);
}
/**
* Constructs a new instance of the {@link ResultSetExtractor} initialized with and adapting the given
* {@link RowMapper}.
*
* @param rowMapper {@link RowMapper} to adapt as a {@link ResultSetExtractor}.
* @return a {@link ResultSetExtractor} implementation adapting an instance of the {@link RowMapper}.
* @see ResultSetExtractor
* @see RowMapper
* @see RowMapperResultSetExtractor
*/
protected <T> AsyncRowMapperResultSetExtractor<T> newAsyncResultSetExtractor(RowMapper<T> rowMapper) {
return new AsyncRowMapperResultSetExtractor<>(rowMapper);
}
private CqlSession getCurrentSession() {
SessionFactory sessionFactory = getSessionFactory();
@@ -786,6 +787,14 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
return sessionFactory.getSession();
}
private static ListenableFuture<AsyncResultSet> toResultSet(AsyncResultSet resultSet) {
SettableListenableFuture<AsyncResultSet> future = new SettableListenableFuture<>();
future.set(resultSet);
return future;
}
private static RuntimeException translateException(PersistenceExceptionTranslator exceptionTranslator,
DriverException e) {
@@ -795,17 +804,16 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
private static class SimpleAsyncPreparedStatementCreator implements AsyncPreparedStatementCreator, CqlProvider {
private final PersistenceExceptionTranslator persistenceExceptionTranslator;
private final PersistenceExceptionTranslator exceptionTranslator;
private final String cql;
private SimpleAsyncPreparedStatementCreator(String cql,
PersistenceExceptionTranslator persistenceExceptionTranslator) {
private SimpleAsyncPreparedStatementCreator(String cql, PersistenceExceptionTranslator exceptionTranslator) {
Assert.hasText(cql, "CQL must not be empty");
this.cql = cql;
this.persistenceExceptionTranslator = persistenceExceptionTranslator;
this.exceptionTranslator = exceptionTranslator;
}
@Override
@@ -814,9 +822,30 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
}
@Override
public ListenableFuture<PreparedStatement> createPreparedStatement(Session session) throws DriverException {
public ListenableFuture<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException {
return new CassandraFutureAdapter<>(session.prepareAsync(getCql()),
exceptionTranslator::translateExceptionIfPossible);
}
}
return new GuavaListenableFutureAdapter<>(session.prepareAsync(getCql()), this.persistenceExceptionTranslator);
/**
* Adapter to enable use of a {@link RowCallbackHandler} inside a {@link ResultSetExtractor}.
*/
protected static class AsyncRowCallbackHandlerResultSetExtractor implements AsyncResultSetExtractor<Void> {
private final RowCallbackHandler rowCallbackHandler;
protected AsyncRowCallbackHandlerResultSetExtractor(RowCallbackHandler rowCallbackHandler) {
this.rowCallbackHandler = rowCallbackHandler;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.ResultSetExtractor#extractData(com.datastax.driver.core.ResultSet)
*/
@Override
@Nullable
public ListenableFuture<Void> extractData(AsyncResultSet resultSet) {
return AsyncResultStream.from(resultSet).forEach(rowCallbackHandler::processRow);
}
}
@@ -835,4 +864,5 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
return mapper.apply(adapteeResult);
}
}
}

View File

@@ -17,14 +17,14 @@ package org.springframework.data.cassandra.core.cql;
import org.springframework.util.concurrent.ListenableFuture;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
/**
* One of the two central callback interfaces used by the {@link AsyncCqlTemplate} class. This interface prepares a CQL
* statement returning a {@link org.springframework.util.concurrent.ListenableFuture} given a {@link Session}, provided
* by the {@link CqlTemplate} class.
* statement returning a {@link org.springframework.util.concurrent.ListenableFuture} given a {@link CqlSession},
* provided by the {@link CqlTemplate} class.
* <p>
* Implementations may either create new prepared statements or reuse cached instances. Implementations do not need to
* concern themselves with {@link DriverException}s that may be thrown from operations they attempt. The
@@ -51,5 +51,5 @@ public interface AsyncPreparedStatementCreator {
* @throws DriverException there is no need to catch DriverException that may be thrown in the implementation of this
* method. The {@link AsyncCqlTemplate} class will handle them.
*/
ListenableFuture<PreparedStatement> createPreparedStatement(Session session) throws DriverException;
ListenableFuture<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException;
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2013-2019 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
*
* https://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.cql;
import org.springframework.dao.DataAccessException;
import org.springframework.lang.Nullable;
import org.springframework.util.concurrent.ListenableFuture;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
/**
* Callback interface used by {@link AsyncCqlTemplate}'s query methods. Implementations of this interface perform the
* actual work of extracting results from a {@link AsyncResultSet}, but don't need to worry about exception handling.
* {@link DriverException}s will be caught and handled by the calling {@link AsyncCqlTemplate}.
* <p>
* This interface is mainly used within the CQL framework itself. A {@link RowMapper} is usually a simpler choice for
* {@link AsyncResultSet} processing, mapping one result object per row instead of one result object for the entire
* {@link AsyncResultSet}.
* <p>
* Note: In contrast to a {@link RowCallbackHandler}, a {@link AsyncResultSetExtractor} object is typically stateless
* and thus reusable, as long as it doesn't access stateful resources or keep result state within the object.
*
* @author Mark Paluch
* @since 3.0
* @see AsyncCqlTemplate
* @see RowMapper
*/
@FunctionalInterface
public interface AsyncResultSetExtractor<T> {
/**
* Implementations must implement this method to process the entire {@link AsyncResultSet}.
*
* @param resultSet {@link AsyncResultSet} to extract data from.
* @return an arbitrary result object, or {@literal null} if none (the extractor will typically be stateful in the
* latter case).
* @throws DriverException if a {@link DriverException} is encountered getting column values or navigating (that is,
* there's no need to catch {@link DriverException})
* @throws DataAccessException in case of custom exceptions
*/
@Nullable
ListenableFuture<T> extractData(AsyncResultSet resultSet) throws DriverException, DataAccessException;
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2016-2019 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
*
* https://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.cql;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.dao.DataAccessException;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
/**
* Adapter implementation of the {@link ResultSetExtractor} interface that delegates to a {@link RowMapper} which is
* supposed to create an object for each row. Each object is added to the results List of this
* {@link ResultSetExtractor}.
* <p>
* Useful for the typical case of one object per row in the database table. The number of entries in the results will
* match the number of rows.
* <p>
* Note that a {@link RowMapper} object is typically stateless and thus reusable.
*
* @author Mark Paluch
* @since 3.0
* @see RowMapper
* @see AsyncCqlTemplate
*/
public class AsyncRowMapperResultSetExtractor<T> implements AsyncResultSetExtractor<List<T>> {
private final RowMapper<T> rowMapper;
/**
* Create a new {@link AsyncRowMapperResultSetExtractor}.
*
* @param rowMapper the {@link RowMapper} which creates an object for each row, must not be {@literal null}.
*/
public AsyncRowMapperResultSetExtractor(RowMapper<T> rowMapper) {
Assert.notNull(rowMapper, "RowMapper is must not be null");
this.rowMapper = rowMapper;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.ResultSetExtractor#extractData(com.datastax.driver.core.ResultSet)
*/
@Override
public ListenableFuture<List<T>> extractData(AsyncResultSet resultSet) throws DriverException, DataAccessException {
return AsyncResultStream.from(resultSet).map(rowMapper).collect(Collectors.toList());
}
}

View File

@@ -18,16 +18,16 @@ package org.springframework.data.cassandra.core.cql;
import org.springframework.dao.DataAccessException;
import org.springframework.util.concurrent.ListenableFuture;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.DriverException;
/**
* Generic callback interface for code that operates asynchronously on a Cassandra {@link Session}. Allows to execute
* Generic callback interface for code that operates asynchronously on a Cassandra {@link CqlSession}. Allows to execute
* any number of operations on a single session, using any type and number of statements.
* <p>
* This is particularly useful for delegating to existing data access code that expects a {@link Session} to work on and
* throws {@link DriverException}. For newly written code, it is strongly recommended to use {@link CqlTemplate}'s more
* specific operations, for example a {@code query} or {@code update} variant.
* This is particularly useful for delegating to existing data access code that expects a {@link CqlSession} to work on
* and throws {@link DriverException}. For newly written code, it is strongly recommended to use {@link CqlTemplate}'s
* more specific operations, for example a {@code query} or {@code update} variant.
*
* @author David Webb
* @author Mark Paluch
@@ -39,8 +39,8 @@ import com.datastax.driver.core.exceptions.DriverException;
public interface AsyncSessionCallback<T> {
/**
* Gets called by {@link CqlTemplate#execute} with an active Cassandra {@link Session}. Does not need to care about
* activating or closing the {@link Session}.
* Gets called by {@link CqlTemplate#execute} with an active Cassandra {@link CqlSession}. Does not need to care about
* activating or closing the {@link CqlSession}.
* <p>
* Allows for returning a result object created within the callback, i.e. a domain object or a collection of domain
* objects. Note that there's special support for single step actions: see {@link CqlTemplate#queryForObject} etc. A
@@ -54,5 +54,5 @@ public interface AsyncSessionCallback<T> {
* @see AsyncCqlTemplate#queryForObject(String, Class)
* @see AsyncCqlTemplate#queryForResultSet(String)
*/
ListenableFuture<T> doInSession(Session session) throws DriverException, DataAccessException;
ListenableFuture<T> doInSession(CqlSession session) throws DriverException, DataAccessException;
}

View File

@@ -20,11 +20,13 @@ import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
/**
* This {@link PreparedStatementCreator} maintains a static cache of all prepared statements for the duration of the JVM
@@ -43,7 +45,7 @@ import com.datastax.driver.core.exceptions.DriverException;
@Deprecated
public class CachedPreparedStatementCreator implements PreparedStatementCreator {
private static final Map<Session, Map<String, PreparedStatement>> CACHE = new ConcurrentHashMap<>();
private static final Map<CqlSession, Map<String, PreparedStatement>> CACHE = new ConcurrentHashMap<>();
protected final Logger log = LoggerFactory.getLogger(getClass());
@@ -74,11 +76,12 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator
* @see org.springframework.data.cassandra.core.cql.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session)
*/
@Override
public PreparedStatement createPreparedStatement(Session session) throws DriverException {
public PreparedStatement createPreparedStatement(CqlSession session) throws DriverException {
String cacheKey = String.valueOf(session.getLoggedKeyspace()).concat("|").concat(this.cql);
CqlIdentifier keyspace = session.getKeyspace().orElse(CqlIdentifier.fromCql("unknown"));
String cacheKey = keyspace.asInternal().concat("|").concat(this.cql);
log.debug("Cacheable PreparedStatement in Keyspace {}", session.getLoggedKeyspace());
log.debug("Cacheable PreparedStatement in Keyspace {}", keyspace.asCql(true));
Map<String, PreparedStatement> sessionCache = getOrCreateSessionLocalCache(session);
@@ -86,7 +89,7 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator
}
@SuppressWarnings("all")
private Map<String, PreparedStatement> getOrCreateSessionLocalCache(Session session) {
private Map<String, PreparedStatement> getOrCreateSessionLocalCache(CqlSession session) {
Map<String, PreparedStatement> sessionMap = CACHE.get(session);
@@ -107,7 +110,7 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator
}
@SuppressWarnings("all")
private PreparedStatement getOrPrepareStatement(Session session, String cacheKey,
private PreparedStatement getOrPrepareStatement(CqlSession session, String cacheKey,
Map<String, PreparedStatement> sessionCache) {
PreparedStatement preparedStatement = sessionCache.get(cacheKey);

View File

@@ -17,10 +17,10 @@ package org.springframework.data.cassandra.core.cql;
import java.util.Map;
import java.util.Optional;
import java.util.stream.StreamSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.data.cassandra.SessionFactory;
@@ -28,21 +28,18 @@ import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.driver.core.policies.RetryPolicy;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.oss.driver.api.core.ConsistencyLevel;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.retry.RetryPolicy;
/**
* {@link CassandraAccessor} provides access to a Cassandra {@link SessionFactory} and the
* {@link CassandraExceptionTranslator}.
* <p>
* Classes providing a higher abstraction level usually extend {@link CassandraAccessor} to provide a richer set of
* functionality on top of a {@link SessionFactory} using {@link Session}.
* functionality on top of a {@link SessionFactory} using {@link CqlSession}.
*
* @author David Webb
* @author Mark Paluch
@@ -52,38 +49,33 @@ import com.datastax.driver.core.querybuilder.QueryBuilder;
*/
public class CassandraAccessor implements InitializingBean {
/**
* Placeholder for default values.
*/
private static final Statement DEFAULTS = QueryBuilder.select().from("DEFAULT");
/** Logger available to subclasses */
protected final Logger logger = LoggerFactory.getLogger(getClass());
private CqlExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
/**
* If this variable is set to a non-negative value, it will be used for setting the {@code fetchSize} property on
* If this variable is set to a non-negative value, it will be used for setting the {@code pageSize} property on
* statements used for query processing.
*/
private int fetchSize = -1;
private int pageSize = -1;
/**
* If this variable is set to a value, it will be used for setting the {@code consistencyLevel} property on statements
* used for query processing.
*/
private @Nullable com.datastax.driver.core.ConsistencyLevel consistencyLevel;
private @Nullable ConsistencyLevel consistencyLevel;
/**
* If this variable is set to a value, it will be used for setting the {@code retryPolicy} property on statements used
* for query processing.
*/
private @Nullable com.datastax.driver.core.policies.RetryPolicy retryPolicy;
private @Deprecated @Nullable RetryPolicy retryPolicy;
private @Nullable SessionFactory sessionFactory;
/**
* Ensures the Cassandra {@link Session} and exception translator has been propertly set.
* Ensures the Cassandra {@link CqlSession} and exception translator has been propertly set.
*/
@Override
public void afterPropertiesSet() {
@@ -141,46 +133,72 @@ public class CassandraAccessor implements InitializingBean {
* transferring row data that will never be read by the application. Default is -1, indicating to use the CQL driver's
* default configuration (i.e. to not pass a specific fetch size setting on to the driver).
*
* @see Statement#setFetchSize(int)
* @see com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder#setPageSize(int)
* @deprecated since 3.0, use {@link #setPageSize(int)}
*/
@Deprecated
public void setFetchSize(int fetchSize) {
this.fetchSize = fetchSize;
setPageSize(fetchSize);
}
/**
* @return the fetch size specified for this template.
* @deprecated since 3.0, use {@link #getPageSize()}.
*/
@Deprecated
public int getFetchSize() {
return this.fetchSize;
return getPageSize();
}
/**
* Set the page size for this template. This is important for processing large result sets: Setting this higher than
* the default value will increase processing speed at the cost of memory consumption; setting this lower can avoid
* transferring row data that will never be read by the application. Default is -1, indicating to use the CQL driver's
* default configuration (i.e. to not pass a specific page size setting on to the driver).
*
* @see com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder#setPageSize(int)
*/
public void setPageSize(int fetchSize) {
this.pageSize = fetchSize;
}
/**
* @return the page size specified for this template.
*/
public int getPageSize() {
return this.pageSize;
}
/**
* Set the retry policy for this template. This is important for defining behavior when a request fails.
*
* @see Statement#setRetryPolicy(RetryPolicy)
* @see RetryPolicy
* @deprecated since 3.0. Use driver execution profiles instead.
*/
@Deprecated
public void setRetryPolicy(@Nullable RetryPolicy retryPolicy) {
this.retryPolicy = retryPolicy;
}
/**
* @return the {@link RetryPolicy} specified for this template.
* @deprecated since 3.0. Use driver execution profiles instead.
*/
@Nullable
@Deprecated
public RetryPolicy getRetryPolicy() {
return this.retryPolicy;
}
/**
* Sets the Cassandra {@link Session} used by this template to perform Cassandra data access operations. The
* Sets the Cassandra {@link CqlSession} used by this template to perform Cassandra data access operations. The
* {@code session} will replace the current {@link #getSessionFactory()} with {@link DefaultSessionFactory}.
*
* @param session Cassandra {@link Session} used by this template, must not be{@literal null}.
* @see com.datastax.driver.core.Session
* @param session Cassandra {@link CqlSession} used by this template, must not be{@literal null}.
* @see CqlSession
* @see DefaultSessionFactory
*/
public void setSession(Session session) {
public void setSession(CqlSession session) {
Assert.notNull(session, "Session must not be null");
@@ -188,16 +206,16 @@ public class CassandraAccessor implements InitializingBean {
}
/**
* Returns the Cassandra {@link Session} from {@link SessionFactory} used by this template to perform Cassandra data
* access operations.
* Returns the Cassandra {@link CqlSession} from {@link SessionFactory} used by this template to perform Cassandra
* data access operations.
*
* @return the Cassandra {@link Session} used by this template.
* @return the Cassandra {@link CqlSession} used by this template.
* @see com.datastax.driver.core.Session
* @deprecated since 2.0. This class uses a {@link SessionFactory} to dispatch CQL calls amongst different
* {@link Session}s during its lifecycle.
* {@link CqlSession}s during its lifecycle.
*/
@Deprecated
public Session getSession() {
public CqlSession getSession() {
Assert.state(getSessionFactory() != null, "SessionFactory was not properly initialized");
@@ -207,7 +225,7 @@ public class CassandraAccessor implements InitializingBean {
/**
* Sets the Cassandra {@link SessionFactory} used by this template to perform Cassandra data access operations.
*
* @param sessionFactory Cassandra {@link Session} used by this template. Must not be{@literal null}.
* @param sessionFactory Cassandra {@link CqlSession} used by this template. Must not be{@literal null}.
* @since 2.0
* @see com.datastax.driver.core.Session
*/
@@ -231,105 +249,82 @@ public class CassandraAccessor implements InitializingBean {
}
/**
* Prepare the given CQL Statement (or {@link com.datastax.driver.core.PreparedStatement}), applying statement
* settings such as retry policy and consistency level.
* Create a {@link SimpleStatement} given {@code cql}.
*
* @param statement the CQL Statement to prepare
* @see #setRetryPolicy(RetryPolicy)
* @see #setConsistencyLevel(ConsistencyLevel)
* @param cql the CQL query.
* @return the {@link SimpleStatement} to use.
* @since 3.0
*/
protected <T extends PreparedStatement> T applyStatementSettings(T statement) {
ConsistencyLevel consistencyLevel = getConsistencyLevel();
if (consistencyLevel != null) {
statement.setConsistencyLevel(consistencyLevel);
}
RetryPolicy retryPolicy = getRetryPolicy();
if (retryPolicy != null) {
statement.setRetryPolicy(retryPolicy);
}
return statement;
protected SimpleStatement newStatement(String cql) {
return SimpleStatement.newInstance(cql);
}
/**
* Prepare the given CQL Statement (or {@link com.datastax.driver.core.PreparedStatement}), applying statement
* settings such as fetch size, retry policy, and consistency level.
* Prepare the given CQL Statement applying statement settings such as page size and consistency level.
*
* @param statement the CQL Statement to prepare
* @see #setFetchSize(int)
* @see #setRetryPolicy(RetryPolicy)
* @see #setPageSize(int)
* @see #setConsistencyLevel(ConsistencyLevel)
*/
protected <T extends Statement> T applyStatementSettings(T statement) {
protected Statement<?> applyStatementSettings(Statement<?> statement) {
Statement<?> statementToUse = statement;
ConsistencyLevel consistencyLevel = getConsistencyLevel();
if (consistencyLevel != null && statement.getConsistencyLevel() == DEFAULTS.getConsistencyLevel()) {
statement.setConsistencyLevel(consistencyLevel);
if (getFetchSize() > -1 && statement.getPageSize() < 0) {
statementToUse = statementToUse.setPageSize(getFetchSize());
}
int fetchSize = getFetchSize();
if (fetchSize != -1 && statement.getFetchSize() == DEFAULTS.getFetchSize()) {
statement.setFetchSize(fetchSize);
if (consistencyLevel != null && statementToUse.getConsistencyLevel() == null) {
statementToUse = statementToUse.setConsistencyLevel(getConsistencyLevel());
}
RetryPolicy retryPolicy = getRetryPolicy();
if (retryPolicy != null && statement.getRetryPolicy() == DEFAULTS.getRetryPolicy()) {
statement.setRetryPolicy(retryPolicy);
}
return statement;
return statementToUse;
}
/**
* Translate the given {@link DriverException} into a generic {@link DataAccessException}.
* Translate the given {@link RuntimeException} into a generic {@link DataAccessException}.
* <p>
* The returned {@link DataAccessException} is supposed to contain the original {@code DriverException} as root cause.
* However, client code may not generally rely on this due to {@link DataAccessException}s possibly being caused by
* other resource APIs as well. That said, a {@code getRootCause() instanceof DataAccessException} check (and
* subsequent cast) is considered reliable when expecting Cassandra-based access to have happened.
* The returned {@link DataAccessException} is supposed to contain the original {@code RuntimeException} as root
* cause. However, client code may not generally rely on this due to {@link DataAccessException}s possibly being
* caused by other resource APIs as well. That said, a {@code getRootCause() instanceof DataAccessException} check
* (and subsequent cast) is considered reliable when expecting Cassandra-based access to have happened.
*
* @param ex the offending {@link DriverException}
* @return the DataAccessException, wrapping the {@code DriverException}
* @param ex the offending {@link RuntimeException}
* @return the DataAccessException, wrapping the {@code RuntimeException}
* @see <a href=
* "https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#dao-exceptions">Consistent
* exception hierarchy</a>
* @see DataAccessException
*/
@Nullable
protected DataAccessException translateExceptionIfPossible(DriverException ex) {
protected DataAccessException translateExceptionIfPossible(RuntimeException ex) {
Assert.notNull(ex, "DriverException must not be null");
Assert.notNull(ex, "RuntimeException must not be null");
return getExceptionTranslator().translateExceptionIfPossible(ex);
}
/**
* Translate the given {@link DriverException} into a generic {@link DataAccessException}.
* Translate the given {@link RuntimeException} into a generic {@link DataAccessException}.
* <p>
* The returned {@link DataAccessException} is supposed to contain the original {@code DriverException} as root cause.
* However, client code may not generally rely on this due to {@link DataAccessException}s possibly being caused by
* other resource APIs as well. That said, a {@code getRootCause() instanceof DataAccessException} check (and
* subsequent cast) is considered reliable when expecting Cassandra-based access to have happened.
* The returned {@link DataAccessException} is supposed to contain the original {@code RuntimeException} as root
* cause. However, client code may not generally rely on this due to {@link DataAccessException}s possibly being
* caused by other resource APIs as well. That said, a {@code getRootCause() instanceof DataAccessException} check
* (and subsequent cast) is considered reliable when expecting Cassandra-based access to have happened.
*
* @param task readable text describing the task being attempted
* @param cql CQL query or update that caused the problem (may be {@literal null})
* @param ex the offending {@link DriverException}
* @return the DataAccessException, wrapping the {@code DriverException}
* @param ex the offending {@link RuntimeException}
* @return the DataAccessException, wrapping the {@code RuntimeException}
* @see org.springframework.dao.DataAccessException#getRootCause()
* @see <a href=
* "https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#dao-exceptions">Consistent
* exception hierarchy</a>
*/
protected DataAccessException translate(String task, @Nullable String cql, DriverException ex) {
protected DataAccessException translate(String task, @Nullable String cql, RuntimeException ex) {
Assert.notNull(ex, "DriverException must not be null");
Assert.notNull(ex, "RuntimeException must not be null");
return getExceptionTranslator().translate(task, cql, ex);
}
@@ -345,49 +340,6 @@ public class CassandraAccessor implements InitializingBean {
return new ArgumentPreparedStatementBinder(args);
}
/**
* Constructs a new instance of the {@link ResultSetExtractor} initialized with and adapting the given
* {@link RowCallbackHandler}.
*
* @param rowCallbackHandler {@link RowCallbackHandler} to adapt as a {@link ResultSetExtractor}.
* @return a {@link ResultSetExtractor} implementation adapting an instance of the {@link RowCallbackHandler}.
* @see AsyncCqlTemplate.RowCallbackHandlerResultSetExtractor
* @see ResultSetExtractor
* @see RowCallbackHandler
*/
protected RowCallbackHandlerResultSetExtractor newResultSetExtractor(RowCallbackHandler rowCallbackHandler) {
return new RowCallbackHandlerResultSetExtractor(rowCallbackHandler);
}
/**
* Constructs a new instance of the {@link ResultSetExtractor} initialized with and adapting the given
* {@link RowMapper}.
*
* @param rowMapper {@link RowMapper} to adapt as a {@link ResultSetExtractor}.
* @return a {@link ResultSetExtractor} implementation adapting an instance of the {@link RowMapper}.
* @see ResultSetExtractor
* @see RowMapper
* @see RowMapperResultSetExtractor
*/
protected <T> RowMapperResultSetExtractor<T> newResultSetExtractor(RowMapper<T> rowMapper) {
return new RowMapperResultSetExtractor<>(rowMapper);
}
/**
* Constructs a new instance of the {@link ResultSetExtractor} initialized with and adapting the given
* {@link RowMapper}.
*
* @param rowMapper {@link RowMapper} to adapt as a {@link ResultSetExtractor}.
* @param rowsExpected number of expected rows in the {@link ResultSet}.
* @return a {@link ResultSetExtractor} implementation adapting an instance of the {@link RowMapper}.
* @see ResultSetExtractor
* @see RowMapper
* @see RowMapperResultSetExtractor
*/
protected <T> RowMapperResultSetExtractor<T> newResultSetExtractor(RowMapper<T> rowMapper, int rowsExpected) {
return new RowMapperResultSetExtractor<>(rowMapper, rowsExpected);
}
/**
* Create a new RowMapper for reading columns as key-value pairs.
*
@@ -425,28 +377,4 @@ public class CassandraAccessor implements InitializingBean {
.map(CqlProvider::getCql) //
.orElse(null);
}
/**
* Adapter to enable use of a {@link RowCallbackHandler} inside a {@link ResultSetExtractor}.
*/
protected static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor<Object> {
private final RowCallbackHandler rowCallbackHandler;
protected RowCallbackHandlerResultSetExtractor(RowCallbackHandler rowCallbackHandler) {
this.rowCallbackHandler = rowCallbackHandler;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.ResultSetExtractor#extractData(com.datastax.driver.core.ResultSet)
*/
@Override
@Nullable
public Object extractData(ResultSet resultSet) {
StreamSupport.stream(resultSet.spliterator(), false).forEach(rowCallbackHandler::processRow);
return null;
}
}
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.cassandra.core.cql;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -31,8 +30,11 @@ import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.WriteType;
import com.datastax.driver.core.exceptions.*;
import com.datastax.oss.driver.api.core.AllNodesFailedException;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.auth.AuthenticationException;
import com.datastax.oss.driver.api.core.metadata.Node;
import com.datastax.oss.driver.api.core.servererrors.*;
/**
* Simple {@link PersistenceExceptionTranslator} for Cassandra.
@@ -67,18 +69,14 @@ public class CassandraExceptionTranslator implements CqlExceptionTranslator {
return (DataAccessException) exception;
}
if (!(exception instanceof DriverException)) {
return null;
}
return translate(null, null, (DriverException) exception);
return translate(null, null, exception);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.cql.CQLExceptionTranslator#translate(java.lang.String, java.lang.String, com.datastax.driver.core.exceptions.DriverException)
* @see org.springframework.data.cassandra.cql.CQLExceptionTranslator#translate(java.lang.String, java.lang.String, com.datastax.oss.driver.api.core.DriverException)
*/
@Override
public DataAccessException translate(@Nullable String task, @Nullable String cql, DriverException exception) {
public DataAccessException translate(@Nullable String task, @Nullable String cql, RuntimeException exception) {
String message = buildMessage(task, cql, exception);
@@ -86,9 +84,10 @@ public class CassandraExceptionTranslator implements CqlExceptionTranslator {
// superclass would match before the subclass!
if (exception instanceof AuthenticationException) {
return new CassandraAuthenticationException(((AuthenticationException) exception).getHost(), message, exception);
return new CassandraAuthenticationException(((AuthenticationException) exception).getEndPoint(), message,
exception);
}
/* TODO ???
if (exception instanceof DriverInternalError) {
return new CassandraInternalException(message, exception);
}
@@ -96,10 +95,11 @@ public class CassandraExceptionTranslator implements CqlExceptionTranslator {
if (exception instanceof InvalidTypeException) {
return new CassandraTypeMismatchException(message, exception);
}
???
*/
if (exception instanceof ReadTimeoutException) {
return new CassandraReadTimeoutException(((ReadTimeoutException) exception).wasDataRetrieved(), message,
exception);
return new CassandraReadTimeoutException(((ReadTimeoutException) exception).wasDataPresent(), message, exception);
}
if (exception instanceof WriteTimeoutException) {
@@ -115,20 +115,16 @@ public class CassandraExceptionTranslator implements CqlExceptionTranslator {
if (exception instanceof UnavailableException) {
UnavailableException ux = (UnavailableException) exception;
return new CassandraInsufficientReplicasAvailableException(ux.getRequiredReplicas(), ux.getAliveReplicas(),
message, exception);
return new CassandraInsufficientReplicasAvailableException(ux.getRequired(), ux.getAlive(), message, exception);
}
if (exception instanceof OverloadedException || exception instanceof BootstrappingException) {
return new TransientDataAccessResourceException(message, exception);
}
if (exception instanceof AlreadyExistsException) {
AlreadyExistsException aex = (AlreadyExistsException) exception;
return aex.wasTableCreation() ? new CassandraTableExistsException(aex.getTable(), message, exception)
: new CassandraKeyspaceExistsException(aex.getKeyspace(), message, exception);
return new CassandraSchemaElementExistsException(aex.getMessage(), aex);
}
if (exception instanceof InvalidConfigurationInQueryException) {
@@ -147,12 +143,8 @@ public class CassandraExceptionTranslator implements CqlExceptionTranslator {
return new CassandraUnauthorizedException(message, exception);
}
if (exception instanceof TraceRetrievalException) {
return new CassandraTraceRetrievalException(message, exception);
}
if (exception instanceof NoHostAvailableException) {
return new CassandraConnectionFailureException(((NoHostAvailableException) exception).getErrors(), message,
if (exception instanceof AllNodesFailedException) {
return new CassandraConnectionFailureException(((AllNodesFailedException) exception).getErrors(), message,
exception);
}
@@ -160,11 +152,11 @@ public class CassandraExceptionTranslator implements CqlExceptionTranslator {
if (CONNECTION_FAILURE_TYPES.contains(exceptionType)) {
Map<InetSocketAddress, Throwable> errorMap = Collections.emptyMap();
Map<Node, Throwable> errorMap = Collections.emptyMap();
if (exception instanceof CoordinatorException) {
CoordinatorException cx = (CoordinatorException) exception;
errorMap = Collections.singletonMap(cx.getAddress(), exception);
errorMap = Collections.singletonMap(cx.getCoordinator(), exception);
}
return new CassandraConnectionFailureException(errorMap, message, exception);
@@ -189,7 +181,7 @@ public class CassandraExceptionTranslator implements CqlExceptionTranslator {
* @param ex the offending {@code DriverException}
* @return the message {@code String} to use
*/
protected String buildMessage(@Nullable String task, @Nullable String cql, DriverException ex) {
protected String buildMessage(@Nullable String task, @Nullable String cql, RuntimeException ex) {
if (StringUtils.hasText(task) || StringUtils.hasText(cql)) {
return task + "; CQL [" + cql + "]; " + ex.getMessage();

View File

@@ -20,8 +20,9 @@ import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedCaseInsensitiveMap;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.Row;
import com.datastax.oss.driver.api.core.cql.ColumnDefinition;
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
import com.datastax.oss.driver.api.core.cql.Row;
/**
* {@link RowMapper} implementation that creates a {@code java.util.Map} for each row, representing all columns as
@@ -53,7 +54,8 @@ public class ColumnMapRowMapper implements RowMapper<Map<String, Object>> {
Map<String, Object> mapOfColValues = createColumnMap(columnCount);
for (int i = 0; i < columnCount; i++) {
String key = getColumnKey(columnDefinitions.getName(i));
ColumnDefinition columnDefinition = columnDefinitions.get(i);
String key = getColumnKey(columnDefinition.getName().toString());
Object obj = getColumnValue(rs, i);
mapOfColValues.put(key, obj);
}

View File

@@ -20,10 +20,10 @@ import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.core.mapping.UnsupportedCassandraOperationException;
import org.springframework.lang.Nullable;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.oss.driver.api.core.DriverException;
/**
* Strategy interface for translating between {@link DriverException DriverExceptios} and Spring's data access
* Strategy interface for translating between {@link RuntimeException driver exceptions} and Spring's data access
* strategy-agnostic {@link DataAccessException} hierarchy.
*
* @author Mark Paluch
@@ -34,7 +34,7 @@ import com.datastax.driver.core.exceptions.DriverException;
public interface CqlExceptionTranslator extends PersistenceExceptionTranslator {
/**
* Translate the given {@link DriverException} into a generic {@link DataAccessException}.
* Translate the given {@link RuntimeException} into a generic {@link DataAccessException}.
* <p>
* The returned {@link DataAccessException} is supposed to contain the original {@code DriverException} as root cause.
* However, client code may not generally rely on this due to {@link DataAccessException}s possibly being caused by
@@ -44,10 +44,10 @@ public interface CqlExceptionTranslator extends PersistenceExceptionTranslator {
* @param task readable text describing the task being attempted.
* @param cql CQL query or update that caused the problem (may be {@literal null}).
* @param ex the offending {@link DriverException}.
* @return the DataAccessException, wrapping the {@link DriverException}.
* @return the DataAccessException, wrapping the {@link RuntimeException}.
* @see org.springframework.dao.DataAccessException#getRootCause()
*/
default DataAccessException translate(@Nullable String task, @Nullable String cql, DriverException ex) {
default DataAccessException translate(@Nullable String task, @Nullable String cql, RuntimeException ex) {
DataAccessException translated = translateExceptionIfPossible(ex);
return translated == null ? new UnsupportedCassandraOperationException("Cannot translate exception", ex)

View File

@@ -20,9 +20,6 @@ import java.util.regex.Pattern;
import org.springframework.util.Assert;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.TableMetadata;
/**
* This encapsulates the logic for CQL quoted and unquoted identifiers.
* <p>
@@ -269,4 +266,14 @@ public final class CqlIdentifier implements Comparable<CqlIdentifier>, Serializa
public String toString() {
return toCql();
}
/**
* Create a Cassandra driver {@link com.datastax.oss.driver.api.core.CqlIdentifier} from this {@link CqlIdentifier}.
*
* @return the {@link com.datastax.oss.driver.api.core.CqlIdentifier} from this {@link CqlIdentifierIdentifier}.
* @since 3.0
*/
public com.datastax.oss.driver.api.core.CqlIdentifier toCqlIdentifier() {
return com.datastax.oss.driver.api.core.CqlIdentifier.fromCql(this.identifier);
}
}

View File

@@ -23,10 +23,10 @@ import org.springframework.dao.DataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.lang.Nullable;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Statement;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.Statement;
/**
* Interface specifying a basic set of CQL operations. Implemented by {@link CqlTemplate}. Not often used directly, but
@@ -40,14 +40,14 @@ import com.datastax.driver.core.Statement;
public interface CqlOperations {
// -------------------------------------------------------------------------
// Methods dealing with a plain com.datastax.driver.core.Session
// Methods dealing with a plain com.datastax.oss.driver.api.core.CqlSession
// -------------------------------------------------------------------------
/**
* Execute a CQL data access operation, implemented as callback action working on a
* {@link com.datastax.driver.core.Session}. This allows for implementing arbitrary data access operations, within
* Spring's managed CQL environment: that is, converting CQL
* {@link com.datastax.driver.core.exceptions.DriverException}s into Spring's {@link DataAccessException} hierarchy.
* Spring's managed CQL environment: that is, converting CQL {@link com.datastax.oss.driver.api.core.DriverException}s
* into Spring's {@link DataAccessException} hierarchy.
* <p>
* The callback action can return a result object, for example a domain object or a collection of domain objects.
*
@@ -101,7 +101,7 @@ public interface CqlOperations {
* Execute a CQL data access operation, implemented as callback action working on a CQL {@link PreparedStatement}.
* This allows for implementing arbitrary data access operations on a single Statement, within Spring's managed CQL
* environment: that is, participating in Spring-managed transactions and converting CQL
* {@link com.datastax.driver.core.exceptions.DriverException}s into Spring's {@link DataAccessException} hierarchy.
* {@link com.datastax.oss.driver.api.core.DriverException}s into Spring's {@link DataAccessException} hierarchy.
* <p>
* The callback action can return a result object, for example a domain object or a collection of domain objects.
*
@@ -482,7 +482,7 @@ public interface CqlOperations {
Iterable<Row> queryForRows(String cql, Object... args) throws DataAccessException;
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
// -------------------------------------------------------------------------
/**
@@ -492,7 +492,7 @@ public interface CqlOperations {
* @return boolean value whether the statement was applied.
* @throws DataAccessException if there is any problem executing the query.
*/
boolean execute(Statement statement) throws DataAccessException;
boolean execute(Statement<?> statement) throws DataAccessException;
/**
* Execute a query given static CQL, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
@@ -507,7 +507,7 @@ public interface CqlOperations {
* @see #query(String, ResultSetExtractor, Object...)
*/
@Nullable
<T> T query(Statement statement, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
<T> T query(Statement<?> statement, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
/**
* Execute a query given static CQL, reading the {@link ResultSet} on a per-row basis with a
@@ -521,7 +521,7 @@ public interface CqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #query(String, RowCallbackHandler, Object[])
*/
void query(Statement statement, RowCallbackHandler rowCallbackHandler) throws DataAccessException;
void query(Statement<?> statement, RowCallbackHandler rowCallbackHandler) throws DataAccessException;
/**
* Execute a query given static CQL, mapping each row to a Java object via a {@link RowMapper}.
@@ -535,7 +535,7 @@ public interface CqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #query(String, RowMapper, Object[])
*/
<T> List<T> query(Statement statement, RowMapper<T> rowMapper) throws DataAccessException;
<T> List<T> query(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException;
/**
* Execute a query for a result {@link List}, given static CQL.
@@ -552,7 +552,7 @@ public interface CqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForList(String, Object[])
*/
List<Map<String, Object>> queryForList(Statement statement) throws DataAccessException;
List<Map<String, Object>> queryForList(Statement<?> statement) throws DataAccessException;
/**
* Execute a query for a result {@link List}, given static CQL.
@@ -571,7 +571,7 @@ public interface CqlOperations {
* @see #queryForList(String, Class, Object[])
* @see SingleColumnRowMapper
*/
<T> List<T> queryForList(Statement statement, Class<T> elementType) throws DataAccessException;
<T> List<T> queryForList(Statement<?> statement, Class<T> elementType) throws DataAccessException;
/**
* Execute a query for a result Map, given static CQL.
@@ -590,7 +590,7 @@ public interface CqlOperations {
* @see #queryForMap(String, Object[])
* @see ColumnMapRowMapper
*/
Map<String, Object> queryForMap(Statement statement) throws DataAccessException;
Map<String, Object> queryForMap(Statement<?> statement) throws DataAccessException;
/**
* Execute a query for a result object, given static CQL.
@@ -610,7 +610,7 @@ public interface CqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForObject(String, Class, Object[])
*/
<T> T queryForObject(Statement statement, Class<T> requiredType) throws DataAccessException;
<T> T queryForObject(Statement<?> statement, Class<T> requiredType) throws DataAccessException;
/**
* Execute a query given static CQL, mapping a single result row to a Java object via a {@link RowMapper}.
@@ -626,7 +626,7 @@ public interface CqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForObject(String, RowMapper, Object[])
*/
<T> T queryForObject(Statement statement, RowMapper<T> rowMapper) throws DataAccessException;
<T> T queryForObject(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException;
/**
* Execute a query for a ResultSet, given static CQL.
@@ -642,7 +642,7 @@ public interface CqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForResultSet(String, Object[])
*/
ResultSet queryForResultSet(Statement statement) throws DataAccessException;
ResultSet queryForResultSet(Statement<?> statement) throws DataAccessException;
/**
* Execute a query for Rows, given static CQL.
@@ -658,10 +658,10 @@ public interface CqlOperations {
* @throws DataAccessException if there is any problem executing the query.
* @see #queryForResultSet(String, Object[])
*/
Iterable<Row> queryForRows(Statement statement) throws DataAccessException;
Iterable<Row> queryForRows(Statement<?> statement) throws DataAccessException;
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.PreparedStatement
// Methods dealing with com.datastax.oss.driver.api.core.cql.PreparedStatement
// -------------------------------------------------------------------------
/**
@@ -672,14 +672,13 @@ public interface CqlOperations {
* @return boolean value whether the statement was applied.
* @throws DataAccessException if there is any problem executing the query.
*/
// TODO: Interferes with execute(session callback lambda)
boolean execute(PreparedStatementCreator psc) throws DataAccessException;
/**
* Execute a CQL data access operation, implemented as callback action working on a CQL {@link PreparedStatement}.
* This allows for implementing arbitrary data access operations on a single {@link PreparedStatement}, within
* Spring's managed CQL environment: that is, participating in Spring-managed transactions and converting CQL
* {@link com.datastax.driver.core.exceptions.DriverException}s into Spring's {@link DataAccessException} hierarchy.
* {@link com.datastax.oss.driver.api.core.DriverException}s into Spring's {@link DataAccessException} hierarchy.
* <p>
* The callback action can return a result object, for example a domain object or a collection of domain objects.
*

View File

@@ -18,8 +18,8 @@ package org.springframework.data.cassandra.core.cql;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.StreamSupport;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
@@ -27,20 +27,18 @@ import org.springframework.data.cassandra.SessionFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.Host;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.metadata.Node;
/**
* <b>This is the central class in the CQL core package.</b> It simplifies the use of CQL and helps to avoid common
* errors. It executes core CQL workflow, leaving application code to provide CQL and extract results. This class
* executes CQL queries or updates, initiating iteration over {@link ResultSet}s and catching {@link DriverException}
* executes CQL queries or updates, initiating iteration over {@link ResultSet}s and catching {@link RuntimeException}
* exceptions and translating them to the generic, more informative exception hierarchy defined in the
* {@code org.springframework.dao} package.
* <p>
@@ -49,10 +47,10 @@ import com.datastax.driver.core.exceptions.DriverException;
* and any necessary parameters. The {@link ResultSetExtractor} interface extracts values from a {@link ResultSet}. See
* also {@link PreparedStatementBinder} and {@link RowMapper} for two popular alternative callback interfaces.
* <p>
* Can be used within a service implementation via direct instantiation with a {@link Session} reference, or get
* prepared in an application context and given to services as bean reference. Note: The {@link Session} should always
* be configured as a bean in the application context, in the first case given to the service directly, in the second
* case to the prepared template.
* Can be used within a service implementation via direct instantiation with a {@link CqlSession} reference, or get
* prepared in an application context and given to services as bean reference. Note: The {@link CqlSession} should
* always be configured as a bean in the application context, in the first case given to the service directly, in the
* second case to the prepared template.
* <p>
* Because this class is parameterizable by the callback interfaces and the
* {@link org.springframework.dao.support.PersistenceExceptionTranslator} interface, there should be no need to subclass
@@ -89,13 +87,12 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
public CqlTemplate() {}
/**
* Create a new {@link CqlTemplate} initialized with the given {@link Session}.
* Create a new {@link CqlTemplate} initialized with the given {@link CqlSession}.
*
* @param session the active Cassandra {@link Session}.
* @throws IllegalStateException if {@link Session} is {@literal null}.
* @see com.datastax.driver.core.Session
* @param session the active Cassandra {@link CqlSession}.
* @throws IllegalStateException if {@link CqlSession} is {@literal null}.
*/
public CqlTemplate(Session session) {
public CqlTemplate(CqlSession session) {
Assert.notNull(session, "Session must not be null");
@@ -116,7 +113,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
}
// -------------------------------------------------------------------------
// Methods dealing with a plain com.datastax.driver.core.Session
// Methods dealing with a plain com.datastax.oss.driver.api.core.CqlSession
// -------------------------------------------------------------------------
/*
@@ -167,7 +164,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
logger.debug("Executing CQL Statement [{}]", cql);
}
SimpleStatement statement = applyStatementSettings(new SimpleStatement(cql));
Statement<?> statement = applyStatementSettings(newStatement(cql));
ResultSet results = getCurrentSession().execute(statement);
@@ -263,15 +260,15 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
}
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
// -------------------------------------------------------------------------
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#execute(com.datastax.driver.core.Statement)
* @see org.springframework.data.cassandra.core.cqlOperations#execute(com.datastax.oss.driver.api.core.cql.Statement)
*/
@Override
public boolean execute(Statement statement) throws DataAccessException {
public boolean execute(Statement<?> statement) throws DataAccessException {
Assert.notNull(statement, "CQL Statement must not be null");
@@ -280,10 +277,10 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#query(com.datastax.driver.core.Statement, org.springframework.data.cassandra.core.cql.ResultSetExtractor)
* @see org.springframework.data.cassandra.core.cqlOperations#query(com.datastax.oss.driver.api.core.cql.Statement, org.springframework.data.cassandra.core.cql.ResultSetExtractor)
*/
@Override
public <T> T query(Statement statement, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
public <T> T query(Statement<?> statement, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
Assert.notNull(statement, "CQL Statement must not be null");
Assert.notNull(resultSetExtractor, "ResultSetExtractor must not be null");
@@ -301,87 +298,87 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#query(com.datastax.driver.core.Statement, org.springframework.data.cassandra.core.cql.RowCallbackHandler)
* @see org.springframework.data.cassandra.core.cqlOperations#query(com.datastax.oss.driver.api.core.cql.Statement, org.springframework.data.cassandra.core.cql.RowCallbackHandler)
*/
@Override
public void query(Statement statement, RowCallbackHandler rowCallbackHandler) throws DataAccessException {
public void query(Statement<?> statement, RowCallbackHandler rowCallbackHandler) throws DataAccessException {
query(statement, newResultSetExtractor(rowCallbackHandler));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#query(com.datastax.driver.core.Statement, org.springframework.data.cassandra.core.cql.RowMapper)
* @see org.springframework.data.cassandra.core.cqlOperations#query(com.datastax.oss.driver.api.core.cql.Statement, org.springframework.data.cassandra.core.cql.RowMapper)
*/
@Override
public <T> List<T> query(Statement statement, RowMapper<T> rowMapper) throws DataAccessException {
public <T> List<T> query(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException {
// noinspection ConstantConditions
return query(statement, newResultSetExtractor(rowMapper));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForList(com.datastax.driver.core.Statement)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForList(com.datastax.oss.driver.api.core.cql.Statement)
*/
@Override
public List<Map<String, Object>> queryForList(Statement statement) throws DataAccessException {
public List<Map<String, Object>> queryForList(Statement<?> statement) throws DataAccessException {
// noinspection ConstantConditions
return query(statement, newResultSetExtractor(newColumnMapRowMapper()));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForList(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForList(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> List<T> queryForList(Statement statement, Class<T> elementType) throws DataAccessException {
public <T> List<T> queryForList(Statement<?> statement, Class<T> elementType) throws DataAccessException {
// noinspection ConstantConditions
return query(statement, newResultSetExtractor(newSingleColumnRowMapper(elementType)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForMap(com.datastax.driver.core.Statement)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForMap(com.datastax.oss.driver.api.core.cql.Statement)
*/
@Override
public Map<String, Object> queryForMap(Statement statement) throws DataAccessException {
public Map<String, Object> queryForMap(Statement<?> statement) throws DataAccessException {
// noinspection ConstantConditions
return queryForObject(statement, newColumnMapRowMapper());
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForObject(com.datastax.driver.core.Statement, java.lang.Class)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForObject(com.datastax.oss.driver.api.core.cql.Statement, java.lang.Class)
*/
@Override
public <T> T queryForObject(Statement statement, Class<T> requiredType) throws DataAccessException {
public <T> T queryForObject(Statement<?> statement, Class<T> requiredType) throws DataAccessException {
return queryForObject(statement, newSingleColumnRowMapper(requiredType));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForObject(com.datastax.driver.core.Statement, org.springframework.data.cassandra.core.cql.RowMapper)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForObject(com.datastax.oss.driver.api.core.cql.Statement, org.springframework.data.cassandra.core.cql.RowMapper)
*/
@Override
public <T> T queryForObject(Statement statement, RowMapper<T> rowMapper) throws DataAccessException {
public <T> T queryForObject(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException {
return DataAccessUtils.requiredSingleResult(query(statement, newResultSetExtractor(rowMapper)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForResultSet(com.datastax.driver.core.Statement)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForResultSet(com.datastax.oss.driver.api.core.cql.Statement)
*/
@Override
public ResultSet queryForResultSet(Statement statement) throws DataAccessException {
public ResultSet queryForResultSet(Statement<?> statement) throws DataAccessException {
// noinspection ConstantConditions
return query(statement, rs -> rs);
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForRows(com.datastax.driver.core.Statement)
* @see org.springframework.data.cassandra.core.cqlOperations#queryForRows(com.datastax.oss.driver.api.core.cql.Statement)
*/
@Override
public Iterable<Row> queryForRows(Statement statement) throws DataAccessException {
public Iterable<Row> queryForRows(Statement<?> statement) throws DataAccessException {
return () -> queryForResultSet(statement).iterator();
}
@@ -445,10 +442,9 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
logger.debug("Preparing statement [{}] using {}", toCql(preparedStatementCreator), preparedStatementCreator);
}
Session session = getCurrentSession();
CqlSession session = getCurrentSession();
return action.doInPreparedStatement(session,
applyStatementSettings(preparedStatementCreator.createPreparedStatement(session)));
return action.doInPreparedStatement(session, preparedStatementCreator.createPreparedStatement(session));
} catch (DriverException e) {
throw translateException("PreparedStatementCallback", toCql(preparedStatementCreator), e);
@@ -505,7 +501,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
logger.debug("Preparing statement [{}] using {}", toCql(preparedStatementCreator), preparedStatementCreator);
}
Session session = getCurrentSession();
CqlSession session = getCurrentSession();
PreparedStatement preparedStatement = preparedStatementCreator.createPreparedStatement(session);
@@ -513,7 +509,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
logger.debug("Executing prepared statement [{}]", preparedStatement);
}
BoundStatement boundStatement = applyStatementSettings(
Statement<?> boundStatement = applyStatementSettings(
psb != null ? psb.bindValues(preparedStatement) : preparedStatement.bind());
ResultSet results = session.execute(boundStatement);
@@ -700,14 +696,28 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
}
/* (non-Javadoc) */
private Set<Host> getHosts() {
return getCurrentSession().getCluster().getMetadata().getAllHosts();
private Collection<Node> getHosts() {
return getCurrentSession().getMetadata().getNodes().values();
}
// -------------------------------------------------------------------------
// Implementation hooks and helper methods
// -------------------------------------------------------------------------
/**
* Translate the given {@link RuntimeException} into a generic {@link DataAccessException}.
*
* @param task readable text describing the task being attempted
* @param cql CQL query or update that caused the problem (may be {@literal null})
* @param RuntimeException the offending {@code RuntimeException}.
* @return the exception translation {@link Function}
* @see CqlProvider
*/
protected DataAccessException translateException(String task, @Nullable String cql,
RuntimeException RuntimeException) {
return translate(task, cql, RuntimeException);
}
/**
* Create a new CQL-based {@link PreparedStatementCreator} using the CQL passed in. By default, we'll create an
* {@link SimplePreparedStatementCreator}. This method allows for the creation to be overridden by subclasses.
@@ -720,19 +730,49 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
}
/**
* Translate the given {@link DriverException} into a generic {@link DataAccessException}.
* Constructs a new instance of the {@link ResultSetExtractor} initialized with and adapting the given
* {@link RowCallbackHandler}.
*
* @param task readable text describing the task being attempted
* @param cql CQL query or update that caused the problem (may be {@literal null})
* @param driverException the offending {@code RuntimeException}.
* @return the exception translation {@link Function}
* @see CqlProvider
* @param rowCallbackHandler {@link RowCallbackHandler} to adapt as a {@link ResultSetExtractor}.
* @return a {@link ResultSetExtractor} implementation adapting an instance of the {@link RowCallbackHandler}.
* @see AsyncCqlTemplate.AsyncRowCallbackHandlerResultSetExtractor
* @see ResultSetExtractor
* @see RowCallbackHandler
*/
protected DataAccessException translateException(String task, @Nullable String cql, DriverException driverException) {
return translate(task, cql, driverException);
protected RowCallbackHandlerResultSetExtractor newResultSetExtractor(RowCallbackHandler rowCallbackHandler) {
return new RowCallbackHandlerResultSetExtractor(rowCallbackHandler);
}
private Session getCurrentSession() {
/**
* Constructs a new instance of the {@link ResultSetExtractor} initialized with and adapting the given
* {@link RowMapper}.
*
* @param rowMapper {@link RowMapper} to adapt as a {@link ResultSetExtractor}.
* @return a {@link ResultSetExtractor} implementation adapting an instance of the {@link RowMapper}.
* @see ResultSetExtractor
* @see RowMapper
* @see RowMapperResultSetExtractor
*/
protected <T> RowMapperResultSetExtractor<T> newResultSetExtractor(RowMapper<T> rowMapper) {
return new RowMapperResultSetExtractor<>(rowMapper);
}
/**
* Constructs a new instance of the {@link ResultSetExtractor} initialized with and adapting the given
* {@link RowMapper}.
*
* @param rowMapper {@link RowMapper} to adapt as a {@link ResultSetExtractor}.
* @param rowsExpected number of expected rows in the {@link ResultSet}.
* @return a {@link ResultSetExtractor} implementation adapting an instance of the {@link RowMapper}.
* @see ResultSetExtractor
* @see RowMapper
* @see RowMapperResultSetExtractor
*/
protected <T> RowMapperResultSetExtractor<T> newResultSetExtractor(RowMapper<T> rowMapper, int rowsExpected) {
return new RowMapperResultSetExtractor<>(rowMapper, rowsExpected);
}
private CqlSession getCurrentSession() {
SessionFactory sessionFactory = getSessionFactory();
@@ -741,4 +781,28 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
return sessionFactory.getSession();
}
/**
* Adapter to enable use of a {@link RowCallbackHandler} inside a {@link ResultSetExtractor}.
*/
protected static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor<Object> {
private final RowCallbackHandler rowCallbackHandler;
protected RowCallbackHandlerResultSetExtractor(RowCallbackHandler rowCallbackHandler) {
this.rowCallbackHandler = rowCallbackHandler;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.cql.ResultSetExtractor#extractData(com.datastax.driver.core.ResultSet)
*/
@Override
@Nullable
public Object extractData(ResultSet resultSet) {
StreamSupport.stream(resultSet.spliterator(), false).forEach(rowCallbackHandler::processRow);
return null;
}
}
}

View File

@@ -1,153 +0,0 @@
/*
* Copyright 2016-2020 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
*
* https://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.cql;
import io.netty.util.concurrent.ImmediateExecutor;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.FailureCallback;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.util.concurrent.SettableListenableFuture;
import org.springframework.util.concurrent.SuccessCallback;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
/**
* Adapter class to adapt Guava's {@link com.google.common.util.concurrent.ListenableFuture} into a Spring
* {@link ListenableFuture}.
*
* @author Mark Paluch
* @since 2.0
*/
public class GuavaListenableFutureAdapter<T> implements ListenableFuture<T> {
private final com.google.common.util.concurrent.ListenableFuture<T> adaptee;
private final ListenableFuture<T> future;
/**
* Create a new {@link GuavaListenableFutureAdapter} given a Guava
* {@link com.google.common.util.concurrent.ListenableFuture} and a {@link PersistenceExceptionTranslator}.
*
* @param adaptee must not be {@literal null}.
* @param persistenceExceptionTranslator must not be {@literal null}.
*/
public GuavaListenableFutureAdapter(com.google.common.util.concurrent.ListenableFuture<T> adaptee,
PersistenceExceptionTranslator persistenceExceptionTranslator) {
Assert.notNull(adaptee, "ListenableFuture must not be null");
Assert.notNull(persistenceExceptionTranslator, "PersistenceExceptionTranslator must not be null");
this.adaptee = adaptee;
this.future = adaptListenableFuture(adaptee, persistenceExceptionTranslator);
}
private static <T> ListenableFuture<T> adaptListenableFuture(
com.google.common.util.concurrent.ListenableFuture<T> guavaFuture,
PersistenceExceptionTranslator exceptionTranslator) {
SettableListenableFuture<T> settableFuture = new SettableListenableFuture<>();
Futures.addCallback(guavaFuture, new FutureCallback<T>() {
@Override
public void onSuccess(@Nullable T result) {
settableFuture.set(result);
}
@Override
public void onFailure(Throwable t) {
if (t instanceof RuntimeException) {
DataAccessException dataAccessException = exceptionTranslator
.translateExceptionIfPossible((RuntimeException) t);
if (dataAccessException != null) {
settableFuture.setException(dataAccessException);
return;
}
}
settableFuture.setException(t);
}
}, ImmediateExecutor.INSTANCE);
return settableFuture;
}
/* (non-Javadoc)
* @see org.springframework.util.concurrent.ListenableFuture#addCallback(org.springframework.util.concurrent.ListenableFutureCallback)
*/
@Override
public void addCallback(ListenableFutureCallback<? super T> callback) {
future.addCallback(callback);
}
/* (non-Javadoc)
* @see org.springframework.util.concurrent.ListenableFuture#addCallback(org.springframework.util.concurrent.SuccessCallback, org.springframework.util.concurrent.FailureCallback)
*/
@Override
public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) {
future.addCallback(successCallback, failureCallback);
}
/* (non-Javadoc)
* @see java.util.concurrent.Future#cancel(boolean)
*/
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return adaptee.cancel(mayInterruptIfRunning);
}
/* (non-Javadoc)
* @see java.util.concurrent.Future#isCancelled()
*/
@Override
public boolean isCancelled() {
return adaptee.isCancelled();
}
/* (non-Javadoc)
* @see java.util.concurrent.Future#isDone()
*/
@Override
public boolean isDone() {
return future.isDone();
}
/* (non-Javadoc)
* @see java.util.concurrent.Future#get()
*/
@Override
public T get() throws InterruptedException, ExecutionException {
return future.get();
}
/* (non-Javadoc)
* @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit)
*/
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return future.get(timeout, unit);
}
}

View File

@@ -17,14 +17,13 @@ package org.springframework.data.cassandra.core.cql;
import java.util.Collection;
import com.datastax.driver.core.Host;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.metadata.Node;
/**
* An interface used by {@link CqlTemplate} for mapping {@link Host}s of a {@link com.datastax.driver.core.Metadata} on
* a per-item basis.. Implementations of this interface perform the actual work of mapping each host to a result object,
* but don't need to worry about exception handling. {@link DriverException} will be caught and handled by the calling
* {@link CqlTemplate}.
* An interface used by {@link CqlTemplate} for mapping {@link Node}s of metadata on a per-item basis.. Implementations
* of this interface perform the actual work of mapping each host to a result object, but don't need to worry about
* exception handling. {@link DriverException} will be caught and handled by the calling {@link CqlTemplate}.
*
* @author Matthew T. Adams
* @author Mark Paluch
@@ -34,13 +33,12 @@ import com.datastax.driver.core.exceptions.DriverException;
public interface HostMapper<T> {
/**
* Implementations must implement this method to map each {@link Host} in the
* {@link com.datastax.driver.core.Metadata}.
* Implementations must implement this method to map each {@link Node}.
*
* @param hosts the {@link Iterable} of {@link Host}s to map, must not be {@literal null}.
* @param hosts the {@link Iterable} of {@link Node}s to map, must not be {@literal null}.
* @return the result objects for the given hosts.
* @throws DriverException if a {@link DriverException} is encountered mapping values (that is, there's no need to
* catch {@link DriverException}).
*/
Collection<T> mapHosts(Iterable<Host> hosts) throws DriverException;
Collection<T> mapHosts(Iterable<Node> hosts) throws DriverException;
}

View File

@@ -19,6 +19,8 @@ import java.util.regex.Pattern;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
/**
* This encapsulates the logic for keyspace identifiers.
* <p/>
@@ -28,7 +30,9 @@ import org.springframework.util.Assert;
* @see #toCql()
* @see #toString()
* @author Matthew T. Adams
* @deprecated since 3.0, use {@link com.datastax.oss.driver.api.core.CqlIdentifier}.
*/
@Deprecated
public final class KeyspaceIdentifier implements Comparable<KeyspaceIdentifier> {
public static final String REGEX = "(?i)[a-z][\\w]{0,47}";
@@ -137,4 +141,14 @@ public final class KeyspaceIdentifier implements Comparable<KeyspaceIdentifier>
public int compareTo(KeyspaceIdentifier that) {
return this.identifier.compareTo(that.identifier);
}
/**
* Create a {@link CqlIdentifier} from this {@link KeyspaceIdentifier}.
*
* @return the {@link CqlIdentifier} from this {@link KeyspaceIdentifier}.
* @since 3.0
*/
public CqlIdentifier toCqlIdentifier() {
return CqlIdentifier.fromCql(this.identifier);
}
}

View File

@@ -15,9 +15,9 @@
*/
package org.springframework.data.cassandra.core.cql;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.cql.BoundStatement;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
/**
* General callback interface used by the {@link CqlTemplate} and {@link ReactiveCqlTemplate} classes.
@@ -35,7 +35,7 @@ import com.datastax.driver.core.exceptions.DriverException;
* @author David Webb
* @author Mark Paluch
* @see CqlTemplate#query(String, PreparedStatementBinder, ResultSetExtractor)
* @see AsyncCqlTemplate#query(AsyncPreparedStatementCreator, PreparedStatementBinder, ResultSetExtractor)
* @see AsyncCqlTemplate#query(String, PreparedStatementBinder, AsyncResultSetExtractor)
* @see ReactiveCqlTemplate#query(String, PreparedStatementBinder, ReactiveResultSetExtractor)
*/
@FunctionalInterface

Some files were not shown because too many files have changed in this diff Show More