diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/CassandraWriteTimeoutException.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/CassandraWriteTimeoutException.java index a99ffee75..8feb10c60 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/CassandraWriteTimeoutException.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/CassandraWriteTimeoutException.java @@ -16,23 +16,26 @@ package org.springframework.data.cassandra; import org.springframework.dao.QueryTimeoutException; +import org.springframework.lang.Nullable; /** * Spring data access exception for a Cassandra write timeout. * * @author Matthew T. Adams + * @author Mark Paluch */ public class CassandraWriteTimeoutException extends QueryTimeoutException { private static final long serialVersionUID = -4374826375213670718L; - private String writeType; + private @Nullable String writeType; - public CassandraWriteTimeoutException(String writeType, String msg, Throwable cause) { + public CassandraWriteTimeoutException(@Nullable String writeType, String msg, Throwable cause) { super(msg, cause); this.writeType = writeType; } + @Nullable public String getWriteType() { return writeType; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractCassandraConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractCassandraConfiguration.java index 86dc868d1..ecbc63f3a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractCassandraConfiguration.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractCassandraConfiguration.java @@ -34,6 +34,10 @@ import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver; import org.springframework.data.cassandra.core.mapping.Table; import org.springframework.data.convert.CustomConversions; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import com.datastax.driver.core.Session; /** * Base class for Spring Data Cassandra configuration using JavaConfig. @@ -47,7 +51,21 @@ import org.springframework.data.mapping.context.MappingContext; public abstract class AbstractCassandraConfiguration extends AbstractClusterConfiguration implements BeanClassLoaderAware { - private ClassLoader beanClassLoader; + private @Nullable ClassLoader beanClassLoader; + + /** + * Returns the initialized {@link Session} instance. + * + * @return the {@link Session}. + * @throws IllegalStateException if the session factory is not initialized. + */ + protected Session getRequiredSession() { + + CassandraSessionFactoryBean factoryBean = session(); + Assert.state(factoryBean.getObject() != null, "Session factory not initialized"); + + return factoryBean.getObject(); + } /** * Creates a {@link CassandraSessionFactoryBean} that provides a Cassandra {@link com.datastax.driver.core.Session}. @@ -55,8 +73,6 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf * {@link #getKeyspaceName() configured keyspace}. * * @return the {@link CassandraSessionFactoryBean}. - * @throws ClassNotFoundException if an error occurs initializing the initial entity set, see - * {@link #cassandraMapping()} * @see #cluster() * @see #cassandraConverter() * @see #getKeyspaceName() @@ -65,11 +81,11 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf * @see #getShutdownScripts() */ @Bean - public CassandraSessionFactoryBean session() throws ClassNotFoundException { + public CassandraSessionFactoryBean session() { CassandraSessionFactoryBean session = new CassandraSessionFactoryBean(); - session.setCluster(cluster().getObject()); + session.setCluster(getRequiredCluster()); session.setConverter(cassandraConverter()); session.setKeyspaceName(getKeyspaceName()); session.setSchemaAction(getSchemaAction()); @@ -84,13 +100,11 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf * {@link org.springframework.data.cassandra.core.CassandraTemplate}. * * @return {@link SessionFactory} used to initialize the Template API. - * @throws ClassNotFoundException if an error occurs initializing the initial entity set, see - * {@link #cassandraMapping()} * @since 2.0 */ @Bean - public SessionFactory sessionFactory() throws ClassNotFoundException { - return new DefaultSessionFactory(session().getObject()); + public SessionFactory sessionFactory() { + return new DefaultSessionFactory(getRequiredSession()); } /** @@ -98,19 +112,21 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf * {@link #customConversions()}. * * @return {@link CassandraConverter} used to convert Java and Cassandra value types during the mapping process. - * @throws ClassNotFoundException if an error occurs initializing the initial entity set, see - * {@link #cassandraMapping()} * @see #cassandraMapping() * @see #customConversions() */ @Bean - public CassandraConverter cassandraConverter() throws ClassNotFoundException { + public CassandraConverter cassandraConverter() { - MappingCassandraConverter mappingCassandraConverter = new MappingCassandraConverter(cassandraMapping()); + try { + MappingCassandraConverter mappingCassandraConverter = new MappingCassandraConverter(cassandraMapping()); - mappingCassandraConverter.setCustomConversions(customConversions()); + mappingCassandraConverter.setCustomConversions(customConversions()); - return mappingCassandraConverter; + return mappingCassandraConverter; + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e); + } } /** @@ -138,14 +154,17 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf CassandraMappingContext mappingContext = new CassandraMappingContext(); - mappingContext.setBeanClassLoader(beanClassLoader); + if (beanClassLoader != null) { + mappingContext.setBeanClassLoader(beanClassLoader); + } + mappingContext.setInitialEntitySet(getInitialEntitySet()); CustomConversions customConversions = customConversions(); mappingContext.setCustomConversions(customConversions); mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); - mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(cluster().getObject(), getKeyspaceName())); + mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(getRequiredCluster(), getKeyspaceName())); return mappingContext; } @@ -156,7 +175,7 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf * of entity classes. * * @return {@link Set} of initial entity classes. - * @throws ClassNotFoundException + * @throws ClassNotFoundException if the entity scan fails. * @see #getEntityBasePackages() * @see CassandraEntityClassScanner * @since 2.0 @@ -201,4 +220,5 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf public SchemaAction getSchemaAction() { return SchemaAction.NONE; } + } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractClusterConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractClusterConfiguration.java index bb1c09a5c..c6f6d37d5 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractClusterConfiguration.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractClusterConfiguration.java @@ -22,8 +22,11 @@ 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; @@ -48,6 +51,20 @@ import com.datastax.driver.core.policies.SpeculativeExecutionPolicy; @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(); + Assert.state(factoryBean.getObject() != null, "Cluster factory not initialized"); + + return factoryBean.getObject(); + } + /** * Creates a {@link CassandraClusterFactoryBean} that provides a Cassandra {@link com.datastax.driver.core.Cluster}. * The lifecycle of {@link CassandraClusterFactoryBean} executes {@link #getStartupScripts() startup} and @@ -97,6 +114,7 @@ public abstract class AbstractClusterConfiguration { * @return the {@link AddressTranslator}; may be {@literal null}. * @since 1.5 */ + @Nullable protected AddressTranslator getAddressTranslator() { return null; } @@ -106,6 +124,7 @@ public abstract class AbstractClusterConfiguration { * * @return the {@link AuthProvider}, may be {@literal null}. */ + @Nullable protected AuthProvider getAuthProvider() { return null; } @@ -116,6 +135,7 @@ public abstract class AbstractClusterConfiguration { * @return the {@link ClusterBuilderConfigurer}; may be {@literal null}. * @since 1.5 */ + @Nullable protected ClusterBuilderConfigurer getClusterBuilderConfigurer() { return null; } @@ -126,6 +146,7 @@ public abstract class AbstractClusterConfiguration { * @return the cluster name; may be {@literal null}. * @since 1.5 */ + @Nullable protected String getClusterName() { return null; } @@ -135,6 +156,7 @@ public abstract class AbstractClusterConfiguration { * * @return the {@link CompressionType}, may be {@literal null}. */ + @Nullable protected CompressionType getCompressionType() { return null; } @@ -154,6 +176,7 @@ public abstract class AbstractClusterConfiguration { * * @return the {@link LoadBalancingPolicy}, may be {@literal null}. */ + @Nullable protected LoadBalancingPolicy getLoadBalancingPolicy() { return null; } @@ -180,7 +203,7 @@ public abstract class AbstractClusterConfiguration { /** * Returns the {@link NettyOptions}. Defaults to {@link NettyOptions#DEFAULT_INSTANCE}. * - * @return + * @return the {@link NettyOptions} to customize netty behavior. * @since 1.5 */ protected NettyOptions getNettyOptions() { @@ -192,6 +215,7 @@ public abstract class AbstractClusterConfiguration { * * @return the {@link PoolingOptions}, may be {@literal null}. */ + @Nullable protected PoolingOptions getPoolingOptions() { return null; } @@ -207,12 +231,13 @@ public abstract class AbstractClusterConfiguration { } /** - * Returns the {@link ProtocolVersion}. + * Returns the {@link ProtocolVersion}. Defaults to {@link ProtocolVersion#NEWEST_SUPPORTED} * - * @return the {@link ProtocolVersion}, may be {@literal null}. + * @return the {@link ProtocolVersion}. + * @see ProtocolVersion#NEWEST_SUPPORTED. */ protected ProtocolVersion getProtocolVersion() { - return null; + return ProtocolVersion.NEWEST_SUPPORTED; } /** @@ -221,6 +246,7 @@ public abstract class AbstractClusterConfiguration { * @return the {@link QueryOptions}, may be {@literal null}. * @since 1.5 */ + @Nullable protected QueryOptions getQueryOptions() { return null; } @@ -230,6 +256,7 @@ public abstract class AbstractClusterConfiguration { * * @return the {@link ReconnectionPolicy}, may be {@literal null}. */ + @Nullable protected ReconnectionPolicy getReconnectionPolicy() { return null; } @@ -239,6 +266,7 @@ public abstract class AbstractClusterConfiguration { * * @return the {@link RetryPolicy}, may be {@literal null}. */ + @Nullable protected RetryPolicy getRetryPolicy() { return null; } @@ -249,6 +277,7 @@ public abstract class AbstractClusterConfiguration { * @return the {@link SpeculativeExecutionPolicy}; may be {@literal null}. * @since 1.5 */ + @Nullable protected SpeculativeExecutionPolicy getSpeculativeExecutionPolicy() { return null; } @@ -258,6 +287,7 @@ public abstract class AbstractClusterConfiguration { * * @return the {@link SocketOptions}, may be {@literal null}. */ + @Nullable protected SocketOptions getSocketOptions() { return null; } @@ -268,6 +298,7 @@ public abstract class AbstractClusterConfiguration { * @return the {@link TimestampGenerator}; may be {@literal null}. * @since 1.5 */ + @Nullable protected TimestampGenerator getTimestampGenerator() { return null; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractReactiveCassandraConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractReactiveCassandraConfiguration.java index b53ddc931..815f9dffd 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractReactiveCassandraConfiguration.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractReactiveCassandraConfiguration.java @@ -46,8 +46,8 @@ public abstract class AbstractReactiveCassandraConfiguration extends AbstractCas * @see DefaultBridgedReactiveSession */ @Bean - public ReactiveSession reactiveSession() throws Exception { - return new DefaultBridgedReactiveSession(session().getObject(), Schedulers.elastic()); + public ReactiveSession reactiveSession() { + return new DefaultBridgedReactiveSession(getRequiredSession(), Schedulers.elastic()); } /** @@ -59,7 +59,7 @@ public abstract class AbstractReactiveCassandraConfiguration extends AbstractCas * @see #reactiveCassandraTemplate() */ @Bean - public ReactiveSessionFactory reactiveSessionFactory() throws Exception { + public ReactiveSessionFactory reactiveSessionFactory() { return new DefaultReactiveSessionFactory(reactiveSession()); } @@ -71,7 +71,7 @@ public abstract class AbstractReactiveCassandraConfiguration extends AbstractCas * @see #cassandraConverter() */ @Bean - public ReactiveCassandraOperations reactiveCassandraTemplate() throws Exception { + public ReactiveCassandraOperations reactiveCassandraTemplate() { return new ReactiveCassandraTemplate(reactiveSessionFactory(), cassandraConverter()); } @@ -82,7 +82,7 @@ public abstract class AbstractReactiveCassandraConfiguration extends AbstractCas * @see #reactiveSessionFactory() */ @Bean - public ReactiveCqlOperations reactiveCqlTemplate() throws Exception { + public ReactiveCqlOperations reactiveCqlTemplate() { return new ReactiveCqlTemplate(reactiveSessionFactory()); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractSessionConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractSessionConfiguration.java index c05a79b47..efac4e6ac 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractSessionConfiguration.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractSessionConfiguration.java @@ -20,6 +20,9 @@ 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.session.DefaultSessionFactory; +import org.springframework.util.Assert; + +import com.datastax.driver.core.Session; /** * Spring {@link @Configuration} class used to configure a Cassandra client application @@ -35,6 +38,20 @@ import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory @Configuration public abstract class AbstractSessionConfiguration extends AbstractClusterConfiguration { + /** + * 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}. @@ -48,7 +65,7 @@ public abstract class AbstractSessionConfiguration extends AbstractClusterConfig CassandraCqlSessionFactoryBean bean = new CassandraCqlSessionFactoryBean(); - bean.setCluster(cluster().getObject()); + bean.setCluster(getRequiredCluster()); bean.setKeyspaceName(getKeyspaceName()); return bean; @@ -63,7 +80,7 @@ public abstract class AbstractSessionConfiguration extends AbstractClusterConfig */ @Bean public SessionFactory sessionFactory() { - return new DefaultSessionFactory(session().getObject()); + return new DefaultSessionFactory(getRequiredSession()); } /** diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraClusterFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraClusterFactoryBean.java index 520af453c..8052e5efb 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraClusterFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraClusterFactoryBean.java @@ -36,6 +36,7 @@ import org.springframework.data.cassandra.core.cql.generator.DropKeyspaceCqlGene 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.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -89,39 +90,39 @@ public class CassandraClusterFactoryBean private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator(); - private Cluster cluster; - private ClusterBuilderConfigurer clusterBuilderConfigurer; + private @Nullable Cluster cluster; + private @Nullable ClusterBuilderConfigurer clusterBuilderConfigurer; - private AddressTranslator addressTranslator; - private AuthProvider authProvider; - private CompressionType compressionType; - private Host.StateListener hostStateListener; - private LatencyTracker latencyTracker; + private @Nullable AddressTranslator addressTranslator; + private @Nullable AuthProvider authProvider; + private @Nullable CompressionType compressionType; + private @Nullable Host.StateListener hostStateListener; + private @Nullable LatencyTracker latencyTracker; private List keyspaceCreations = new ArrayList<>(); private List keyspaceDrops = new ArrayList<>(); - private Set> keyspaceSpecifications = new HashSet<>(); + private Set keyspaceSpecifications = new HashSet<>(); private List startupScripts = new ArrayList<>(); private List shutdownScripts = new ArrayList<>(); - private LoadBalancingPolicy loadBalancingPolicy; - private NettyOptions nettyOptions; - private PoolingOptions poolingOptions; - private ProtocolVersion protocolVersion; - private QueryOptions queryOptions; - private ReconnectionPolicy reconnectionPolicy; - private RetryPolicy retryPolicy; - private SpeculativeExecutionPolicy speculativeExecutionPolicy; - private SocketOptions socketOptions; - private SSLOptions sslOptions; - private TimestampGenerator timestampGenerator; + 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 String beanName; - private String clusterName; + private @Nullable String beanName; + private @Nullable String clusterName; private String contactPoints = DEFAULT_CONTACT_POINTS; - private String password; - private String username; + private @Nullable String password; + private @Nullable String username; /* * (non-Javadoc) @@ -142,7 +143,7 @@ public class CassandraClusterFactoryBean Optional.ofNullable(addressTranslator).ifPresent(clusterBuilder::withAddressTranslator); Optional.ofNullable(loadBalancingPolicy).ifPresent(clusterBuilder::withLoadBalancingPolicy); - Optional.ofNullable(nettyOptions).ifPresent(clusterBuilder::withNettyOptions); + clusterBuilder.withNettyOptions(nettyOptions); Optional.ofNullable(poolingOptions).ifPresent(clusterBuilder::withPoolingOptions); Optional.ofNullable(protocolVersion).ifPresent(clusterBuilder::withProtocolVersion); Optional.ofNullable(queryOptions).ifPresent(clusterBuilder::withQueryOptions); @@ -186,7 +187,7 @@ public class CassandraClusterFactoryBean Optional.ofNullable(latencyTracker).ifPresent(cluster::register); generateSpecificationsFromFactoryBeans(); - executeSpecsAndScripts(keyspaceCreations, startupScripts); + executeSpecsAndScripts(keyspaceCreations, startupScripts, cluster); } /* @@ -198,8 +199,9 @@ public class CassandraClusterFactoryBean } /* (non-Javadoc) */ + @Nullable private String resolveClusterName() { - return (StringUtils.hasText(clusterName) ? clusterName : beanName); + return StringUtils.hasText(clusterName) ? clusterName : beanName; } /* @@ -207,9 +209,13 @@ public class CassandraClusterFactoryBean * @see org.springframework.beans.factory.DisposableBean#destroy() */ @Override - public void destroy() throws Exception { - executeSpecsAndScripts(keyspaceDrops, shutdownScripts); - cluster.close(); + public void destroy() { + + if (cluster != null) { + + executeSpecsAndScripts(keyspaceDrops, shutdownScripts, cluster); + cluster.close(); + } } /* @@ -266,8 +272,8 @@ public class CassandraClusterFactoryBean }); } - protected void executeSpecsAndScripts(List> kepspaceActionSpecifications, - List scripts) { + protected void executeSpecsAndScripts(List kepspaceActionSpecifications, + List scripts, Cluster cluster) { if (!CollectionUtils.isEmpty(kepspaceActionSpecifications) || !CollectionUtils.isEmpty(scripts)) { @@ -288,7 +294,7 @@ public class CassandraClusterFactoryBean } } - private String toCql(KeyspaceActionSpecification keyspaceActionSpecification) { + private String toCql(KeyspaceActionSpecification keyspaceActionSpecification) { return (keyspaceActionSpecification instanceof CreateKeyspaceSpecification ? new CreateKeyspaceCqlGenerator((CreateKeyspaceSpecification) keyspaceActionSpecification).toCql() @@ -329,7 +335,7 @@ public class CassandraClusterFactoryBean * * @param compressionType the {@link CompressionType} used by the new cluster. */ - public void setCompressionType(CompressionType compressionType) { + public void setCompressionType(@Nullable CompressionType compressionType) { this.compressionType = compressionType; } @@ -338,7 +344,7 @@ public class CassandraClusterFactoryBean * * @param poolingOptions the {@link PoolingOptions} used by the new cluster. */ - public void setPoolingOptions(PoolingOptions poolingOptions) { + public void setPoolingOptions(@Nullable PoolingOptions poolingOptions) { this.poolingOptions = poolingOptions; } @@ -348,7 +354,7 @@ public class CassandraClusterFactoryBean * @param protocolVersion the {@link ProtocolVersion} used by the new cluster. * @since 1.4 */ - public void setProtocolVersion(ProtocolVersion protocolVersion) { + public void setProtocolVersion(@Nullable ProtocolVersion protocolVersion) { this.protocolVersion = protocolVersion; } @@ -357,7 +363,7 @@ public class CassandraClusterFactoryBean * * @param socketOptions the {@link SocketOptions} used by the new cluster. */ - public void setSocketOptions(SocketOptions socketOptions) { + public void setSocketOptions(@Nullable SocketOptions socketOptions) { this.socketOptions = socketOptions; } @@ -366,7 +372,7 @@ public class CassandraClusterFactoryBean * * @param queryOptions the {@link QueryOptions} used by the new cluster. */ - public void setQueryOptions(QueryOptions queryOptions) { + public void setQueryOptions(@Nullable QueryOptions queryOptions) { this.queryOptions = queryOptions; } @@ -375,7 +381,7 @@ public class CassandraClusterFactoryBean * * @param authProvider the {@link AuthProvider} used by the new cluster. */ - public void setAuthProvider(AuthProvider authProvider) { + public void setAuthProvider(@Nullable AuthProvider authProvider) { this.authProvider = authProvider; } @@ -394,7 +400,7 @@ public class CassandraClusterFactoryBean * * @param loadBalancingPolicy the {@link LoadBalancingPolicy} used by the new cluster. */ - public void setLoadBalancingPolicy(LoadBalancingPolicy loadBalancingPolicy) { + public void setLoadBalancingPolicy(@Nullable LoadBalancingPolicy loadBalancingPolicy) { this.loadBalancingPolicy = loadBalancingPolicy; } @@ -403,7 +409,7 @@ public class CassandraClusterFactoryBean * * @param reconnectionPolicy the {@link ReconnectionPolicy} used by the new cluster. */ - public void setReconnectionPolicy(ReconnectionPolicy reconnectionPolicy) { + public void setReconnectionPolicy(@Nullable ReconnectionPolicy reconnectionPolicy) { this.reconnectionPolicy = reconnectionPolicy; } @@ -412,7 +418,7 @@ public class CassandraClusterFactoryBean * * @param retryPolicy the {@link RetryPolicy} used by the new cluster. */ - public void setRetryPolicy(RetryPolicy retryPolicy) { + public void setRetryPolicy(@Nullable RetryPolicy retryPolicy) { this.retryPolicy = retryPolicy; } @@ -499,14 +505,14 @@ public class CassandraClusterFactoryBean /** * @param keyspaceSpecifications The {@link KeyspaceActionSpecification} to set. */ - public void setKeyspaceSpecifications(Set> keyspaceSpecifications) { + public void setKeyspaceSpecifications(Set keyspaceSpecifications) { this.keyspaceSpecifications = keyspaceSpecifications; } /** * @return the {@link KeyspaceActionSpecification} associated with this factory. */ - public Set> getKeyspaceSpecifications() { + public Set getKeyspaceSpecifications() { return keyspaceSpecifications; } @@ -576,7 +582,7 @@ public class CassandraClusterFactoryBean * @see com.datastax.driver.core.policies.AddressTranslator * @since 1.5 */ - public void setAddressTranslator(AddressTranslator addressTranslator) { + public void setAddressTranslator(@Nullable AddressTranslator addressTranslator) { this.addressTranslator = addressTranslator; } @@ -590,7 +596,7 @@ public class CassandraClusterFactoryBean * {@link com.datastax.driver.core.Cluster.Builder}. * @see org.springframework.data.cql.config.ClusterBuilderConfigurer */ - public void setClusterBuilderConfigurer(ClusterBuilderConfigurer clusterBuilderConfigurer) { + public void setClusterBuilderConfigurer(@Nullable ClusterBuilderConfigurer clusterBuilderConfigurer) { this.clusterBuilderConfigurer = clusterBuilderConfigurer; } @@ -601,7 +607,7 @@ public class CassandraClusterFactoryBean * @see com.datastax.driver.core.Cluster.Builder#withClusterName(String) * @since 1.5 */ - public void setClusterName(String clusterName) { + public void setClusterName(@Nullable String clusterName) { this.clusterName = clusterName; } @@ -625,7 +631,7 @@ public class CassandraClusterFactoryBean * @see com.datastax.driver.core.policies.SpeculativeExecutionPolicy * @since 1.5 */ - public void setSpeculativeExecutionPolicy(SpeculativeExecutionPolicy speculativeExecutionPolicy) { + public void setSpeculativeExecutionPolicy(@Nullable SpeculativeExecutionPolicy speculativeExecutionPolicy) { this.speculativeExecutionPolicy = speculativeExecutionPolicy; } @@ -637,7 +643,7 @@ public class CassandraClusterFactoryBean * @see com.datastax.driver.core.TimestampGenerator * @since 1.5 */ - public void setTimestampGenerator(TimestampGenerator timestampGenerator) { + public void setTimestampGenerator(@Nullable TimestampGenerator timestampGenerator) { this.timestampGenerator = timestampGenerator; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlClusterParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlClusterParser.java index 0e51b18d2..7314a5e9b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlClusterParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlClusterParser.java @@ -30,6 +30,7 @@ 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; @@ -205,7 +206,7 @@ class CassandraCqlClusterParser extends AbstractBeanDefinitionParser { * @param element {@link Element} to parse. * @param builder The {@link BeanDefinitionBuilder} to add the replication to */ - private void parseReplication(Element element, BeanDefinitionBuilder builder) { + private void parseReplication(@Nullable Element element, BeanDefinitionBuilder builder) { ManagedList networkTopologyDataCenters = new ManagedList<>(); ManagedList networkTopologyReplicationFactors = new ManagedList<>(); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlSessionFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlSessionFactoryBean.java index cfaae3de3..70ead16ea 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlSessionFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlSessionFactoryBean.java @@ -20,6 +20,8 @@ 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; @@ -28,13 +30,11 @@ 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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Session; @@ -45,6 +45,7 @@ import com.datastax.driver.core.Session; * @author Alex Shvid * @author Matthew T. Adams * @author John Blum + * @author Mark Paluch * @see org.springframework.beans.factory.DisposableBean * @see org.springframework.beans.factory.FactoryBean * @see org.springframework.beans.factory.InitializingBean @@ -60,15 +61,15 @@ public class CassandraCqlSessionFactoryBean protected final Logger logger = LoggerFactory.getLogger(getClass()); protected final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator(); - private Cluster cluster; + private @Nullable Cluster cluster; private List startupScripts = Collections.emptyList(); private List shutdownScripts = Collections.emptyList(); - private Session session; + private @Nullable Session session; - private String keyspaceName; + private @Nullable String keyspaceName; /* * (non-Javadoc) @@ -83,7 +84,7 @@ public class CassandraCqlSessionFactoryBean } /* (non-Javadoc) */ - Session connect(String keyspaceName) { + Session connect(@Nullable String keyspaceName) { return (StringUtils.hasText(keyspaceName) ? getCluster().connect(keyspaceName) : getCluster().connect()); } @@ -178,7 +179,9 @@ public class CassandraCqlSessionFactoryBean * @see #getCluster() */ public void setCluster(Cluster cluster) { + Assert.notNull(cluster, "Cluster must not be null"); + this.cluster = cluster; } @@ -191,18 +194,20 @@ public class CassandraCqlSessionFactoryBean * @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 {@code null}, an empty String, or whitespace will + * 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(String keyspaceName) { + public void setKeyspaceName(@Nullable String keyspaceName) { this.keyspaceName = keyspaceName; } @@ -212,6 +217,7 @@ public class CassandraCqlSessionFactoryBean * @return the name of the Cassandra Keyspace to connect to as a String. * @see #setKeyspaceName(String) */ + @Nullable protected String getKeyspaceName() { return this.keyspaceName; } @@ -235,7 +241,7 @@ public class CassandraCqlSessionFactoryBean /** * Sets CQL scripts to be executed immediately after the session is connected. */ - public void setStartupScripts(List scripts) { + public void setStartupScripts(@Nullable List scripts) { this.startupScripts = (scripts != null ? new ArrayList<>(scripts) : Collections.emptyList()); } @@ -249,7 +255,7 @@ public class CassandraCqlSessionFactoryBean /** * Sets CQL scripts to be executed immediately before the session is shutdown. */ - public void setShutdownScripts(List scripts) { + public void setShutdownScripts(@Nullable List scripts) { this.shutdownScripts = (scripts != null ? new ArrayList<>(scripts) : Collections.emptyList()); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlTemplateFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlTemplateFactoryBean.java index 8d7ece5ff..2bcb89af3 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlTemplateFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraCqlTemplateFactoryBean.java @@ -18,6 +18,7 @@ package org.springframework.data.cassandra.config; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.data.cassandra.core.cql.CqlTemplate; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.driver.core.Session; @@ -30,8 +31,9 @@ import com.datastax.driver.core.Session; */ public class CassandraCqlTemplateFactoryBean implements FactoryBean, InitializingBean { - private CqlTemplate template; - private Session session; + private @Nullable CqlTemplate template; + + private @Nullable Session session; /* (non-Javadoc) * @see org.springframework.beans.factory.FactoryBean#getObject() diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java index 1e9d6af2d..828ad5101 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java @@ -28,51 +28,110 @@ import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.data.annotation.Persistent; import org.springframework.data.cassandra.core.mapping.PrimaryKeyClass; import org.springframework.data.cassandra.core.mapping.Table; +import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** - * Scans packages for Cassandra entities. + * Scans packages for Cassandra entities. The entity scanner scans for entity classes annotated with + * {@link #getEntityAnnotations() entity annotations} on the class path using either base package names, base package + * classes or both. * * @author Matthew T. Adams + * @author Mark Paluch + * @see ClassUtils#forName(String, ClassLoader) */ public class CassandraEntityClassScanner { + private Set entityBasePackages = new HashSet<>(); + private Set> entityBasePackageClasses = new HashSet<>(); + private @Nullable ClassLoader beanClassLoader; + + /** + * Scan one or more base packages for entity classes. Classes are loaded using the current class loader. + * + * @param entityBasePackages must not be {@literal null}. + * @return + * @throws ClassNotFoundException + */ public static Set> scan(String... entityBasePackages) throws ClassNotFoundException { return new CassandraEntityClassScanner(entityBasePackages).scanForEntityClasses(); } + /** + * Scan one or more base packages for entity classes. Classes are loaded using the current class loader. + * + * @param entityBasePackageClasses must not be {@literal null}. + * @return + * @throws ClassNotFoundException + */ public static Set> scan(Class... entityBasePackageClasses) throws ClassNotFoundException { return new CassandraEntityClassScanner(entityBasePackageClasses).scanForEntityClasses(); } + /** + * Scan one or more base packages for entity classes. Classes are loaded using the current class loader. + * + * @param entityBasePackages must not be {@literal null}. + * @return + * @throws ClassNotFoundException + */ public static Set> scan(Collection entityBasePackages) throws ClassNotFoundException { return new CassandraEntityClassScanner(entityBasePackages).scanForEntityClasses(); } + /** + * Scan one or more base packages for entity classes. Classes are loaded using the current class loader. + * + * @param entityBasePackages must not be {@literal null}. + * @param entityBasePackageClasses must not be {@literal null}. + * @return + * @throws ClassNotFoundException + */ public static Set> scan(Collection entityBasePackages, Collection> entityBasePackageClasses) throws ClassNotFoundException { return new CassandraEntityClassScanner(entityBasePackages, entityBasePackageClasses).scanForEntityClasses(); } - protected Set entityBasePackages = new HashSet<>(); - protected Set> entityBasePackageClasses = new HashSet<>(); - protected ClassLoader beanClassLoader; - + /** + * Creates a new {@link CassandraEntityClassScanner}. + */ public CassandraEntityClassScanner() {} + /** + * Creates a new {@link CassandraEntityClassScanner} given {@code entityBasePackageClasses}. + * + * @param entityBasePackageClasses must not be {@literal null}. + */ public CassandraEntityClassScanner(Class... entityBasePackageClasses) { - this(null, Arrays.asList(entityBasePackageClasses)); + setEntityBasePackageClasses(Arrays.asList(entityBasePackageClasses)); } + /** + * Creates a new {@link CassandraEntityClassScanner} given {@code entityBasePackages}. + * + * @param entityBasePackages must not be {@literal null}. + */ public CassandraEntityClassScanner(String... entityBasePackages) { this(Arrays.asList(entityBasePackages)); } + /** + * Creates a new {@link CassandraEntityClassScanner} given {@code entityBasePackages}. + * + * @param entityBasePackages must not be {@literal null}. + */ public CassandraEntityClassScanner(Collection entityBasePackages) { - this(entityBasePackages, null); + setEntityBasePackages(entityBasePackages); } + /** + * Creates a new {@link CassandraEntityClassScanner} given {@code entityBasePackages} and + * {@code entityBasePackageClasses}. + * + * @param entityBasePackages must not be {@literal null}. + * @param entityBasePackageClasses must not be {@literal null}. + */ public CassandraEntityClassScanner(Collection entityBasePackages, Collection> entityBasePackageClasses) { @@ -80,23 +139,43 @@ public class CassandraEntityClassScanner { setEntityBasePackageClasses(entityBasePackageClasses); } + /** + * @return base package names used for the entity scan. + */ public Set getEntityBasePackages() { return Collections.unmodifiableSet(entityBasePackages); } + /** + * Set the base package names to be used for the entity scan. + * + * @param entityBasePackages must not be {@literal null}. + */ public void setEntityBasePackages(Collection entityBasePackages) { - this.entityBasePackages = entityBasePackages == null ? new HashSet<>() : new HashSet<>(entityBasePackages); + this.entityBasePackages = new HashSet<>(entityBasePackages); } + /** + * @return base package classes used for the entity scan. + */ public Set> getEntityBasePackageClasses() { return Collections.unmodifiableSet(entityBasePackageClasses); } + /** + * Set the base package classes to be used for the entity scan. + * + * @param entityBasePackageClasses must not be {@literal null}. + */ public void setEntityBasePackageClasses(Collection> entityBasePackageClasses) { - this.entityBasePackageClasses = entityBasePackageClasses == null ? new HashSet<>() - : new HashSet<>(entityBasePackageClasses); + this.entityBasePackageClasses = new HashSet<>(entityBasePackageClasses); } + /** + * Set the bean {@link ClassLoader} to load class candidates discovered by the class path scan. + * + * @param beanClassLoader must not be {@literal null}. + */ public void setBeanClassLoader(ClassLoader beanClassLoader) { this.beanClassLoader = beanClassLoader; } @@ -127,14 +206,20 @@ public class CassandraEntityClassScanner { HashSet> classes = new HashSet<>(); - if (StringUtils.hasText(basePackage)) { - ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider( - false); - for (Class annoClass : getEntityAnnotations()) { - componentProvider.addIncludeFilter(new AnnotationTypeFilter(annoClass)); - } + if (StringUtils.isEmpty(basePackage)) { + return classes; + } - for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { + ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider( + false); + + for (Class annotation : getEntityAnnotations()) { + componentProvider.addIncludeFilter(new AnnotationTypeFilter(annotation)); + } + + for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { + + if (candidate.getBeanClassName() != null) { classes.add(ClassUtils.forName(candidate.getBeanClassName(), beanClassLoader)); } } @@ -142,8 +227,14 @@ public class CassandraEntityClassScanner { return classes; } + /** + * @return entity annotations. + * @see Table + * @see Persistent + * @see PrimaryKeyClass + */ @SuppressWarnings("unchecked") - public Class[] getEntityAnnotations() { + protected Class[] getEntityAnnotations() { return new Class[] { Table.class, Persistent.class, PrimaryKeyClass.class }; } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextParser.java index 75cb9991d..65d153ba7 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextParser.java @@ -97,10 +97,7 @@ class CassandraMappingContextParser extends AbstractSingleBeanDefinitionParser { DomUtils.getChildElementsByTagName(element, "entity").forEach(entity -> { EntityMapping entityMapping = parseEntity(entity); - - if (entityMapping != null) { - mappings.add(entityMapping); - } + mappings.add(entityMapping); }); List userTypeResolvers = DomUtils.getChildElementsByTagName(element, "user-type-resolver"); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java index c0672123e..1c9f488ef 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java @@ -28,14 +28,16 @@ import org.w3c.dom.Element; * Ensures that a {@link CassandraMappingBeanFactoryPostProcessor} is registered. * * @author Matthew T. Adams + * @author Mark Paluch */ +@Deprecated class CassandraMappingXmlBeanFactoryPostProcessorRegistrar { /** * Ensures that a {@link CassandraMappingBeanFactoryPostProcessor} is registered. This method is a no-op if one is * already registered. */ - public static void ensureRegistration(Element element, ParserContext parserContext) { + static void ensureRegistration(Element element, ParserContext parserContext) { BeanDefinitionRegistry registry = parserContext.getRegistry(); if (!(registry instanceof GenericApplicationContext)) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraSessionFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraSessionFactoryBean.java index 9e0e21d2e..09a97e4e1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraSessionFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraSessionFactoryBean.java @@ -21,6 +21,7 @@ import org.springframework.data.cassandra.core.CassandraPersistentEntitySchemaCr 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; /** @@ -40,9 +41,9 @@ public class CassandraSessionFactoryBean extends CassandraCqlSessionFactoryBean protected static final boolean DEFAULT_DROP_TABLES = false; protected static final boolean DEFAULT_DROP_UNUSED_TABLES = false; - private CassandraAdminOperations admin; + private @Nullable CassandraAdminOperations admin; - private CassandraConverter converter; + private @Nullable CassandraConverter converter; private SchemaAction schemaAction = SchemaAction.NONE; @@ -107,6 +108,7 @@ public class CassandraSessionFactoryBean extends CassandraCqlSessionFactoryBean /** * @return the {@link CassandraConverter}. */ + @Nullable public CassandraConverter getConverter() { return this.converter; } @@ -114,6 +116,7 @@ public class CassandraSessionFactoryBean extends CassandraCqlSessionFactoryBean /** * @return the {@link CassandraMappingContext}. */ + @Nullable protected CassandraMappingContext getMappingContext() { return getConverter().getMappingContext(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraTemplateFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraTemplateFactoryBean.java index b3c11b450..c838c481e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraTemplateFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraTemplateFactoryBean.java @@ -22,6 +22,7 @@ import org.springframework.data.cassandra.core.CassandraTemplate; import org.springframework.data.cassandra.core.convert.CassandraConverter; import org.springframework.data.cassandra.core.cql.CqlOperations; 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; @@ -34,11 +35,11 @@ import com.datastax.driver.core.Session; */ public class CassandraTemplateFactoryBean implements FactoryBean, InitializingBean { - protected SessionFactory sessionFactory; + protected @Nullable SessionFactory sessionFactory; - protected CqlOperations cqlOperations; + protected @Nullable CqlOperations cqlOperations; - protected CassandraConverter converter; + protected @Nullable CassandraConverter converter; /* * (non-Javadoc) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/KeyspaceActionSpecificationFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/KeyspaceActionSpecificationFactoryBean.java index 604b201ac..6bdd374f1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/KeyspaceActionSpecificationFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/KeyspaceActionSpecificationFactoryBean.java @@ -32,6 +32,7 @@ import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceActionSpecif 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 org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -42,16 +43,16 @@ import org.springframework.util.Assert; * @author David Webb */ public class KeyspaceActionSpecificationFactoryBean - implements FactoryBean>>, InitializingBean, DisposableBean { + implements FactoryBean>, InitializingBean, DisposableBean { - private KeyspaceAction action; + private @Nullable KeyspaceAction action; - private String name; + private @Nullable String name; private List networkTopologyDataCenters = new LinkedList<>(); private List networkTopologyReplicationFactors = new LinkedList<>(); - private ReplicationStrategy replicationStrategy; + private @Nullable ReplicationStrategy replicationStrategy; private long replicationFactor; @@ -59,7 +60,7 @@ public class KeyspaceActionSpecificationFactoryBean private boolean ifNotExists = false; - private Set> specs = new HashSet<>(); + private Set specs = new HashSet<>(); /* (non-Javadoc) * @see org.springframework.beans.factory.DisposableBean#destroy() @@ -68,10 +69,10 @@ public class KeyspaceActionSpecificationFactoryBean public void destroy() { action = null; name = null; - networkTopologyDataCenters = null; - networkTopologyReplicationFactors = null; + networkTopologyDataCenters = new LinkedList<>(); + networkTopologyReplicationFactors = new LinkedList<>(); replicationStrategy = null; - specs = null; + specs = new HashSet<>(); } /* (non-Javadoc) @@ -102,8 +103,8 @@ public class KeyspaceActionSpecificationFactoryBean */ private CreateKeyspaceSpecification generateCreateKeyspaceSpecification() { - CreateKeyspaceSpecification create = new CreateKeyspaceSpecification(); - create.name(name).ifNotExists(ifNotExists).with(KeyspaceOption.DURABLE_WRITES, durableWrites); + CreateKeyspaceSpecification create = CreateKeyspaceSpecification.createKeyspace(name).ifNotExists(ifNotExists) + .with(KeyspaceOption.DURABLE_WRITES, durableWrites); Map replicationStrategyMap = new HashMap<>(); replicationStrategyMap.put(new DefaultOption("class", String.class, true, false, true), @@ -133,16 +134,14 @@ public class KeyspaceActionSpecificationFactoryBean * @return The {@link DropKeyspaceSpecification} */ private DropKeyspaceSpecification generateDropKeyspaceSpecification() { - DropKeyspaceSpecification drop = new DropKeyspaceSpecification(); - drop.name(getName()); - return drop; + return DropKeyspaceSpecification.dropKeyspace(getName()); } /* (non-Javadoc) * @see org.springframework.beans.factory.FactoryBean#getObject() */ @Override - public Set> getObject() throws Exception { + public Set getObject() { return specs; } @@ -165,6 +164,7 @@ public class KeyspaceActionSpecificationFactoryBean /** * @return Returns the name. */ + @Nullable public String getName() { return name; } @@ -193,6 +193,7 @@ public class KeyspaceActionSpecificationFactoryBean /** * @return Returns the action. */ + @Nullable public KeyspaceAction getAction() { return action; } @@ -221,6 +222,7 @@ public class KeyspaceActionSpecificationFactoryBean /** * @return Returns the replicationStrategy. */ + @Nullable public ReplicationStrategy getReplicationStrategy() { return replicationStrategy; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/ParsingUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/ParsingUtils.java index 77152ba9a..c01c4b80d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/ParsingUtils.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/ParsingUtils.java @@ -19,6 +19,7 @@ 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.xml.ParserContext; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.w3c.dom.Attr; @@ -29,11 +30,6 @@ import org.w3c.dom.Element; * * @author John Blum * @author Mark Paluch - * @see org.springframework.beans.factory.config.BeanDefinition - * @see org.springframework.beans.factory.support.BeanDefinitionBuilder - * @see org.springframework.beans.factory.xml.ParserContext - * @see org.w3c.dom.Attr - * @see org.w3c.dom.Element */ abstract class ParsingUtils { @@ -129,7 +125,7 @@ abstract class ParsingUtils { * {@link #addProperty(BeanDefinitionBuilder, String, String, String, boolean, boolean)}. */ public static void addOptionalPropertyValue(BeanDefinitionBuilder builder, String propertyName, Element element, - String attributeName, String defaultValue) { + String attributeName, @Nullable String defaultValue) { addProperty(builder, propertyName, element.getAttribute(attributeName), defaultValue, false, false); } @@ -233,7 +229,7 @@ abstract class ParsingUtils { * @see BeanDefinitionBuilder#addPropertyValue(String, Object) */ public static BeanDefinitionBuilder addProperty(BeanDefinitionBuilder builder, String propertyName, String value, - String defaultValue, boolean required, boolean reference) { + @Nullable String defaultValue, boolean required, boolean reference) { Assert.notNull(builder, "BeanDefinitionBuilder must not be null"); Assert.hasText(propertyName, "Property name must not be null"); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/PoolingOptionsFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/PoolingOptionsFactoryBean.java index ad89db84c..3578117a9 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/PoolingOptionsFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/PoolingOptionsFactoryBean.java @@ -23,6 +23,7 @@ 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; @@ -39,7 +40,7 @@ import com.datastax.driver.core.PoolingOptions; * @see org.springframework.beans.factory.InitializingBean * @see com.datastax.driver.core.PoolingOptions */ -@SuppressWarnings("unused") +@SuppressWarnings({ "unused", "WeakerAccess" }) public class PoolingOptionsFactoryBean implements FactoryBean, InitializingBean { private static final PoolingOptions DEFAULT = new PoolingOptions(); @@ -56,26 +57,26 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I GET_MAX_QUEUE_SIZE = Optional.ofNullable(ReflectionUtils.findMethod(PoolingOptions.class, "getMaxQueueSize")); } - private Executor initializationExecutor; + private @Nullable Executor initializationExecutor; private int heartbeatIntervalSeconds; private int idleTimeoutSeconds; - private Integer localCoreConnections; - private Integer localMaxConnections; - private Integer localMaxSimultaneousRequests; - private Integer localMinSimultaneousRequests; + 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 Integer remoteCoreConnections; - private Integer remoteMaxConnections; - private Integer remoteMaxSimultaneousRequests; - private Integer remoteMinSimultaneousRequests; + private @Nullable Integer remoteCoreConnections; + private @Nullable Integer remoteMaxConnections; + private @Nullable Integer remoteMaxSimultaneousRequests; + private @Nullable Integer remoteMinSimultaneousRequests; - private PoolingOptions poolingOptions; + private @Nullable PoolingOptions poolingOptions; /* * (non-Javadoc) @@ -137,7 +138,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * @see PoolingOptionsFactoryBean.LocalHostDistancePoolingOptions */ protected HostDistancePoolingOptions newLocalHostDistancePoolingOptions() { - return LocalHostDistancePoolingOptions.create(getLocalCoreConnections(), getLocalMaxConnections(), + return new LocalHostDistancePoolingOptions(getLocalCoreConnections(), getLocalMaxConnections(), getLocalMaxSimultaneousRequests(), getLocalMinSimultaneousRequests()); } @@ -153,7 +154,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * @see PoolingOptionsFactoryBean.RemoteHostDistancePoolingOptions */ protected HostDistancePoolingOptions newRemoteHostDistancePoolingOptions() { - return RemoteHostDistancePoolingOptions.create(getRemoteCoreConnections(), getRemoteMaxConnections(), + return new RemoteHostDistancePoolingOptions(getRemoteCoreConnections(), getRemoteMaxConnections(), getRemoteMaxSimultaneousRequests(), getRemoteMinSimultaneousRequests()); } @@ -260,6 +261,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @return the {@code initializationExecutor}. */ + @Nullable public Executor getInitializationExecutor() { return initializationExecutor; } @@ -305,7 +307,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @param localCoreConnections core number of local connections per host. */ - public void setLocalCoreConnections(Integer localCoreConnections) { + public void setLocalCoreConnections(@Nullable Integer localCoreConnections) { this.localCoreConnections = localCoreConnections; } @@ -314,6 +316,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @return the {@code localCoreConnections). */ + @Nullable public Integer getLocalCoreConnections() { return localCoreConnections; } @@ -323,7 +326,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @param localMaxConnections max number of local connections per host. */ - public void setLocalMaxConnections(Integer localMaxConnections) { + public void setLocalMaxConnections(@Nullable Integer localMaxConnections) { this.localMaxConnections = localMaxConnections; } @@ -332,6 +335,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @return the {@code localMaxConnections}. */ + @Nullable public Integer getLocalMaxConnections() { return localMaxConnections; } @@ -341,7 +345,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @param localMaxSimultaneousRequests max number of requests for local connections. */ - public void setLocalMaxSimultaneousRequests(Integer localMaxSimultaneousRequests) { + public void setLocalMaxSimultaneousRequests(@Nullable Integer localMaxSimultaneousRequests) { this.localMaxSimultaneousRequests = localMaxSimultaneousRequests; } @@ -350,6 +354,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @return the {@code localMaxSimultaneousRequests}. */ + @Nullable public Integer getLocalMaxSimultaneousRequests() { return localMaxSimultaneousRequests; } @@ -360,7 +365,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @param localMinSimultaneousRequests threshold triggering the creation of local connections to a host. */ - public void setLocalMinSimultaneousRequests(Integer localMinSimultaneousRequests) { + public void setLocalMinSimultaneousRequests(@Nullable Integer localMinSimultaneousRequests) { this.localMinSimultaneousRequests = localMinSimultaneousRequests; } @@ -370,6 +375,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @return the {@code localMinSimultaneousRequests}. */ + @Nullable public Integer getLocalMinSimultaneousRequests() { return localMinSimultaneousRequests; } @@ -379,7 +385,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @param remoteCoreConnections core number of remote connections per host. */ - public void setRemoteCoreConnections(Integer remoteCoreConnections) { + public void setRemoteCoreConnections(@Nullable Integer remoteCoreConnections) { this.remoteCoreConnections = remoteCoreConnections; } @@ -388,6 +394,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @return the {@code remoteCoreConnections). */ + @Nullable public Integer getRemoteCoreConnections() { return remoteCoreConnections; } @@ -397,7 +404,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @param remoteMaxConnections max number of remote connections per host. */ - public void setRemoteMaxConnections(Integer remoteMaxConnections) { + public void setRemoteMaxConnections(@Nullable Integer remoteMaxConnections) { this.remoteMaxConnections = remoteMaxConnections; } @@ -406,6 +413,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @return the {@code remoteMaxConnections}. */ + @Nullable public Integer getRemoteMaxConnections() { return remoteMaxConnections; } @@ -415,7 +423,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @param remoteMaxSimultaneousRequests max number of requests for local connections. */ - public void setRemoteMaxSimultaneousRequests(Integer remoteMaxSimultaneousRequests) { + public void setRemoteMaxSimultaneousRequests(@Nullable Integer remoteMaxSimultaneousRequests) { this.remoteMaxSimultaneousRequests = remoteMaxSimultaneousRequests; } @@ -424,6 +432,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @return the {@code remoteMaxSimultaneousRequests}. */ + @Nullable public Integer getRemoteMaxSimultaneousRequests() { return remoteMaxSimultaneousRequests; } @@ -434,7 +443,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @param remoteMinSimultaneousRequests threshold triggering the creation of remote connections to a host. */ - public void setRemoteMinSimultaneousRequests(Integer remoteMinSimultaneousRequests) { + public void setRemoteMinSimultaneousRequests(@Nullable Integer remoteMinSimultaneousRequests) { this.remoteMinSimultaneousRequests = remoteMinSimultaneousRequests; } @@ -444,6 +453,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @return the {@code remoteMinSimultaneousRequests}. */ + @Nullable public Integer getRemoteMinSimultaneousRequests() { return remoteMinSimultaneousRequests; } @@ -457,10 +467,10 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I */ protected static abstract class HostDistancePoolingOptions { - private final Integer coreConnectionsPerHost; - private final Integer maxConnectionsPerHost; - private final Integer maxRequestsPerConnection; - private final Integer newConnectionThreshold; + 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 @@ -471,8 +481,9 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * @param maxRequestsPerConnection maximum number of requests per connection. * @param newConnectionThreshold threshold that triggers the creation of a new connection to a host. */ - protected HostDistancePoolingOptions(Integer coreConnectionsPerHost, Integer maxConnectionsPerHost, - Integer maxRequestsPerConnection, Integer newConnectionThreshold) { + protected HostDistancePoolingOptions(@Nullable Integer coreConnectionsPerHost, + @Nullable Integer maxConnectionsPerHost, @Nullable Integer maxRequestsPerConnection, + @Nullable Integer newConnectionThreshold) { this.coreConnectionsPerHost = coreConnectionsPerHost; this.maxConnectionsPerHost = maxConnectionsPerHost; @@ -492,12 +503,10 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * (non-Javadoc) * @see com.datastax.driver.core.PoolingOptions#setCoreConnectionsPerHost(HostDistance, int) */ - PoolingOptions setCoreConnectionsPerHost(PoolingOptions poolingOptions) { + void setCoreConnectionsPerHost(PoolingOptions poolingOptions) { if (coreConnectionsPerHost != null) { poolingOptions.setCoreConnectionsPerHost(getHostDistance(), coreConnectionsPerHost); } - - return poolingOptions; } /** @@ -507,6 +516,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * @see com.datastax.driver.core.PoolingOptions#getCoreConnectionsPerHost(HostDistance) * @see #getHostDistance() */ + @Nullable protected Integer getCoreConnectionsPerHost() { return coreConnectionsPerHost; } @@ -515,12 +525,10 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * (non-Javadoc) * @see com.datastax.driver.core.PoolingOptions#setMaxConnectionsPerHost(HostDistance, int) */ - PoolingOptions setMaxConnectionsPerHost(PoolingOptions poolingOptions) { + void setMaxConnectionsPerHost(PoolingOptions poolingOptions) { if (maxConnectionsPerHost != null) { poolingOptions.setMaxConnectionsPerHost(getHostDistance(), maxConnectionsPerHost); } - - return poolingOptions; } /** @@ -530,6 +538,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * @see com.datastax.driver.core.PoolingOptions#getMaxConnectionsPerHost(HostDistance) * @see #getHostDistance() */ + @Nullable protected Integer getMaxConnectionsPerHost() { return maxConnectionsPerHost; } @@ -538,12 +547,10 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * (non-Javadoc) * @see com.datastax.driver.core.PoolingOptions#setMaxRequestsPerConnection(HostDistance, int) */ - PoolingOptions setMaxRequestsPerConnection(PoolingOptions poolingOptions) { + void setMaxRequestsPerConnection(PoolingOptions poolingOptions) { if (maxRequestsPerConnection != null) { poolingOptions.setMaxRequestsPerConnection(getHostDistance(), maxRequestsPerConnection); } - - return poolingOptions; } /** @@ -553,6 +560,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * @see com.datastax.driver.core.PoolingOptions#getMaxRequestsPerConnection(HostDistance) * @see #getHostDistance() */ + @Nullable protected Integer getMaxRequestsPerConnection() { return maxRequestsPerConnection; } @@ -564,7 +572,8 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * * @see com.datastax.driver.core.PoolingOptions#setNewConnectionThreshold(HostDistance, int) */ - PoolingOptions setNewConnectionThreshold(PoolingOptions poolingOptions) { + void setNewConnectionThreshold(PoolingOptions poolingOptions) { + if (newConnectionThreshold != null) { int currentNewConnectionThreshold = poolingOptions.getNewConnectionThreshold(getHostDistance()); @@ -572,8 +581,6 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I poolingOptions.setNewConnectionThreshold(getHostDistance(), newConnectionThreshold); } } - - return poolingOptions; } /** @@ -583,6 +590,7 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * @see com.datastax.driver.core.PoolingOptions#getNewConnectionThreshold(HostDistance) * @see #getHostDistance() */ + @Nullable protected Integer getNewConnectionThreshold() { return newConnectionThreshold; } @@ -611,22 +619,6 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I */ static class LocalHostDistancePoolingOptions extends HostDistancePoolingOptions { - /** - * Creates 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. - */ - static LocalHostDistancePoolingOptions create(Integer coreConnectionsPerHost, Integer maxConnectionsPerHost, - Integer maxRequestsPerConnection, Integer newConnectionThreshold) { - - return new LocalHostDistancePoolingOptions(coreConnectionsPerHost, maxConnectionsPerHost, - maxRequestsPerConnection, newConnectionThreshold); - } - /** * Constructs an instance of {@link LocalHostDistancePoolingOptions} initialized with {@link PoolingOptions} based * on {@link HostDistance#LOCAL}. @@ -636,8 +628,8 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * @param maxRequestsPerConnection maximum number of requests per connection. * @param newConnectionThreshold threshold that triggers the creation of a new connection to a host. */ - LocalHostDistancePoolingOptions(Integer coreConnectionsPerHost, Integer maxConnectionsPerHost, - Integer maxRequestsPerConnection, Integer newConnectionThreshold) { + LocalHostDistancePoolingOptions(@Nullable Integer coreConnectionsPerHost, @Nullable Integer maxConnectionsPerHost, + @Nullable Integer maxRequestsPerConnection, @Nullable Integer newConnectionThreshold) { super(coreConnectionsPerHost, maxConnectionsPerHost, maxRequestsPerConnection, newConnectionThreshold); } @@ -662,22 +654,6 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I */ static class RemoteHostDistancePoolingOptions extends HostDistancePoolingOptions { - /** - * Creates 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. - */ - static RemoteHostDistancePoolingOptions create(Integer coreConnectionsPerHost, Integer maxConnectionsPerHost, - Integer maxRequestsPerConnection, Integer newConnectionThreshold) { - - return new RemoteHostDistancePoolingOptions(coreConnectionsPerHost, maxConnectionsPerHost, - maxRequestsPerConnection, newConnectionThreshold); - } - /** * Constructs an instance of {@link RemoteHostDistancePoolingOptions} initialized with {@link PoolingOptions} based * on {@link HostDistance#REMOTE}. @@ -687,8 +663,8 @@ public class PoolingOptionsFactoryBean implements FactoryBean, I * @param maxRequestsPerConnection maximum number of requests per connection. * @param newConnectionThreshold threshold that triggers the creation of a new connection to a host. */ - RemoteHostDistancePoolingOptions(Integer coreConnectionsPerHost, Integer maxConnectionsPerHost, - Integer maxRequestsPerConnection, Integer newConnectionThreshold) { + RemoteHostDistancePoolingOptions(@Nullable Integer coreConnectionsPerHost, @Nullable Integer maxConnectionsPerHost, + @Nullable Integer maxRequestsPerConnection, @Nullable Integer newConnectionThreshold) { super(coreConnectionsPerHost, maxConnectionsPerHost, maxRequestsPerConnection, newConnectionThreshold); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SchemaAction.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SchemaAction.java index 68ad1ea05..96673f895 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SchemaAction.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SchemaAction.java @@ -48,21 +48,4 @@ public enum SchemaAction { * Drop all tables in the keyspace, then create each table as necessary. */ RECREATE_DROP_UNUSED - - // TODO: - // /** - // * Alter or create each table and column as necessary, leaving unused tables and columns untouched. - // */ - // UPDATE, - // - // /** - // * Alter or create each table and column as necessary, removing unused tables and columns. - // */ - // UPDATE_DROP_UNUNSED, - // - // /** - // * Validate that each required table and column exists. Fail if any required table or column does not exists. - // */ - // VALIDATE - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SocketOptionsFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SocketOptionsFactoryBean.java index 119418b2f..d386dbc5d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SocketOptionsFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/SocketOptionsFactoryBean.java @@ -18,6 +18,7 @@ package org.springframework.data.cassandra.config; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.lang.Nullable; import com.datastax.driver.core.SocketOptions; @@ -27,18 +28,19 @@ import com.datastax.driver.core.SocketOptions; * @author Matthew T. Adams * @author David Webb */ +@SuppressWarnings({ "unused", "WeakerAccess" }) public class SocketOptionsFactoryBean implements FactoryBean, InitializingBean, DisposableBean { - private Integer connectTimeoutMillis; - private Boolean keepAlive; - private Integer readTimeoutMillis; - private Boolean reuseAddress; - private Integer soLinger; - private Boolean tcpNoDelay; - private Integer receiveBufferSize; - private Integer sendBufferSize; + 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 SocketOptions socketOptions; + private @Nullable SocketOptions socketOptions; /* (non-Javadoc) * @see org.springframework.beans.factory.FactoryBean#getObject() @@ -122,57 +124,64 @@ public class SocketOptionsFactoryBean implements FactoryBean, Ini return true; } + @Nullable public Boolean getKeepAlive() { return keepAlive; } - public void setKeepAlive(Boolean keepAlive) { + public void setKeepAlive(@Nullable Boolean keepAlive) { this.keepAlive = keepAlive; } + @Nullable public Boolean getReuseAddress() { return reuseAddress; } - public void setReuseAddress(Boolean reuseAddress) { + public void setReuseAddress(@Nullable Boolean reuseAddress) { this.reuseAddress = reuseAddress; } + @Nullable public Integer getSoLinger() { return soLinger; } - public void setSoLinger(Integer soLinger) { + public void setSoLinger(@Nullable Integer soLinger) { this.soLinger = soLinger; } + @Nullable public Boolean getTcpNoDelay() { return tcpNoDelay; } - public void setTcpNoDelay(Boolean tcpNoDelay) { + public void setTcpNoDelay(@Nullable Boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; } + @Nullable public Integer getReceiveBufferSize() { return receiveBufferSize; } - public void setReceiveBufferSize(Integer receiveBufferSize) { + public void setReceiveBufferSize(@Nullable Integer receiveBufferSize) { this.receiveBufferSize = receiveBufferSize; } + @Nullable public Integer getSendBufferSize() { return sendBufferSize; } - public void setSendBufferSize(Integer sendBufferSize) { + public void setSendBufferSize(@Nullable Integer sendBufferSize) { this.sendBufferSize = sendBufferSize; } /** * @return Returns the connectTimeoutMillis. */ + @Nullable public Integer getConnectTimeoutMillis() { return connectTimeoutMillis; } @@ -180,13 +189,14 @@ public class SocketOptionsFactoryBean implements FactoryBean, Ini /** * @param connectTimeoutMillis The connectTimeoutMillis to set. */ - public void setConnectTimeoutMillis(Integer connectTimeoutMillis) { + public void setConnectTimeoutMillis(@Nullable Integer connectTimeoutMillis) { this.connectTimeoutMillis = connectTimeoutMillis; } /** * @return Returns the readTimeoutMillis. */ + @Nullable public Integer getReadTimeoutMillis() { return readTimeoutMillis; } @@ -194,7 +204,7 @@ public class SocketOptionsFactoryBean implements FactoryBean, Ini /** * @param readTimeoutMillis The readTimeoutMillis to set. */ - public void setReadTimeoutMillis(Integer readTimeoutMillis) { + public void setReadTimeoutMillis(@Nullable Integer readTimeoutMillis) { this.readTimeoutMillis = readTimeoutMillis; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/package-info.java index a22c3ade0..0619da9a4 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/package-info.java @@ -1,4 +1,7 @@ /** * Spring Data Cassandra {@link org.springframework.beans.factory.FactoryBean factory beans} and configuration. */ +@NonNullApi package org.springframework.data.cassandra.config; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraOperations.java index 0f7170b29..cf2a907d4 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraOperations.java @@ -240,9 +240,10 @@ public interface AsyncCassandraOperations { * Insert the given entity applying {@link WriteOptions} and return the entity if the insert was applied. * * @param entity The entity to insert, must not be {@literal null}. - * @param options may be {@literal null}. + * @param options must not be {@literal null}. * @return the {@link WriteResult} for this operation. * @throws DataAccessException if there is any problem executing the query. + * @see InsertOptions#empty() */ ListenableFuture insert(Object entity, InsertOptions options) throws DataAccessException; @@ -259,9 +260,10 @@ public interface AsyncCassandraOperations { * Update the given entity applying {@link WriteOptions} and return the entity if the update was applied. * * @param entity The entity to update, must not be {@literal null}. - * @param options may be {@literal null}. + * @param options must not be {@literal null}. * @return the {@link WriteResult} for this operation. * @throws DataAccessException if there is any problem executing the query. + * @see UpdateOptions#empty() */ ListenableFuture update(Object entity, UpdateOptions options) throws DataAccessException; @@ -278,9 +280,10 @@ public interface AsyncCassandraOperations { * Delete the given entity applying {@link QueryOptions} and return the entity if the delete was applied. * * @param entity must not be {@literal null}. - * @param options may be {@literal null}. + * @param options must not be {@literal null}. * @return the {@link WriteResult} for this operation. * @throws DataAccessException if there is any problem executing the query. + * @see QueryOptions#empty() */ ListenableFuture delete(Object entity, QueryOptions options) throws DataAccessException; @@ -303,5 +306,4 @@ public interface AsyncCassandraOperations { * @throws DataAccessException if there is any problem executing the query. */ ListenableFuture truncate(Class entityClass) throws DataAccessException; - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java index 9d32a0433..7cc2382d2 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java @@ -40,6 +40,7 @@ import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty; import org.springframework.data.cassandra.core.query.Query; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.concurrent.ListenableFuture; @@ -417,7 +418,7 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations { */ @Override public ListenableFuture insert(T entity) { - return new MappingListenableFutureAdapter<>(insert(entity, null), writeResult -> entity); + return new MappingListenableFutureAdapter<>(insert(entity, InsertOptions.empty()), writeResult -> entity); } /* @@ -428,6 +429,7 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations { public ListenableFuture insert(Object entity, InsertOptions options) { Assert.notNull(entity, "Entity must not be null"); + Assert.notNull(options, "InsertOptions must not be null"); Insert insert = QueryUtils.createInsertQuery(getTableName(entity).toCql(), entity, options, getConverter()); @@ -441,7 +443,7 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations { */ @Override public ListenableFuture update(T entity) { - return new MappingListenableFutureAdapter<>(update(entity, null), writeResult -> entity); + return new MappingListenableFutureAdapter<>(update(entity, UpdateOptions.empty()), writeResult -> entity); } /* @@ -452,6 +454,7 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations { public ListenableFuture update(Object entity, UpdateOptions options) { Assert.notNull(entity, "Entity must not be null"); + Assert.notNull(options, "UpdateOptions must not be null"); Update update = QueryUtils.createUpdateQuery(getTableName(entity).toCql(), entity, options, getConverter()); @@ -465,7 +468,7 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations { */ @Override public ListenableFuture delete(T entity) { - return new MappingListenableFutureAdapter<>(delete(entity, null), writeResult -> entity); + return new MappingListenableFutureAdapter<>(delete(entity, QueryOptions.empty()), writeResult -> entity); } /* @@ -476,6 +479,7 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations { public ListenableFuture delete(Object entity, QueryOptions options) { Assert.notNull(entity, "Entity must not be null"); + Assert.notNull(options, "QueryOptions must not be null"); Delete delete = QueryUtils.createDeleteQuery(getTableName(entity).toCql(), entity, options, getConverter()); @@ -528,7 +532,7 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations { } @Override - protected T adapt(S adapteeResult) throws ExecutionException { + protected T adapt(@Nullable S adapteeResult) throws ExecutionException { return mapper.apply(adapteeResult); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminOperations.java index 8c5f71695..c8d51d583 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminOperations.java @@ -78,5 +78,4 @@ public interface CassandraAdminOperations extends CassandraOperations { * @since 1.5 */ void dropUserType(CqlIdentifier typeName); - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java index f2351fe04..e50c0ee15 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraAdminTemplate.java @@ -126,6 +126,7 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand @Override public KeyspaceMetadata getKeyspaceMetadata() { + // noinspection ConstantConditions return getCqlOperations().execute((SessionCallback) session -> { KeyspaceMetadata keyspaceMetadata = session.getCluster().getMetadata().getKeyspace(session.getLoggedKeyspace()); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchOperations.java index 319367096..b14ccccfb 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchOperations.java @@ -129,5 +129,4 @@ public interface CassandraBatchOperations { * @throws IllegalStateException if the batch was already executed. */ CassandraBatchOperations delete(Iterable entities); - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java index 2aa15fc92..d4f251fdf 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java @@ -18,6 +18,7 @@ package org.springframework.data.cassandra.core; import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; +import org.springframework.data.cassandra.core.cql.QueryOptions; import org.springframework.data.cassandra.core.cql.WriteOptions; import org.springframework.util.Assert; @@ -34,8 +35,6 @@ import com.datastax.driver.core.querybuilder.QueryBuilder; */ class CassandraBatchTemplate implements CassandraBatchOperations { - private static final WriteOptions EMPTY = new WriteOptions(); - private AtomicBoolean executed = new AtomicBoolean(); private final Batch batch; @@ -47,7 +46,7 @@ class CassandraBatchTemplate implements CassandraBatchOperations { * * @param operations must not be {@literal null}. */ - public CassandraBatchTemplate(CassandraOperations operations) { + CassandraBatchTemplate(CassandraOperations operations) { Assert.notNull(operations, "CassandraOperations must not be null"); @@ -65,9 +64,7 @@ class CassandraBatchTemplate implements CassandraBatchOperations { return WriteResult.of(operations.getCqlOperations().queryForResultSet(batch)); } - assertNotExecuted(); - - return null; // code won't reach this line + throw new IllegalStateException("This Cassandra Batch was already executed"); } /* (non-Javadoc) @@ -99,7 +96,7 @@ class CassandraBatchTemplate implements CassandraBatchOperations { */ @Override public CassandraBatchOperations insert(Iterable entities) { - return insert(entities, EMPTY); + return insert(entities, InsertOptions.empty()); } /* (non-Javadoc) @@ -137,7 +134,7 @@ class CassandraBatchTemplate implements CassandraBatchOperations { */ @Override public CassandraBatchOperations update(Iterable entities) { - return update(entities, EMPTY); + return update(entities, UpdateOptions.empty()); } /* (non-Javadoc) @@ -181,7 +178,8 @@ class CassandraBatchTemplate implements CassandraBatchOperations { for (Object entity : entities) { Assert.notNull(entity, "Entity must not be null"); - batch.add(QueryUtils.createDeleteQuery(getTableName(entity), entity, null, operations.getConverter())); + batch.add( + QueryUtils.createDeleteQuery(getTableName(entity), entity, QueryOptions.empty(), operations.getConverter())); } return this; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java index e8d58a43a..1a42f50f1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java @@ -27,6 +27,7 @@ import org.springframework.data.cassandra.core.cql.QueryOptions; import org.springframework.data.cassandra.core.cql.WriteOptions; import org.springframework.data.cassandra.core.query.Query; import org.springframework.data.cassandra.core.query.Update; +import org.springframework.lang.Nullable; import com.datastax.driver.core.Statement; @@ -113,6 +114,7 @@ public interface CassandraOperations { * @return the converted object or {@literal null}. * @throws DataAccessException if there is any problem executing the query. */ + @Nullable T selectOne(String cql, Class entityClass) throws DataAccessException; // ------------------------------------------------------------------------- @@ -151,6 +153,7 @@ public interface CassandraOperations { * @return the converted object or {@literal null}. * @throws DataAccessException if there is any problem executing the query. */ + @Nullable T selectOne(Statement statement, Class entityClass) throws DataAccessException; // ------------------------------------------------------------------------- @@ -190,6 +193,7 @@ public interface CassandraOperations { * @throws DataAccessException if there is any problem executing the query. * @since 2.0 */ + @Nullable T selectOne(Query query, Class entityClass) throws DataAccessException; /** @@ -246,6 +250,7 @@ public interface CassandraOperations { * @return the converted object or {@literal null}. * @throws DataAccessException if there is any problem executing the query. */ + @Nullable T selectOneById(Object id, Class entityClass) throws DataAccessException; /** @@ -260,9 +265,10 @@ public interface CassandraOperations { * Insert the given entity applying {@link WriteOptions} and return the entity if the insert was applied. * * @param entity The entity to insert, must not be {@literal null}. - * @param options may be {@literal null}. + * @param options must not be {@literal null}. * @return the {@link WriteResult} for this operation. * @throws DataAccessException if there is any problem executing the query. + * @see InsertOptions#empty() */ WriteResult insert(Object entity, InsertOptions options) throws DataAccessException; @@ -278,9 +284,10 @@ public interface CassandraOperations { * Update the given entity applying {@link WriteOptions} and return the entity if the update was applied. * * @param entity The entity to update, must not be {@literal null}. - * @param options may be {@literal null}. + * @param options must not be {@literal null}. * @return the {@link WriteResult} for this operation. * @throws DataAccessException if there is any problem executing the query. + * @see UpdateOptions#empty() */ WriteResult update(Object entity, UpdateOptions options) throws DataAccessException; @@ -296,9 +303,10 @@ public interface CassandraOperations { * Delete the given entity applying {@link QueryOptions} and return the entity if the delete was applied. * * @param entity must not be {@literal null}. - * @param options may be {@literal null}. + * @param options must not be {@literal null}. * @return the {@link WriteResult} for this operation. * @throws DataAccessException if there is any problem executing the query. + * @see QueryOptions#empty() */ WriteResult delete(Object entity, QueryOptions options) throws DataAccessException; @@ -320,5 +328,4 @@ public interface CassandraOperations { * @throws DataAccessException if there is any problem executing the query. */ void truncate(Class entityClass) throws DataAccessException; - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index e9901c4bb..10f987b14 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -18,10 +18,7 @@ package org.springframework.data.cassandra.core; import lombok.NonNull; import lombok.Value; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -286,8 +283,8 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(query, "Query must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return select(getStatementFactory().select(query, - getMappingContext().getRequiredPersistentEntity(entityClass)), entityClass); + return select(getStatementFactory().select(query, getMappingContext().getRequiredPersistentEntity(entityClass)), + entityClass); } /* (non-Javadoc) @@ -299,8 +296,8 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(query, "Query must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return stream(getStatementFactory().select(query, - getMappingContext().getRequiredPersistentEntity(entityClass)), entityClass); + return stream(getStatementFactory().select(query, getMappingContext().getRequiredPersistentEntity(entityClass)), + entityClass); } /* (non-Javadoc) @@ -325,8 +322,8 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(update, "Update must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return getCqlOperations().execute(getStatementFactory().update(query, update, - getMappingContext().getRequiredPersistentEntity(entityClass))); + return getCqlOperations().execute( + getStatementFactory().update(query, update, getMappingContext().getRequiredPersistentEntity(entityClass))); } /* (non-Javadoc) @@ -338,8 +335,8 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(query, "Query must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return getCqlOperations().execute(getStatementFactory().delete(query, - getMappingContext().getRequiredPersistentEntity(entityClass))); + return getCqlOperations() + .execute(getStatementFactory().delete(query, getMappingContext().getRequiredPersistentEntity(entityClass))); } // ------------------------------------------------------------------------- @@ -358,7 +355,9 @@ public class CassandraTemplate implements CassandraOperations { Select select = QueryBuilder.select().countAll() .from(getMappingContext().getRequiredPersistentEntity(entityClass).getTableName().toCql()); - return getCqlOperations().queryForObject(select, Long.class); + Long count = getCqlOperations().queryForObject(select, Long.class); + + return count != null ? count : 0L; } /* @@ -405,7 +404,7 @@ public class CassandraTemplate implements CassandraOperations { */ @Override public void insert(Object entity) { - insert(entity, null); + insert(entity, InsertOptions.empty()); } /* @@ -416,10 +415,11 @@ public class CassandraTemplate implements CassandraOperations { public WriteResult insert(Object entity, InsertOptions options) { Assert.notNull(entity, "Entity must not be null"); + Assert.notNull(options, "InsertOptions must not be null"); - Insert insert = QueryUtils.createInsertQuery( - getTableName(entity.getClass()).toCql(), entity, options, converter); + Insert insert = QueryUtils.createInsertQuery(getTableName(entity.getClass()).toCql(), entity, options, converter); + // noinspection ConstantConditions return getCqlOperations().execute(new StatementCallback(insert)); } @@ -429,7 +429,7 @@ public class CassandraTemplate implements CassandraOperations { */ @Override public void update(Object entity) { - update(entity, null); + update(entity, UpdateOptions.empty()); } /* @@ -440,10 +440,11 @@ public class CassandraTemplate implements CassandraOperations { public WriteResult update(Object entity, UpdateOptions options) { Assert.notNull(entity, "Entity must not be null"); + Assert.notNull(options, "UpdateOptions must not be null"); - Update update = QueryUtils.createUpdateQuery( - getTableName(entity.getClass()).toCql(), entity, options, converter); + Update update = QueryUtils.createUpdateQuery(getTableName(entity.getClass()).toCql(), entity, options, converter); + // noinspection ConstantConditions return getCqlOperations().execute(new StatementCallback(update)); } @@ -453,7 +454,7 @@ public class CassandraTemplate implements CassandraOperations { */ @Override public void delete(Object entity) { - delete(entity, null); + delete(entity, QueryOptions.empty()); } /* @@ -464,10 +465,11 @@ public class CassandraTemplate implements CassandraOperations { public WriteResult delete(Object entity, QueryOptions options) { Assert.notNull(entity, "Entity must not be null"); + Assert.notNull(options, "QueryOptions must not be null"); - Delete delete = QueryUtils.createDeleteQuery( - getTableName(entity.getClass()).toCql(), entity, options, converter); + Delete delete = QueryUtils.createDeleteQuery(getTableName(entity.getClass()).toCql(), entity, options, converter); + // noinspection ConstantConditions return getCqlOperations().execute(new StatementCallback(delete)); } @@ -499,8 +501,8 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(entityClass, "Entity type must not be null"); - Truncate truncate = QueryBuilder.truncate( - getMappingContext().getRequiredPersistentEntity(entityClass).getTableName().toCql()); + Truncate truncate = QueryBuilder + .truncate(getMappingContext().getRequiredPersistentEntity(entityClass).getTableName().toCql()); getCqlOperations().execute(truncate); } @@ -528,19 +530,6 @@ public class CassandraTemplate implements CassandraOperations { return new CassandraBatchTemplate(this); } - private List toList(Iterable iterable) { - - if (iterable instanceof List) { - return (List) iterable; - } - - if (iterable instanceof Collection) { - return new ArrayList<>((Collection) iterable); - } - - return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()); - } - @Value static class StatementCallback implements SessionCallback, CqlProvider { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java index fbe9f1b70..b1c831103 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java @@ -15,9 +15,14 @@ */ package org.springframework.data.cassandra.core; +import java.time.Duration; import java.util.concurrent.TimeUnit; import org.springframework.data.cassandra.core.cql.WriteOptions; +import org.springframework.lang.Nullable; + +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.policies.RetryPolicy; /** * Extension to {@link WriteOptions} for use with {@code INSERT} operations. @@ -27,12 +32,26 @@ import org.springframework.data.cassandra.core.cql.WriteOptions; */ public class InsertOptions extends WriteOptions { + private static final InsertOptions EMPTY = new InsertOptionsBuilder().build(); + private boolean ifNotExists; + private InsertOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, + @Nullable Boolean tracing, @Nullable Integer fetchSize, Duration readTimeout, Duration ttl, boolean ifNotExists) { + + super(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout, ttl); + this.ifNotExists = ifNotExists; + } + /** - * Creates new {@link InsertOptions}. + * Create default {@link InsertOptions}. + * + * @return default {@link InsertOptions}. + * @since 2.0 */ - InsertOptions() {} + public static InsertOptions empty() { + return EMPTY; + } /** * Create a new {@link InsertOptionsBuilder}. @@ -62,73 +81,52 @@ public class InsertOptions extends WriteOptions { private InsertOptionsBuilder() {} - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#consistencyLevel(com.datastax.driver.core.ConsistencyLevel) - */ @Override public InsertOptionsBuilder consistencyLevel(com.datastax.driver.core.ConsistencyLevel consistencyLevel) { return (InsertOptionsBuilder) super.consistencyLevel(consistencyLevel); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#retryPolicy(org.springframework.data.cassandra.core.cql.RetryPolicy) - */ @Override public InsertOptionsBuilder retryPolicy(com.datastax.driver.core.policies.RetryPolicy driverRetryPolicy) { return (InsertOptionsBuilder) super.retryPolicy(driverRetryPolicy); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#fetchSize(int) - */ @Override public InsertOptionsBuilder fetchSize(int fetchSize) { return (InsertOptionsBuilder) super.fetchSize(fetchSize); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#readTimeout(long) - */ @Override public InsertOptionsBuilder readTimeout(long readTimeout) { return (InsertOptionsBuilder) super.readTimeout(readTimeout); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#readTimeout(long, java.util.concurrent.TimeUnit) - */ @Override + @Deprecated public InsertOptionsBuilder readTimeout(long readTimeout, TimeUnit timeUnit) { return (InsertOptionsBuilder) super.readTimeout(readTimeout, timeUnit); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#tracing(boolean) - */ + @Override + public InsertOptionsBuilder readTimeout(Duration readTimeout) { + return (InsertOptionsBuilder) super.readTimeout(readTimeout); + } + + @Override + public InsertOptionsBuilder ttl(Duration ttl) { + return (InsertOptionsBuilder) super.ttl(ttl); + } + @Override public InsertOptionsBuilder tracing(boolean tracing) { return (InsertOptionsBuilder) super.tracing(tracing); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#withTracing() - */ @Override public InsertOptionsBuilder withTracing() { return (InsertOptionsBuilder) super.withTracing(); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.WriteOptions.WriteOptionsBuilder#ttl(int) - */ public InsertOptionsBuilder ttl(int ttl) { return (InsertOptionsBuilder) super.ttl(ttl); } @@ -161,12 +159,7 @@ public class InsertOptions extends WriteOptions { * @return a new {@link InsertOptions} with the configured values */ public InsertOptions build() { - - InsertOptions insertOptions = applyOptions(new InsertOptions()); - - insertOptions.ifNotExists = this.ifNotExists; - - return insertOptions; + return new InsertOptions(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout, ttl, ifNotExists); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/QueryUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/QueryUtils.java index 04680c4ca..16a0b015a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/QueryUtils.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/QueryUtils.java @@ -46,7 +46,7 @@ class QueryUtils { * @param entityWriter the {@link EntityWriter} to write insert values. * @return The Query object to run with session.execute(); */ - public static Insert createInsertQuery(String tableName, Object objectToUpdate, WriteOptions options, + static Insert createInsertQuery(String tableName, Object objectToUpdate, WriteOptions options, EntityWriter entityWriter) { Assert.hasText(tableName, "TableName must not be empty"); @@ -75,11 +75,11 @@ class QueryUtils { * * @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, may 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(); */ - public static Update createUpdateQuery(String tableName, Object objectToUpdate, WriteOptions options, + static Update createUpdateQuery(String tableName, Object objectToUpdate, WriteOptions options, EntityWriter entityWriter) { Assert.hasText(tableName, "TableName must not be empty"); @@ -108,11 +108,11 @@ class QueryUtils { * * @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, may 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(); */ - public static Delete createDeleteQuery(String tableName, Object objectToDelete, QueryOptions options, + static Delete createDeleteQuery(String tableName, Object objectToDelete, QueryOptions options, EntityWriter entityWriter) { Assert.hasText(tableName, "TableName must not be empty"); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraOperations.java index 33a6f783d..513ea417a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraOperations.java @@ -186,9 +186,10 @@ public interface ReactiveCassandraOperations { * Insert the given entity applying {@link WriteOptions} and emit the entity if the insert was applied. * * @param entity The entity to insert, must not be {@literal null}. - * @param options may be {@literal null}. + * @param options must not be {@literal null}. * @return the {@link WriteResult} for this operation. * @throws DataAccessException if there is any problem issuing the execution. + * @see InsertOptions#empty() */ Mono insert(Object entity, InsertOptions options) throws DataAccessException; @@ -205,9 +206,10 @@ public interface ReactiveCassandraOperations { * Update the given entity applying {@link WriteOptions} and emit the entity if the update was applied. * * @param entity The entity to update, must not be {@literal null}. - * @param options may be {@literal null}. + * @param options must not be {@literal null}. * @return the {@link WriteResult} for this operation. * @throws DataAccessException if there is any problem issuing the execution. + * @see UpdateOptions#empty() */ Mono update(Object entity, UpdateOptions options) throws DataAccessException; @@ -224,9 +226,10 @@ public interface ReactiveCassandraOperations { * Delete the given entity applying {@link QueryOptions} and emit the entity if the delete was applied. * * @param entity must not be {@literal null}. - * @param options may be {@literal null}. + * @param options must not be {@literal null}. * @return the {@link WriteResult} for this operation. * @throws DataAccessException if there is any problem issuing the execution. + * @see QueryOptions#empty() */ Mono delete(Object entity, QueryOptions options) throws DataAccessException; @@ -264,5 +267,4 @@ public interface ReactiveCassandraOperations { * @see ReactiveCqlOperations */ ReactiveCqlOperations getReactiveCqlOperations(); - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java index 3187ba136..15a1b9fc9 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java @@ -17,10 +17,10 @@ package org.springframework.data.cassandra.core; import lombok.NonNull; import lombok.Value; - import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import org.reactivestreams.Publisher; import org.springframework.dao.DataAccessException; import org.springframework.data.cassandra.ReactiveResultSet; import org.springframework.data.cassandra.ReactiveSession; @@ -44,8 +44,6 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.reactivestreams.Publisher; - import com.datastax.driver.core.Session; import com.datastax.driver.core.SimpleStatement; import com.datastax.driver.core.Statement; @@ -378,7 +376,7 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { */ @Override public Mono insert(T entity) { - return insert(entity, null).map(writeResult -> entity); + return insert(entity, InsertOptions.empty()).map(writeResult -> entity); } /* @@ -389,6 +387,7 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { public Mono insert(Object entity, InsertOptions options) { Assert.notNull(entity, "Entity must not be null"); + Assert.notNull(options, "InsertOptions must not be null"); Insert insert = QueryUtils.createInsertQuery(getTableName(entity).toCql(), entity, options, getConverter()); @@ -401,7 +400,7 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { */ @Override public Mono update(T entity) { - return update(entity, null).map(writeResult -> entity); + return update(entity, UpdateOptions.empty()).map(writeResult -> entity); } /* @@ -412,6 +411,7 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { public Mono update(Object entity, UpdateOptions options) { Assert.notNull(entity, "Entity must not be null"); + Assert.notNull(options, "UpdateOptions must not be null"); Update update = QueryUtils.createUpdateQuery(getTableName(entity).toCql(), entity, options, converter); @@ -424,7 +424,7 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { */ @Override public Mono delete(T entity) { - return delete(entity, null).map(reactiveWriteResult -> entity); + return delete(entity, QueryOptions.empty()).map(reactiveWriteResult -> entity); } /* @@ -435,6 +435,7 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { public Mono delete(Object entity, QueryOptions options) { Assert.notNull(entity, "Entity must not be null"); + Assert.notNull(options, "QueryOptions must not be null"); Delete delete = QueryUtils.createDeleteQuery(getTableName(entity).toCql(), entity, options, getConverter()); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java index daa7182ad..3b4f31b6c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java @@ -140,8 +140,8 @@ public class StatementFactory { List selectors = getQueryMapper().getMappedSelectors(query.getColumns(), entity); - Sort sort = Optional.ofNullable(query.getSort()).map(querySort -> getQueryMapper().getMappedSort(querySort, entity)) - .orElse(null); + Sort sort = Optional.of(query.getSort()).map(querySort -> getQueryMapper().getMappedSort(querySort, entity)) + .orElse(Sort.unsorted()); Select select = select(selectors, entity.getTableName(), filter, sort); @@ -178,7 +178,7 @@ public class StatementFactory { select.where(toClause(criteriaDefinition)); } - if (sort != null) { + if (sort.isSorted()) { List orderings = new ArrayList<>(); for (Order order : sort) { @@ -412,7 +412,7 @@ public class StatementFactory { return QueryBuilder.in(columnName, (List) predicate.getValue()); } - if (predicate.getValue().getClass().isArray()) { + if (predicate.getValue() != null && predicate.getValue().getClass().isArray()) { return QueryBuilder.in(columnName, (Object[]) predicate.getValue()); } @@ -422,9 +422,13 @@ public class StatementFactory { return QueryBuilder.like(columnName, predicate.getValue()); case "CONTAINS": + Assert.state(predicate.getValue() != null, + () -> String.format("CONTAINS value for column %s is null", columnName)); return QueryBuilder.contains(columnName, predicate.getValue()); case "CONTAINS KEY": + Assert.state(predicate.getValue() != null, + () -> String.format("CONTAINS KEY value for column %s is null", columnName)); return QueryBuilder.containsKey(columnName, predicate.getValue()); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/UpdateOptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/UpdateOptions.java index 7d15edeaf..58f95f243 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/UpdateOptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/UpdateOptions.java @@ -15,9 +15,14 @@ */ package org.springframework.data.cassandra.core; +import java.time.Duration; import java.util.concurrent.TimeUnit; import org.springframework.data.cassandra.core.cql.WriteOptions; +import org.springframework.lang.Nullable; + +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.policies.RetryPolicy; /** * Extension to {@link WriteOptions} for use with {@code UPDATE} operations. @@ -27,12 +32,26 @@ import org.springframework.data.cassandra.core.cql.WriteOptions; */ public class UpdateOptions extends WriteOptions { + private static final UpdateOptions EMPTY = new UpdateOptionsBuilder().build(); + private boolean ifExists; + private UpdateOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, + @Nullable Boolean tracing, @Nullable Integer fetchSize, Duration readTimeout, Duration ttl, boolean ifExists) { + + super(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout, ttl); + this.ifExists = ifExists; + } + /** - * Creates new {@link UpdateOptions}. + * Create default {@link UpdateOptions}. + * + * @return default {@link UpdateOptions}. + * @since 2.0 */ - UpdateOptions() {} + public static UpdateOptions empty() { + return EMPTY; + } /** * Create a new {@link UpdateOptionsBuilder}. @@ -62,73 +81,52 @@ public class UpdateOptions extends WriteOptions { private UpdateOptionsBuilder() {} - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#consistencyLevel(com.datastax.driver.core.ConsistencyLevel) - */ @Override public UpdateOptionsBuilder consistencyLevel(com.datastax.driver.core.ConsistencyLevel consistencyLevel) { return (UpdateOptionsBuilder) super.consistencyLevel(consistencyLevel); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#retryPolicy(org.springframework.data.cassandra.core.cql.RetryPolicy) - */ @Override public UpdateOptionsBuilder retryPolicy(com.datastax.driver.core.policies.RetryPolicy driverRetryPolicy) { return (UpdateOptionsBuilder) super.retryPolicy(driverRetryPolicy); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#fetchSize(int) - */ @Override public UpdateOptionsBuilder fetchSize(int fetchSize) { return (UpdateOptionsBuilder) super.fetchSize(fetchSize); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#readTimeout(long) - */ @Override public UpdateOptionsBuilder readTimeout(long readTimeout) { return (UpdateOptionsBuilder) super.readTimeout(readTimeout); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#readTimeout(long, java.util.concurrent.TimeUnit) - */ @Override + @Deprecated public UpdateOptionsBuilder readTimeout(long readTimeout, TimeUnit timeUnit) { return (UpdateOptionsBuilder) super.readTimeout(readTimeout, timeUnit); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#tracing(boolean) - */ + @Override + public UpdateOptionsBuilder readTimeout(Duration readTimeout) { + return (UpdateOptionsBuilder) super.readTimeout(readTimeout); + } + + @Override + public UpdateOptionsBuilder ttl(Duration ttl) { + return (UpdateOptionsBuilder) super.ttl(ttl); + } + @Override public UpdateOptionsBuilder tracing(boolean tracing) { return (UpdateOptionsBuilder) super.tracing(tracing); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#withTracing() - */ @Override public UpdateOptionsBuilder withTracing() { return (UpdateOptionsBuilder) super.withTracing(); } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.WriteOptions.WriteOptionsBuilder#ttl(int) - */ public UpdateOptionsBuilder ttl(int ttl) { return (UpdateOptionsBuilder) super.ttl(ttl); } @@ -161,12 +159,7 @@ public class UpdateOptions extends WriteOptions { * @return a new {@link UpdateOptions} with the configured values */ public UpdateOptions build() { - - UpdateOptions insertOptions = applyOptions(new UpdateOptions()); - - insertOptions.ifExists = this.ifExists; - - return insertOptions; + return new UpdateOptions(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout, ttl, ifExists); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/WriteResult.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/WriteResult.java index 536e054b8..f1583deb8 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/WriteResult.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/WriteResult.java @@ -35,7 +35,6 @@ import com.datastax.driver.core.Row; public class WriteResult { private final boolean wasApplied; - private final List executionInfo; private final List rows; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/AbstractCassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/AbstractCassandraConverter.java index bf6b56dac..c5d2164ce 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/AbstractCassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/AbstractCassandraConverter.java @@ -19,10 +19,10 @@ import java.util.Collections; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.EntityInstantiators; +import org.springframework.util.Assert; /** * Base class for {@link CassandraConverter} implementations. Sets up a {@link ConversionService} and populates basic @@ -36,32 +36,36 @@ import org.springframework.data.convert.EntityInstantiators; */ public abstract class AbstractCassandraConverter implements CassandraConverter, InitializingBean { - protected final ConversionService conversionService; + private final ConversionService conversionService; - protected CustomConversions conversions = new CassandraCustomConversions(Collections.emptyList()); + private CustomConversions conversions = new CassandraCustomConversions(Collections.emptyList()); - protected EntityInstantiators instantiators = new EntityInstantiators(); + EntityInstantiators instantiators = new EntityInstantiators(); /** * Create a new {@link AbstractCassandraConverter} using the given {@link ConversionService}. */ - public AbstractCassandraConverter(ConversionService conversionService) { - this.conversionService = conversionService == null ? new DefaultConversionService() : conversionService; + protected AbstractCassandraConverter(ConversionService conversionService) { + + Assert.notNull(conversionService, "ConversionService must not be null"); + + this.conversionService = conversionService; } /** * Registers {@link EntityInstantiators} to customize entity instantiation. * - * @param instantiators + * @param instantiators must not be {@literal null}. */ public void setInstantiators(EntityInstantiators instantiators) { - this.instantiators = instantiators == null ? new EntityInstantiators() : instantiators; + + Assert.notNull(instantiators, "EntityInstantiators must not be null"); + + this.instantiators = instantiators; } /** * Registers the given custom conversions with the converter. - * - * @param conversions */ public void setCustomConversions(CustomConversions conversions) { this.conversions = conversions; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/BasicCassandraRowValueProvider.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/BasicCassandraRowValueProvider.java index ea86930da..05fe68cde 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/BasicCassandraRowValueProvider.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/BasicCassandraRowValueProvider.java @@ -18,6 +18,7 @@ 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.Row; @@ -55,6 +56,7 @@ public class BasicCassandraRowValueProvider implements CassandraRowValueProvider /* (non-Javadoc) * @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty) */ + @Nullable @Override @SuppressWarnings("unchecked") public T getPropertyValue(CassandraPersistentProperty property) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraConverter.java index e485c1931..d0c4fe670 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraConverter.java @@ -22,6 +22,7 @@ import org.springframework.data.cassandra.core.mapping.MapId; import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.EntityConverter; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; /** * Central Cassandra specific converter interface from Object to Row. @@ -61,6 +62,7 @@ public interface CassandraConverter * @param entity must not be {@literal null}. * @return */ + @Nullable Object getId(Object object, CassandraPersistentEntity entity); /** @@ -76,7 +78,7 @@ public interface CassandraConverter * Converts the given object into a value Cassandra will be able to store natively in a column. * * @param value {@link Object} to convert; must not be {@literal null}. - * @param typeInformation {@link TypeInformation} used to describe the object type; may be {@literal null}. + * @param typeInformation {@link TypeInformation} used to describe the object type; must not be {@literal null}. * @return the result of the conversion. * @since 1.5 */ @@ -85,10 +87,9 @@ public interface CassandraConverter /** * Converts and writes a {@code source} object into a {@code sink} using the given {@link CassandraPersistentEntity}. * - * @param source the source, may be {@literal null}. + * @param source the source, must not be {@literal null}. * @param sink must not be {@literal null}. * @param entity must not be {@literal null}. */ void write(Object source, Object sink, CassandraPersistentEntity entity); - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraConverters.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraConverters.java index 9ae0582a8..de82dd3ba 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraConverters.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraConverters.java @@ -140,7 +140,7 @@ abstract class CassandraConverters { private final Class targetType; - public RowToNumber(Class targetType) { + RowToNumber(Class targetType) { this.targetType = targetType; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraCustomConversions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraCustomConversions.java index 18b51f3d3..50e591832 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraCustomConversions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraCustomConversions.java @@ -38,6 +38,7 @@ public class CassandraCustomConversions extends org.springframework.data.convert private static final List STORE_CONVERTERS; static { + List converters = new ArrayList<>(); converters.addAll(CassandraConverters.getConvertersToRegister()); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraUDTValueProvider.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraUDTValueProvider.java index 2503fd478..0e464911f 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraUDTValueProvider.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraUDTValueProvider.java @@ -18,6 +18,7 @@ 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; @@ -61,6 +62,7 @@ public class CassandraUDTValueProvider implements CassandraValueProvider { /* (non-Javadoc) * @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty) */ + @Nullable @SuppressWarnings("unchecked") public T getPropertyValue(CassandraPersistentProperty property) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/ColumnReader.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/ColumnReader.java index 1cd6b233e..df7d0b5c5 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/ColumnReader.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/ColumnReader.java @@ -18,6 +18,7 @@ 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 com.datastax.driver.core.CodecRegistry; import com.datastax.driver.core.ColumnDefinitions; @@ -40,6 +41,7 @@ public class ColumnReader { private final CodecRegistry codecRegistry; public ColumnReader(Row row) { + this.row = row; this.columns = row.getColumnDefinitions(); this.codecRegistry = CodecRegistry.DEFAULT_INSTANCE; @@ -48,6 +50,7 @@ public class ColumnReader { /** * Returns the row's column value. */ + @Nullable public Object get(CqlIdentifier name) { return get(name.toCql()); } @@ -55,6 +58,7 @@ public class ColumnReader { /** * Returns the row's column value. */ + @Nullable public Object get(String name) { int indexOf = getColumnIndex(name); return get(indexOf); @@ -66,6 +70,7 @@ public class ColumnReader { * @param index * @return */ + @Nullable public Object get(int index) { if (row.isNull(index)) { @@ -89,6 +94,7 @@ public class ColumnReader { return row.getObject(index); } + @Nullable public Object getCollection(int i, DataType type) { List collectionTypes = type.getTypeArguments(); @@ -131,6 +137,7 @@ public class ColumnReader { * * @throws ClassCastException if the value cannot be converted to the requested type. */ + @Nullable public T get(CqlIdentifier name, Class requestedType) { return get(getColumnIndex(name.toCql()), requestedType); } @@ -140,6 +147,7 @@ public class ColumnReader { * * @throws ClassCastException if the value cannot be converted to the requested type. */ + @Nullable public T get(String name, Class requestedType) { return get(columns.getIndexOf(name), requestedType); } @@ -149,6 +157,7 @@ public class ColumnReader { * * @throws ClassCastException if the value cannot be converted to the requested type. */ + @Nullable @SuppressWarnings("unchecked") public T get(int i, Class requestedType) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java index b7a3ea68b..79207d540 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java @@ -51,6 +51,7 @@ import org.springframework.data.mapping.model.PersistentEntityParameterValueProv import org.springframework.data.mapping.model.SpELContext; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -86,7 +87,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter private final CassandraMappingContext mappingContext; - private ClassLoader beanClassLoader; + private @Nullable ClassLoader beanClassLoader; private SpELContext spELContext; @@ -151,6 +152,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * @param row must not be {@literal null}. * @return the converted valued. */ + @Nullable @SuppressWarnings("unchecked") public R readRow(Class type, Row row) { @@ -302,24 +304,22 @@ public class MappingCassandraConverter extends AbstractCassandraConverter @Override public Object convertToColumnType(Object value, TypeInformation typeInformation) { + Assert.notNull(value, "Value must not be null"); Assert.notNull(typeInformation, "TypeInformation must not be null"); - if (value == null) { - return null; - } - + // noinspection ConstantConditions return value.getClass().isArray() ? value : getWriteValue(value, typeInformation); } @Override public void write(Object source, Object sink) { - if (source != null) { - Class beanClassLoaderClass = transformClassToBeanClassLoaderClass(source.getClass()); - CassandraPersistentEntity entity = getMappingContext().getRequiredPersistentEntity(beanClassLoaderClass); + Assert.notNull(source, "Value must not be null"); - write(source, sink, entity); - } + Class beanClassLoaderClass = transformClassToBeanClassLoaderClass(source.getClass()); + CassandraPersistentEntity entity = getMappingContext().getRequiredPersistentEntity(beanClassLoaderClass); + + write(source, sink, entity); } @SuppressWarnings("unchecked") @@ -335,9 +335,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter @SuppressWarnings("unchecked") public void write(Object source, Object sink, CassandraPersistentEntity entity) { - if (source == null) { - return; - } + Assert.notNull(source, "Value must not be null"); if (entity == null) { throw new MappingException("No mapping metadata found for " + source.getClass()); @@ -360,11 +358,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter } } - protected void writeInsertFromObject(final Object object, final Insert insert, CassandraPersistentEntity entity) { + protected void writeInsertFromObject(Object object, Insert insert, CassandraPersistentEntity entity) { writeInsertFromWrapper(getConvertingAccessor(object, entity), insert, entity); } - private void writeMapFromWrapper(final ConvertingPropertyAccessor accessor, final Map insert, + private void writeMapFromWrapper(ConvertingPropertyAccessor accessor, Map insert, CassandraPersistentEntity entity) { for (CassandraPersistentProperty property : entity) { @@ -381,6 +379,10 @@ public class MappingCassandraConverter extends AbstractCassandraConverter log.debug("Property is a compositeKey"); } + if (value == null) { + continue; + } + CassandraPersistentEntity compositePrimaryKey = mappingContext.getRequiredPersistentEntity(property); writeMapFromWrapper(getConvertingAccessor(value, compositePrimaryKey), insert, compositePrimaryKey); @@ -412,6 +414,10 @@ public class MappingCassandraConverter extends AbstractCassandraConverter log.debug("Property is a compositeKey"); } + if (value == null) { + continue; + } + CassandraPersistentEntity compositePrimaryKey = mappingContext.getRequiredPersistentEntity(property); writeInsertFromWrapper(getConvertingAccessor(value, compositePrimaryKey), insert, compositePrimaryKey); @@ -445,7 +451,12 @@ public class MappingCassandraConverter extends AbstractCassandraConverter CassandraPersistentEntity compositePrimaryKey = mappingContext.getRequiredPersistentEntity(property); + if (value == null) { + continue; + } + writeUpdateFromWrapper(getConvertingAccessor(value, compositePrimaryKey), update, compositePrimaryKey); + continue; } @@ -696,6 +707,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * @param accessor the property accessor * @return the return value, may be {@literal null}. */ + @Nullable @SuppressWarnings("unchecked") private T getWriteValue(CassandraPersistentProperty property, ConvertingPropertyAccessor accessor) { return (T) getWriteValue(accessor.getProperty(property, (Class) getTargetType(property)), @@ -710,8 +722,9 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * @param typeInformation the type information. * @return the return value, may be {@literal null}. */ + @Nullable @SuppressWarnings("unchecked") - private Object getWriteValue(Object value, TypeInformation typeInformation) { + private Object getWriteValue(@Nullable Object value, @Nullable TypeInformation typeInformation) { if (value == null) { return null; @@ -735,7 +748,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter TypeInformation type = typeInformation != null ? typeInformation : ClassTypeInformation.from((Class) value.getClass()); - TypeInformation actualType = type.getActualType(); + TypeInformation actualType = type.getRequiredActualType(); if (value instanceof Collection) { @@ -771,7 +784,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * @see CassandraType */ @SuppressWarnings("unchecked") - private Object getPotentiallyConvertedSimpleValue(Object value, Class requestedTargetType) { + @Nullable + private Object getPotentiallyConvertedSimpleValue(@Nullable Object value, @Nullable Class requestedTargetType) { if (value == null) { return null; @@ -800,8 +814,9 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * @param target must not be {@literal null}. * @return the converted value. */ + @Nullable @SuppressWarnings({ "rawtypes", "unchecked" }) - private Object getPotentiallyConvertedSimpleRead(Object value, Class target) { + private Object getPotentiallyConvertedSimpleRead(@Nullable Object value, @Nullable Class target) { if (value == null || target == null || target.isAssignableFrom(value.getClass())) { return value; @@ -847,6 +862,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * @param property the property. * @return the return value, may be {@literal null}. */ + @Nullable @SuppressWarnings("unchecked") protected Object getReadValue(CassandraValueProvider row, CassandraPersistentProperty property) { @@ -899,6 +915,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * @param sourceValue must not be {@literal null}. * @return the converted {@link Collection} or array, will never be {@literal null}. */ + @Nullable @SuppressWarnings({ "rawtypes", "unchecked" }) private Object readCollectionOrArray(TypeInformation targetType, Collection sourceValue) { @@ -917,7 +934,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return getPotentiallyConvertedSimpleRead(items, collectionType); } - BasicCassandraPersistentEntity entity = getMappingContext().getPersistentEntity(componentType); + BasicCassandraPersistentEntity entity = getMappingContext().getPersistentEntity(rawComponentType); if (entity != null && entity.isUserDefinedType()) { @@ -961,7 +978,9 @@ public class MappingCassandraConverter extends AbstractCassandraConverter /* (non-Javadoc) * @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty) */ + @Nullable @Override + @SuppressWarnings("unchecked") public T getPropertyValue(CassandraPersistentProperty property) { return (T) getReadValue(parent, property); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/QueryMapper.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/QueryMapper.java index 92d03e004..18015690c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/QueryMapper.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/QueryMapper.java @@ -46,22 +46,18 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.PersistentPropertyPath; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** * Map {@link org.springframework.data.cassandra.core.query.Query} to CQL-specific data types. * * @author Mark Paluch - * @see org.springframework.data.cassandra.core.query.ColumnName - * @see org.springframework.data.cassandra.core.query.Columns - * @see org.springframework.data.cassandra.core.query.Criteria - * @see org.springframework.data.cassandra.core.query.Filter - * @see org.springframework.data.domain.Sort - * @see org.springframework.data.mapping.PersistentProperty - * @see org.springframework.data.mapping.PropertyPath - * @see org.springframework.data.mapping.context.MappingContext - * @see org.springframework.data.mapping.context.PersistentPropertyPath - * @see org.springframework.data.util.TypeInformation + * @see ColumnName + * @see Columns + * @see Criteria + * @see Filter + * @see Sort * @since 2.0 */ public class QueryMapper { @@ -131,7 +127,7 @@ public class QueryMapper { Object value = predicate.getValue(); TypeInformation typeInformation = getTypeInformation(field, value); - Object mappedValue = getConverter().convertToColumnType(value, typeInformation); + Object mappedValue = value != null ? getConverter().convertToColumnType(value, typeInformation) : null; Predicate mappedPredicate = new Predicate(predicate.getOperator(), mappedValue); @@ -328,18 +324,13 @@ public class QueryMapper { } } - /** - * @param entity - * @param key - * @return - */ protected Field createPropertyField(CassandraPersistentEntity entity, ColumnName key) { - return Optional.ofNullable(entity). map(e -> new MetadataBackedField(key, e, getMappingContext())) + return Optional.of(entity). map(e -> new MetadataBackedField(key, e, getMappingContext())) .orElseGet(() -> new Field(key)); } @SuppressWarnings("unchecked") - TypeInformation getTypeInformation(Field field, Object value) { + TypeInformation getTypeInformation(Field field, @Nullable Object value) { if (field.getProperty().isPresent()) { return field.getProperty().get().getTypeInformation(); @@ -366,7 +357,7 @@ public class QueryMapper { * * @param name must not be {@literal null} or empty. */ - public Field(ColumnName name) { + Field(ColumnName name) { Assert.notNull(name, "Name must not be null!"); this.name = name; } @@ -385,8 +376,6 @@ public class QueryMapper { * Returns the underlying {@link CassandraPersistentProperty} backing the field. For path traversals this will be * the property that represents the value to handle. This means it'll be the leaf property for plain paths or the * association property in case we refer to an association somewhere in the path. - * - * @return */ public Optional getProperty() { return Optional.empty(); @@ -394,8 +383,6 @@ public class QueryMapper { /** * Returns the key to be used in the mapped document eventually. - * - * @return */ public ColumnName getMappedKey() { return name; @@ -412,7 +399,7 @@ public class QueryMapper { private final CassandraPersistentEntity entity; private final MappingContext, CassandraPersistentProperty> mappingContext; private final Optional> path; - private final CassandraPersistentProperty property; + private final @Nullable CassandraPersistentProperty property; private final Optional optionalProperty; /** @@ -440,7 +427,7 @@ public class QueryMapper { */ public MetadataBackedField(ColumnName name, CassandraPersistentEntity entity, MappingContext, CassandraPersistentProperty> mappingContext, - CassandraPersistentProperty property) { + @Nullable CassandraPersistentProperty property) { super(name); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/RowReaderPropertyAccessor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/RowReaderPropertyAccessor.java index 76cacee0a..997b88e92 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/RowReaderPropertyAccessor.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/RowReaderPropertyAccessor.java @@ -18,6 +18,7 @@ package org.springframework.data.cassandra.core.convert; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypedValue; +import org.springframework.lang.Nullable; import com.datastax.driver.core.Row; @@ -38,12 +39,16 @@ enum RowReaderPropertyAccessor implements PropertyAccessor { } @Override - public boolean canRead(EvaluationContext context, Object target, String name) { - return ((Row) target).getColumnDefinitions().contains(name); + public boolean canRead(EvaluationContext context, @Nullable Object target, String name) { + return target != null && ((Row) target).getColumnDefinitions().contains(name); } @Override - public TypedValue read(EvaluationContext context, Object target, String name) { + public TypedValue read(EvaluationContext context, @Nullable Object target, String name) { + + if (target == null) { + return TypedValue.NULL; + } Row row = (Row) target; @@ -55,12 +60,12 @@ enum RowReaderPropertyAccessor implements PropertyAccessor { } @Override - public boolean canWrite(EvaluationContext context, Object target, String name) { + public boolean canWrite(EvaluationContext context, @Nullable Object target, String name) { return false; } @Override - public void write(EvaluationContext context, Object target, String name, Object newValue) { + public void write(EvaluationContext context, @Nullable Object target, String name, @Nullable Object newValue) { throw new UnsupportedOperationException(); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/UpdateMapper.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/UpdateMapper.java index 6ca8eb899..3c292b415 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/UpdateMapper.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/UpdateMapper.java @@ -120,12 +120,13 @@ public class UpdateMapper extends QueryMapper { Object rawValue = updateOp.getValue(); - Object value = rawValue; - if (updateOp instanceof SetAtKeyOp) { SetAtKeyOp op = (SetAtKeyOp) updateOp; + Assert.state(op.getValue() != null, + () -> String.format("SetAtKeyOp for %s attempts to set null", field.getProperty())); + Optional> typeInformation = field.getProperty() .map(PersistentProperty::getTypeInformation); @@ -141,12 +142,15 @@ public class UpdateMapper extends QueryMapper { return new SetAtKeyOp(field.getMappedKey(), mappedKey, mappedValue); } - TypeInformation typeInformation = getTypeInformation(field, value); + TypeInformation typeInformation = getTypeInformation(field, rawValue); if (updateOp instanceof SetAtIndexOp) { SetAtIndexOp op = (SetAtIndexOp) updateOp; + Assert.state(op.getValue() != null, + () -> String.format("SetAtIndexOp for %s attempts to set null", field.getProperty())); + Object mappedValue = getConverter().convertToColumnType(op.getValue(), typeInformation); return new SetAtIndexOp(field.getMappedKey(), op.getIndex(), mappedValue); @@ -169,7 +173,7 @@ public class UpdateMapper extends QueryMapper { } } - Object mappedValue = getConverter().convertToColumnType(value, typeInformation); + Object mappedValue = rawValue == null ? null : getConverter().convertToColumnType(rawValue, typeInformation); return new SetOp(field.getMappedKey(), mappedValue); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/package-info.java index 44c551c3f..089ec1bce 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/package-info.java @@ -1,4 +1,7 @@ /** * Spring Data Cassandra specific converter infrastructure. */ +@NonNullApi package org.springframework.data.cassandra.core.convert; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ArgumentPreparedStatementBinder.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ArgumentPreparedStatementBinder.java index b9f8f83bf..baab58548 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ArgumentPreparedStatementBinder.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ArgumentPreparedStatementBinder.java @@ -15,6 +15,8 @@ */ 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; @@ -27,14 +29,14 @@ import com.datastax.driver.core.exceptions.DriverException; */ public class ArgumentPreparedStatementBinder implements PreparedStatementBinder { - private final Object[] args; + private final @Nullable Object[] args; /** * Create a new {@link ArgumentPreparedStatementBinder} for the given arguments. * * @param args the arguments to set. May be empty or {@link null} if no arguments are provided. */ - public ArgumentPreparedStatementBinder(Object[] args) { + public ArgumentPreparedStatementBinder(@Nullable Object[] args) { this.args = args; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncCqlOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncCqlOperations.java index ce641eb9a..69235e58a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncCqlOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncCqlOperations.java @@ -22,6 +22,7 @@ import java.util.Map; import org.springframework.dao.DataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.lang.Nullable; import org.springframework.util.concurrent.ListenableFuture; import com.datastax.driver.core.PreparedStatement; @@ -42,9 +43,6 @@ import com.datastax.driver.core.Statement; */ public interface AsyncCqlOperations { - // TODO many of these data access operations could be implemented as default methods, in terms of other data access - // operations - // ------------------------------------------------------------------------- // Methods dealing with a plain com.datastax.driver.core.Session // ------------------------------------------------------------------------- @@ -70,7 +68,7 @@ public interface AsyncCqlOperations { /** * Issue a single CQL execute, typically a DDL statement, insert, update or delete statement. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @return boolean value whether the statement was applied. * @throws DataAccessException if there is any problem executing the query. */ @@ -80,7 +78,7 @@ public interface AsyncCqlOperations { * Issue a single CQL operation (such as an insert, update or delete statement) via a prepared statement, binding the * given arguments. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding * CQL type). * @return boolean value whether the statement was applied. @@ -93,14 +91,14 @@ public interface AsyncCqlOperations { * using a {@link AsyncPreparedStatementCreator} as this method will create the {@link PreparedStatement}: The * {@link PreparedStatementBinder} just needs to set parameters. * - * @param cql static CQL to execute, must not be empty or {@literal null}. - * @param preparedStatementBinder 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 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. * @return boolean value whether the statement was applied. * @throws DataAccessException if there is any problem issuing the execution. */ - ListenableFuture execute(String cql, PreparedStatementBinder preparedStatementBinder) + ListenableFuture execute(String cql, @Nullable PreparedStatementBinder psb) throws DataAccessException; /** @@ -111,7 +109,7 @@ public interface AsyncCqlOperations { *

* 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 empty or {@literal null}. + * @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 TODO: Lambda-usage clashes with execute(cql, @@ -125,7 +123,7 @@ public interface AsyncCqlOperations { * 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 empty or {@literal null}. + * @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. * @throws DataAccessException if there is any problem executing the query. @@ -138,9 +136,9 @@ public interface AsyncCqlOperations { * {@link RowCallbackHandler}. *

* 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 {@code null} as argument array. + * {@link PreparedStatement}, use the overloaded {@code query} method with {@literal null} as argument array. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param rowCallbackHandler object that will extract results, one row at a time, must not be {@literal null}. * @throws DataAccessException if there is any problem executing the query * @see #query(String, RowCallbackHandler, Object[]) @@ -153,7 +151,7 @@ public interface AsyncCqlOperations { * 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 empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param rowMapper object that will map one object per row, must not be {@literal null}. * @return the result {@link List}, containing mapped objects. * @throws DataAccessException if there is any problem executing the query @@ -165,7 +163,7 @@ 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}. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @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). @@ -179,7 +177,7 @@ 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} on a per-row basis with a {@link RowCallbackHandler}. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param rowCallbackHandler object that will extract results, one row at a time, 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) @@ -192,7 +190,7 @@ public interface AsyncCqlOperations { * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, mapping each * row to a Java object via a {@link RowMapper}. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param rowMapper object that will map one object per row * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding * CQL type) @@ -204,15 +202,15 @@ public interface AsyncCqlOperations { /** * Query using a prepared statement, reading the {@link ResultSet} with a {@link ResultSetExtractor}. * - * @param cql static CQL to execute, must not be empty or {@literal null}. - * @param preparedStatementBinder 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 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}. * @throws DataAccessException if there is any problem */ - ListenableFuture query(String cql, PreparedStatementBinder preparedStatementBinder, + ListenableFuture query(String cql, @Nullable PreparedStatementBinder psb, ResultSetExtractor resultSetExtractor) throws DataAccessException; /** @@ -220,29 +218,29 @@ public interface AsyncCqlOperations { * knows how to bind values to the query, reading the {@link ResultSet} on a per-row basis with a * {@link RowCallbackHandler}. * - * @param cql static CQL to execute, must not be empty or {@literal null}. - * @param preparedStatementBinder 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 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 rowCallbackHandler object that will extract results, one row at a time, must not be {@literal null}. * @throws DataAccessException if there is any problem executing the query. */ - ListenableFuture query(String cql, PreparedStatementBinder preparedStatementBinder, + ListenableFuture query(String cql, @Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException; /** * Query given CQL to create a prepared statement from CQL and a {@link PreparedStatementBinder} implementation that * knows how to bind values to the query, mapping each row to a Java object via a {@link RowMapper}. * - * @param cql static CQL to execute, must not be empty or {@literal null}. - * @param preparedStatementBinder 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 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 rowMapper object that will map one object per row, must not be {@literal null}. * @return the result {@link List}, containing mapped objects. * @throws DataAccessException if there is any problem executing the query. */ - ListenableFuture> query(String cql, PreparedStatementBinder preparedStatementBinder, + ListenableFuture> query(String cql, @Nullable PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException; /** @@ -255,7 +253,7 @@ public interface AsyncCqlOperations { * using the column name as the key). Each item in the {@link List} will be of the form returned by this interface's * queryForMap() methods. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @return a {@link List} that contains a {@link Map} per row. * @throws DataAccessException if there is any problem executing the query. * @see #queryForList(String, Object[]) @@ -270,7 +268,7 @@ public interface AsyncCqlOperations { * using the column name as the key). Each item in the {@link List} will be of the form returned by this interface's * queryForMap() methods. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding * CQL type). * @return a {@link List} that contains a {@link Map} per row @@ -288,7 +286,7 @@ public interface AsyncCqlOperations { * The results will be mapped to a {@link List} (one item for each row) of result objects, each of them matching the * specified element type. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param elementType the required type of element in the result {@link List} (for example, {@code Integer.class}), * must not be {@literal null}. * @return a {@link List} of objects that match the specified element type. @@ -305,7 +303,7 @@ public interface AsyncCqlOperations { * The results will be mapped to a {@link List} (one item for each row) of result objects, each of them matching the * specified element type. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param elementType the required type of element in the result {@link List} (for example, {@code Integer.class}), * must not be {@literal null}. * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding @@ -328,7 +326,7 @@ public interface AsyncCqlOperations { * The query is expected to be a single row query; the result row will be mapped to a Map (one entry for each column, * using the column name as the key). * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @return the result Map (one entry for each column, using the column name as the key), must not be {@literal null}. * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row. * @throws DataAccessException if there is any problem executing the query. @@ -345,7 +343,7 @@ public interface AsyncCqlOperations { * The query is expected to be a single row query; the result row will be mapped to a Map (one entry for each column, * using the column name as the key). * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding * CQL type). * @return the result Map (one entry for each column, using the column name as the key). @@ -366,7 +364,7 @@ public interface AsyncCqlOperations { * This method is useful for running static CQL with a known outcome. The query is expected to be a single row/single * column query; the returned result will be directly mapped to the corresponding object type. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param requiredType the type that the result object is expected to match, must not be {@literal null}. * @return the result object of the required type, or {@link Mono#empty()} in case of CQL NULL. * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row, or does not return @@ -383,7 +381,7 @@ public interface AsyncCqlOperations { * The query is expected to be a single row/single column query; the returned result will be directly mapped to the * corresponding object type. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param requiredType the type that the result object is expected to match, must not be {@literal null}. * @param args arguments to bind to the query (leaving it to the PreparedStatement to guess the corresponding CQL * type) @@ -402,7 +400,7 @@ public interface AsyncCqlOperations { * {@link PreparedStatement}, use the overloaded {@link #queryForObject(String, RowMapper, Object...)} method with * {@literal null} as argument array. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param rowMapper object that will map one object per row, must not be {@literal null}. * @return the single mapped object. * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row. @@ -415,7 +413,7 @@ public interface AsyncCqlOperations { * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, mapping a * single result row to a Java object via a {@link RowMapper}. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param rowMapper object that will map one object per row, 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) @@ -434,7 +432,7 @@ public interface AsyncCqlOperations { *

* The results will be mapped to an {@link ResultSet}. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @return a {@link ResultSet} representation. * @throws DataAccessException if there is any problem executing the query. * @see #queryForResultSet(String, Object[]) @@ -447,7 +445,7 @@ public interface AsyncCqlOperations { *

* The results will be mapped to an {@link ResultSet}. * - * @param cql static CQL to execute, must not be empty or {@literal null}. + * @param cql static CQL to execute, must not be {@literal null} or empty. * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding * CQL type). * @return a {@link ResultSet} representation. @@ -489,7 +487,7 @@ public interface AsyncCqlOperations { * {@link RowCallbackHandler}. *

* 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 {@code null} as argument array. + * {@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 rowCallbackHandler object that will extract results, one row at a time, must not be {@literal null}. @@ -522,7 +520,7 @@ public interface AsyncCqlOperations { * using the column name as the key). Each item in the {@link List} will be of the form returned by this interface's * queryForMap() methods. * - * @param statement static CQL {@link Statement} to execute, must not be empty or {@literal null}. + * @param statement static CQL {@link Statement} to execute, must not be {@literal null} or empty. * @return a {@link List} that contains a {@link Map} per row. * @throws DataAccessException if there is any problem executing the query. * @see #queryForList(String, Object[]) @@ -693,15 +691,15 @@ public interface AsyncCqlOperations { * * @param preparedStatementCreator object that can create a {@link PreparedStatement} given a * {@link com.datastax.driver.core.Session}, must not be {@literal null}. - * @param preparedStatementBinder 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 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}. * @throws DataAccessException if there is any problem */ ListenableFuture query(AsyncPreparedStatementCreator preparedStatementCreator, - PreparedStatementBinder preparedStatementBinder, ResultSetExtractor resultSetExtractor) + @Nullable PreparedStatementBinder psb, ResultSetExtractor resultSetExtractor) throws DataAccessException; /** @@ -710,14 +708,14 @@ public interface AsyncCqlOperations { * * @param preparedStatementCreator object that can create a {@link PreparedStatement} given a * {@link com.datastax.driver.core.Session}, must not be {@literal null}. - * @param preparedStatementBinder 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 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 rowCallbackHandler object that will extract results, one row at a time, must not be {@literal null}. * @throws DataAccessException if there is any problem executing the query. */ ListenableFuture query(AsyncPreparedStatementCreator preparedStatementCreator, - PreparedStatementBinder preparedStatementBinder, RowCallbackHandler rowCallbackHandler) + @Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException; /** @@ -726,14 +724,13 @@ public interface AsyncCqlOperations { * * @param preparedStatementCreator object that can create a {@link PreparedStatement} given a * {@link com.datastax.driver.core.Session}, must not be {@literal null}. - * @param preparedStatementBinder 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 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 rowMapper object that will map one object per row, must not be {@literal null}. * @return the result {@link List}, containing mapped objects. * @throws DataAccessException if there is any problem executing the query. */ ListenableFuture> query(AsyncPreparedStatementCreator preparedStatementCreator, - PreparedStatementBinder preparedStatementBinder, RowMapper rowMapper) throws DataAccessException; - + @Nullable PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncCqlTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncCqlTemplate.java index 487e77b8a..d996de4e6 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncCqlTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncCqlTemplate.java @@ -24,6 +24,7 @@ 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.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.SettableListenableFuture; @@ -412,13 +413,13 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera /* * (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#execute(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementBinder) + * @see org.springframework.data.cassandra.core.cql.AsyncCqlOperations#execute(java.lang.String, org.springframework.data.cassandra.core.cql.@Nullablender) */ @Override - public ListenableFuture execute(String cql, PreparedStatementBinder preparedStatementBinder) + public ListenableFuture execute(String cql, @Nullable PreparedStatementBinder psb) throws DataAccessException { - return query(newAsyncPreparedStatementCreator(cql), preparedStatementBinder, ResultSet::wasApplied); + return query(newAsyncPreparedStatementCreator(cql), psb, ResultSet::wasApplied); } /* @@ -441,6 +442,8 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera Assert.notNull(preparedStatementCreator, "PreparedStatementCreator must not be null"); Assert.notNull(action, "PreparedStatementCallback object must not be null"); + PersistenceExceptionTranslator exceptionTranslator = ex -> translateExceptionIfPossible("PreparedStatementCallback", + toCql(preparedStatementCreator), ex); try { if (logger.isDebugEnabled()) { logger.debug("Preparing statement [{}] using {}", toCql(preparedStatementCreator), preparedStatementCreator); @@ -452,12 +455,12 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera try { return action.doInPreparedStatement(currentSession, applyStatementSettings(preparedStatement)); } catch (DriverException e) { - throw translateException("PreparedStatementCallback", preparedStatement.toString(), e); + throw translateException(exceptionTranslator, e); } }), getExceptionTranslator()); } catch (DriverException e) { - throw translateException("PreparedStatementCallback", toCql(preparedStatementCreator), e); + throw translateException(exceptionTranslator, e); } } @@ -503,30 +506,30 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera */ @Override public ListenableFuture query(AsyncPreparedStatementCreator preparedStatementCreator, - PreparedStatementBinder preparedStatementBinder, ResultSetExtractor resultSetExtractor) + @Nullable PreparedStatementBinder psb, ResultSetExtractor resultSetExtractor) throws DataAccessException { Assert.notNull(preparedStatementCreator, "AsyncPreparedStatementCreator must not be null"); Assert.notNull(resultSetExtractor, "ResultSetExtractor object must not be null"); + PersistenceExceptionTranslator exceptionTranslator = ex -> translateExceptionIfPossible("Query", + toCql(preparedStatementCreator), ex); + try { + if (logger.isDebugEnabled()) { logger.debug("Preparing statement [{}] using {}", toCql(preparedStatementCreator), preparedStatementCreator); } Session session = getCurrentSession(); - PersistenceExceptionTranslator exceptionTranslator = ex -> translateExceptionIfPossible("Query", - toCql(preparedStatementCreator), ex); - ListenableFuture statementFuture = new MappingListenableFutureAdapter<>( preparedStatementCreator.createPreparedStatement(session), preparedStatement -> { if (logger.isDebugEnabled()) { logger.debug("Executing prepared statement [{}]", preparedStatement); } - return applyStatementSettings(preparedStatementBinder != null - ? preparedStatementBinder.bindValues(preparedStatement) : preparedStatement.bind()); + return applyStatementSettings(psb != null ? psb.bindValues(preparedStatement) : preparedStatement.bind()); }); SettableListenableFuture settableListenableFuture = new SettableListenableFuture<>(); @@ -534,27 +537,25 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera statementFuture.addCallback( boundStatement -> Futures.addCallback(session.executeAsync(boundStatement), new FutureCallback() { @Override - public void onSuccess(ResultSet result) { + public void onSuccess(@Nullable ResultSet result) { try { - settableListenableFuture.set(resultSetExtractor.extractData(result)); + settableListenableFuture.set(result != null ? resultSetExtractor.extractData(result) : null); } catch (DriverException e) { - settableListenableFuture.setException(exceptionTranslator.translateExceptionIfPossible(e)); + settableListenableFuture.setException(translateException(exceptionTranslator, e)); } } @Override public void onFailure(Throwable ex) { if (ex instanceof DriverException) { - settableListenableFuture - .setException(exceptionTranslator.translateExceptionIfPossible((DriverException) ex)); + settableListenableFuture.setException(translateException(exceptionTranslator, (DriverException) ex)); } else { settableListenableFuture.setException(ex); } } }), ex -> { if (ex instanceof DriverException) { - settableListenableFuture - .setException(exceptionTranslator.translateExceptionIfPossible((DriverException) ex)); + settableListenableFuture.setException(translateException(exceptionTranslator, (DriverException) ex)); } else { settableListenableFuture.setException(ex); } @@ -563,7 +564,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera return settableListenableFuture; } catch (DriverException e) { - throw translateException("Query", toCql(preparedStatementCreator), e); + throw translateException(exceptionTranslator, e); } } @@ -573,10 +574,10 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera */ @Override public ListenableFuture query(AsyncPreparedStatementCreator preparedStatementCreator, - PreparedStatementBinder preparedStatementBinder, RowCallbackHandler rowCallbackHandler) + @Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException { - ListenableFuture results = query(preparedStatementCreator, preparedStatementBinder, + ListenableFuture results = query(preparedStatementCreator, psb, newResultSetExtractor(rowCallbackHandler)); return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null), @@ -589,9 +590,8 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera */ @Override public ListenableFuture> query(AsyncPreparedStatementCreator preparedStatementCreator, - PreparedStatementBinder preparedStatementBinder, RowMapper rowMapper) throws DataAccessException { - - return query(preparedStatementCreator, preparedStatementBinder, newResultSetExtractor(rowMapper)); + @Nullable PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException { + return query(preparedStatementCreator, psb, newResultSetExtractor(rowMapper)); } /* @@ -601,7 +601,6 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera @Override public ListenableFuture query(String cql, ResultSetExtractor resultSetExtractor, Object... args) throws DataAccessException { - return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), resultSetExtractor); } @@ -627,7 +626,6 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera @Override public ListenableFuture> query(String cql, RowMapper rowMapper, Object... args) throws DataAccessException { - return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), newResultSetExtractor(rowMapper)); } @@ -637,10 +635,10 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera * @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) */ @Override - public ListenableFuture query(String cql, PreparedStatementBinder preparedStatementBinder, + public ListenableFuture query(String cql, @Nullable PreparedStatementBinder psb, ResultSetExtractor resultSetExtractor) throws DataAccessException { - return query(newAsyncPreparedStatementCreator(cql), preparedStatementBinder, resultSetExtractor); + return query(newAsyncPreparedStatementCreator(cql), psb, resultSetExtractor); } /* @@ -648,10 +646,10 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera * @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.RowCallbackHandler) */ @Override - public ListenableFuture query(String cql, PreparedStatementBinder preparedStatementBinder, + public ListenableFuture query(String cql, @Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException { - ListenableFuture results = query(newAsyncPreparedStatementCreator(cql), preparedStatementBinder, + ListenableFuture results = query(newAsyncPreparedStatementCreator(cql), psb, newResultSetExtractor(rowCallbackHandler)); return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null), @@ -663,10 +661,10 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera * @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.RowMapper) */ @Override - public ListenableFuture> query(String cql, PreparedStatementBinder preparedStatementBinder, + public ListenableFuture> query(String cql, @Nullable PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException { - return query(newAsyncPreparedStatementCreator(cql), preparedStatementBinder, newResultSetExtractor(rowMapper)); + return query(newAsyncPreparedStatementCreator(cql), psb, newResultSetExtractor(rowMapper)); } /* @@ -757,13 +755,12 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera * Translate the given {@link DriverException} 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 {@code null}) + * @param cql CQL query or update that caused the problem (may be {@literal null}) * @param ex the offending {@code RuntimeException}. * @return the exception translation {@link Function} * @see CqlProvider */ - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - protected DataAccessException translateException(String task, String cql, DriverException ex) { + protected DataAccessException translateException(String task, @Nullable String cql, DriverException ex) { return translate(task, cql, ex); } @@ -771,18 +768,30 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera * Translate the given {@link DriverException} 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 {@code null}) + * @param cql CQL query or update that caused the problem (may be {@literal null}) * @param ex the offending {@code RuntimeException}. * @return the translated {@link DataAccessException} or {@literal null} if translation not possible. * @see CqlProvider */ - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - protected DataAccessException translateExceptionIfPossible(String task, String cql, RuntimeException ex) { + @Nullable + protected DataAccessException translateExceptionIfPossible(String task, @Nullable String cql, RuntimeException ex) { return (ex instanceof DriverException ? translate(task, cql, (DriverException) ex) : null); } private Session getCurrentSession() { - return getSessionFactory().getSession(); + + SessionFactory sessionFactory = getSessionFactory(); + + Assert.state(sessionFactory != null, "SessionFactory is null"); + + return sessionFactory.getSession(); + } + + private static RuntimeException translateException(PersistenceExceptionTranslator exceptionTranslator, + DriverException e) { + + DataAccessException translated = exceptionTranslator.translateExceptionIfPossible(e); + return translated == null ? e : translated; } private static class SimpleAsyncPreparedStatementCreator implements AsyncPreparedStatementCreator, CqlProvider { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncPreparedStatementCreator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncPreparedStatementCreator.java index 00783c531..9c7b47f7f 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncPreparedStatementCreator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncPreparedStatementCreator.java @@ -36,6 +36,7 @@ import com.datastax.driver.core.exceptions.DriverException; * * @author Mark Paluch * @since 2.0 + * @see AsyncCqlTemplate#execute(AsyncPreparedStatementCreator, PreparedStatementCallback) */ @FunctionalInterface public interface AsyncPreparedStatementCreator { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncSessionCallback.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncSessionCallback.java index 27da3e275..d2882c2ef 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncSessionCallback.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/AsyncSessionCallback.java @@ -48,7 +48,7 @@ public interface AsyncSessionCallback { * template. * * @param session active Cassandra Session, must not be {@literal null}. - * @return a result object, or {@code null} if none. + * @return a result object, or {@code ListenableFuture} if none. * @throws DriverException if thrown by a Session method, to be auto-converted to a {@link DataAccessException}. * @throws DataAccessException in case of custom exceptions. * @see AsyncCqlTemplate#queryForObject(String, Class) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraAccessor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraAccessor.java index 9a33093aa..aa46202a2 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraAccessor.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraAccessor.java @@ -25,6 +25,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessException; import org.springframework.data.cassandra.SessionFactory; 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; @@ -59,7 +60,7 @@ public class CassandraAccessor implements InitializingBean { /** Logger available to subclasses */ protected final Logger logger = LoggerFactory.getLogger(getClass()); - protected CqlExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator(); + 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 @@ -71,15 +72,15 @@ public class CassandraAccessor implements InitializingBean { * 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 com.datastax.driver.core.ConsistencyLevel consistencyLevel; + private @Nullable com.datastax.driver.core.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 com.datastax.driver.core.policies.RetryPolicy retryPolicy; + private @Nullable com.datastax.driver.core.policies.RetryPolicy retryPolicy; - private SessionFactory sessionFactory; + private @Nullable SessionFactory sessionFactory; /** * Ensures the Cassandra {@link Session} and exception translator has been propertly set. @@ -97,13 +98,14 @@ public class CassandraAccessor implements InitializingBean { * @see Statement#setConsistencyLevel(ConsistencyLevel) * @see RetryPolicy */ - public void setConsistencyLevel(ConsistencyLevel consistencyLevel) { + public void setConsistencyLevel(@Nullable ConsistencyLevel consistencyLevel) { this.consistencyLevel = consistencyLevel; } /** * @return the {@link ConsistencyLevel} specified for this template. */ + @Nullable public ConsistencyLevel getConsistencyLevel() { return this.consistencyLevel; } @@ -118,6 +120,7 @@ public class CassandraAccessor implements InitializingBean { public void setExceptionTranslator(CqlExceptionTranslator exceptionTranslator) { Assert.notNull(exceptionTranslator, "CQLExceptionTranslator must not be null"); + this.exceptionTranslator = exceptionTranslator; } @@ -129,9 +132,6 @@ public class CassandraAccessor implements InitializingBean { * @see CqlExceptionTranslator */ public CqlExceptionTranslator getExceptionTranslator() { - - Assert.state(this.exceptionTranslator != null, "CQLExceptionTranslator was not properly initialized"); - return this.exceptionTranslator; } @@ -160,13 +160,14 @@ public class CassandraAccessor implements InitializingBean { * @see Statement#setRetryPolicy(RetryPolicy) * @see RetryPolicy */ - public void setRetryPolicy(RetryPolicy retryPolicy) { + public void setRetryPolicy(@Nullable RetryPolicy retryPolicy) { this.retryPolicy = retryPolicy; } /** * @return the {@link RetryPolicy} specified for this template. */ + @Nullable public RetryPolicy getRetryPolicy() { return this.retryPolicy; } @@ -224,6 +225,7 @@ public class CassandraAccessor implements InitializingBean { * @since 2.0 * @see SessionFactory */ + @Nullable public SessionFactory getSessionFactory() { return this.sessionFactory; } @@ -300,6 +302,7 @@ public class CassandraAccessor implements InitializingBean { * exception hierarchy * @see DataAccessException */ + @Nullable protected DataAccessException translateExceptionIfPossible(DriverException ex) { Assert.notNull(ex, "DriverException must not be null"); @@ -316,7 +319,7 @@ public class CassandraAccessor implements InitializingBean { * 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 {@code null}) + * @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} * @see org.springframework.dao.DataAccessException#getRootCause() @@ -324,7 +327,7 @@ public class CassandraAccessor implements InitializingBean { * "http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#dao-exceptions">Consistent * exception hierarchy */ - protected DataAccessException translate(String task, String cql, DriverException ex) { + protected DataAccessException translate(String task, @Nullable String cql, DriverException ex) { Assert.notNull(ex, "DriverException must not be null"); @@ -410,10 +413,12 @@ public class CassandraAccessor implements InitializingBean { * Determine CQL from potential provider object. * * @param cqlProvider object that's potentially a {@link CqlProvider} - * @return the CQL string, or {@code null} + * @return the CQL string, or {@literal null} * @see CqlProvider */ - protected static String toCql(Object cqlProvider) { + @Nullable + protected static String toCql(@Nullable Object cqlProvider) { + return Optional.ofNullable(cqlProvider) // .filter(o -> o instanceof CqlProvider) // .map(o -> (CqlProvider) o) // @@ -433,10 +438,10 @@ public class CassandraAccessor implements InitializingBean { } /* (non-Javadoc) - * - @see org.springframework.data.cassandra.core.cql.ResultSetExtractor#extractData(com.datastax.driver.core.ResultSet) + * @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); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraExceptionTranslator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraExceptionTranslator.java index 0d6c41afe..56d0be6aa 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraExceptionTranslator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraExceptionTranslator.java @@ -27,6 +27,7 @@ import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.TransientDataAccessResourceException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.cassandra.*; +import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -59,6 +60,7 @@ public class CassandraExceptionTranslator implements CqlExceptionTranslator { * @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException) */ @Override + @Nullable public DataAccessException translateExceptionIfPossible(RuntimeException exception) { if (exception instanceof DataAccessException) { @@ -76,7 +78,7 @@ public class CassandraExceptionTranslator implements CqlExceptionTranslator { * @see org.springframework.data.cassandra.cql.CQLExceptionTranslator#translate(java.lang.String, java.lang.String, com.datastax.driver.core.exceptions.DriverException) */ @Override - public DataAccessException translate(String task, String cql, DriverException exception) { + public DataAccessException translate(@Nullable String task, @Nullable String cql, DriverException exception) { String message = buildMessage(task, cql, exception); @@ -183,11 +185,11 @@ public class CassandraExceptionTranslator implements CqlExceptionTranslator { * {@link org.springframework.dao.DataAccessException} class. * * @param task readable text describing the task being attempted - * @param cql the CQL statement that caused the problem (may be {@code null}) + * @param cql the CQL statement that caused the problem (may be {@literal null}) * @param ex the offending {@code DriverException} * @return the message {@code String} to use */ - protected String buildMessage(String task, String cql, DriverException ex) { + protected String buildMessage(@Nullable String task, @Nullable String cql, DriverException ex) { if (StringUtils.hasText(task) || StringUtils.hasText(cql)) { return task + "; CQL [" + cql + "]; " + ex.getMessage(); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ColumnMapRowMapper.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ColumnMapRowMapper.java index 0e0111e87..9a664ad2e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ColumnMapRowMapper.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ColumnMapRowMapper.java @@ -17,6 +17,7 @@ package org.springframework.data.cassandra.core.cql; import java.util.Map; +import org.springframework.lang.Nullable; import org.springframework.util.LinkedCaseInsensitiveMap; import com.datastax.driver.core.ColumnDefinitions; @@ -94,6 +95,7 @@ public class ColumnMapRowMapper implements RowMapper> { * @param index is the column index. * @return the Object returned */ + @Nullable protected Object getColumnValue(Row row, int index) { return row.getObject(index); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlExceptionTranslator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlExceptionTranslator.java index 3e5d6113f..bd738aad4 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlExceptionTranslator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlExceptionTranslator.java @@ -17,6 +17,8 @@ package org.springframework.data.cassandra.core.cql; import org.springframework.dao.DataAccessException; 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; @@ -40,12 +42,15 @@ public interface CqlExceptionTranslator extends PersistenceExceptionTranslator { * 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 {@code null}). + * @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}. * @see org.springframework.dao.DataAccessException#getRootCause() */ - default DataAccessException translate(String task, String cql, DriverException ex) { - return translateExceptionIfPossible(ex); + default DataAccessException translate(@Nullable String task, @Nullable String cql, DriverException ex) { + + DataAccessException translated = translateExceptionIfPossible(ex); + return translated == null ? new UnsupportedCassandraOperationException("Cannot translate exception", ex) + : translated; } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlIdentifier.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlIdentifier.java index 55459ab4b..d1f4d4ee5 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlIdentifier.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlIdentifier.java @@ -35,7 +35,6 @@ import com.datastax.driver.core.TableMetadata; * @author Mark Paluch * @author John Blum * @see #toCql() - * @see #toCql(StringBuilder) * @see #toString() */ public final class CqlIdentifier implements Comparable, Serializable { @@ -50,18 +49,18 @@ public final class CqlIdentifier implements Comparable, Serializa public static final Pattern QUOTED = Pattern.compile(QUOTED_REGEX); - private String identifier; + private final String identifier; - private String unquoted; + private final String unquoted; - private boolean quoted; + private final boolean quoted; /** * Create a new {@link CqlIdentifier} without force-quoting it. It may end up quoted, depending on its value. * * @see #cqlId(CharSequence) */ - public CqlIdentifier(CharSequence identifier) { + private CqlIdentifier(CharSequence identifier) { this(identifier, false); } @@ -78,8 +77,25 @@ public final class CqlIdentifier implements Comparable, Serializa * @see #cqlId(CharSequence, boolean) * @see #quotedCqlId(CharSequence) */ - public CqlIdentifier(CharSequence identifier, boolean forceQuote) { - setIdentifier(identifier, forceQuote); + private CqlIdentifier(CharSequence identifier, boolean forceQuote) { + + Assert.notNull(identifier, "Identifier must not be null"); + + String string = identifier.toString(); + + Assert.hasText(string, "Identifier must not be empty"); + + if (forceQuote || isQuotedIdentifier(string)) { + this.unquoted = string; + this.identifier = "\"" + string + "\""; + this.quoted = true; + } else if (isUnquotedIdentifier(string)) { + this.identifier = this.unquoted = string.toLowerCase(); + this.quoted = false; + } else { + throw new IllegalArgumentException( + String.format("given string [%s] is not a valid quoted or unquoted identifier", identifier)); + } } /** @@ -123,29 +139,6 @@ public final class CqlIdentifier implements Comparable, Serializa return QUOTED.matcher(chars).matches() || ReservedKeyword.isReserved(chars); } - /** - * Tests & sets the given identifier. - */ - private void setIdentifier(CharSequence identifier, boolean forceQuoting) { - - Assert.notNull(identifier, "Identifier must not be null"); - - String string = identifier.toString(); - - Assert.hasText(string, "Identifier must not be empty"); - - if (forceQuoting || isQuotedIdentifier(string)) { - this.unquoted = string; - this.identifier = "\"" + string + "\""; - quoted = true; - } else if (isUnquotedIdentifier(string)) { - this.identifier = this.unquoted = string.toLowerCase(); - } else { - throw new IllegalArgumentException( - String.format("given string [%s] is not a valid quoted or unquoted identifier", identifier)); - } - } - /** * Returns the identifier without encasing quotes, regardless of the value of {@link #isQuoted()}. For * example, if {@link #isQuoted()} is {@code true}, then this value will be the same as {@link #toCql()} and @@ -167,10 +160,11 @@ public final class CqlIdentifier implements Comparable, Serializa /** * Appends the rendering of this identifier to the given {@link StringBuilder}, then returns that - * {@link StringBuilder}. If {@code null} is given, a new {@link StringBuilder} is created, appended to, and returned. + * {@link StringBuilder}. If {@literal null} is given, a new {@link StringBuilder} is created, appended to, and + * returned. */ public StringBuilder toCql(StringBuilder builder) { - return (builder != null ? builder : new StringBuilder()).append(toCql()); + return builder.append(toCql()); } /** @@ -211,7 +205,7 @@ public final class CqlIdentifier implements Comparable, Serializa if (quoted != that.quoted) return false; - return identifier != null ? identifier.equals(that.identifier) : that.identifier == null; + return identifier.equals(that.identifier); } /* (non-Javadoc) @@ -220,7 +214,7 @@ public final class CqlIdentifier implements Comparable, Serializa @Override public int hashCode() { - int result = identifier != null ? identifier.hashCode() : 0; + int result = identifier.hashCode(); result = 31 * result + (quoted ? 1 : 0); return result; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlOperations.java index 939dfbe2c..8dc44fc0e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlOperations.java @@ -23,6 +23,7 @@ import java.util.Map; 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; @@ -56,6 +57,7 @@ public interface CqlOperations { * @return a result object returned by the action, or {@literal null}. * @throws DataAccessException if there is any problem executing the query. */ + @Nullable T execute(SessionCallback action) throws DataAccessException; // ------------------------------------------------------------------------- @@ -95,7 +97,7 @@ public interface CqlOperations { * @return boolean value whether the statement was applied. * @throws DataAccessException if there is any problem issuing the execution. */ - boolean execute(String cql, PreparedStatementBinder psb) throws DataAccessException; + boolean execute(String cql, @Nullable PreparedStatementBinder psb) throws DataAccessException; /** * Execute a CQL data access operation, implemented as callback action working on a CQL {@link PreparedStatement}. @@ -110,6 +112,7 @@ public interface CqlOperations { * @return a result object returned by the action, or {@literal null} * @throws DataAccessException if there is any problem */ + @Nullable T execute(String cql, PreparedStatementCallback action) throws DataAccessException; /** @@ -124,6 +127,7 @@ public interface CqlOperations { * @throws DataAccessException if there is any problem executing the query. * @see #query(String, ResultSetExtractor, Object...) */ + @Nullable T query(String cql, ResultSetExtractor resultSetExtractor) throws DataAccessException; /** @@ -131,7 +135,7 @@ public interface CqlOperations { * {@link RowCallbackHandler}. *

* 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 {@code null} as argument array. + * {@link PreparedStatement}, use the overloaded {@code query} method with {@literal null} as argument array. * * @param cql static CQL to execute, must not be empty or {@literal null}. * @param rowCallbackHandler object that will extract results, one row at a time, must not be {@literal null}. @@ -165,6 +169,7 @@ public interface CqlOperations { * @return an arbitrary result object, as returned by the {@link ResultSetExtractor} * @throws DataAccessException if there is any problem executing the query. */ + @Nullable T query(String cql, ResultSetExtractor resultSetExtractor, Object... args) throws DataAccessException; /** @@ -196,14 +201,15 @@ public interface CqlOperations { * Query using a prepared statement, reading the {@link ResultSet} with a {@link ResultSetExtractor}. * * @param cql static CQL to execute, must not be empty or {@literal null}. - * @param preparedStatementBinder 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 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}. * @throws DataAccessException if there is any problem */ - T query(String cql, PreparedStatementBinder preparedStatementBinder, ResultSetExtractor resultSetExtractor) + @Nullable + T query(String cql, @Nullable PreparedStatementBinder psb, ResultSetExtractor resultSetExtractor) throws DataAccessException; /** @@ -212,13 +218,13 @@ public interface CqlOperations { * {@link RowCallbackHandler}. * * @param cql static CQL to execute, must not be empty or {@literal null}. - * @param preparedStatementBinder 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 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 rowCallbackHandler object that will extract results, one row at a time, must not be {@literal null}. * @throws DataAccessException if there is any problem executing the query. */ - void query(String cql, PreparedStatementBinder preparedStatementBinder, RowCallbackHandler rowCallbackHandler) + void query(String cql, @Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException; /** @@ -226,14 +232,14 @@ public interface CqlOperations { * knows how to bind values to the query, mapping each row to a Java object via a {@link RowMapper}. * * @param cql static CQL to execute, must not be empty or {@literal null}. - * @param preparedStatementBinder 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 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 rowMapper object that will map one object per row, must not be {@literal null}. * @return the result {@link List}, containing mapped objects. * @throws DataAccessException if there is any problem executing the query. */ - List query(String cql, PreparedStatementBinder preparedStatementBinder, RowMapper rowMapper) + List query(String cql, @Nullable PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException; /** @@ -364,6 +370,7 @@ public interface CqlOperations { * @throws DataAccessException if there is any problem executing the query. * @see #queryForObject(String, Class, Object[]) */ + @Nullable T queryForObject(String cql, Class requiredType) throws DataAccessException; /** @@ -383,6 +390,7 @@ public interface CqlOperations { * @throws DataAccessException if there is any problem executing the query. * @see #queryForObject(String, Class) */ + @Nullable T queryForObject(String cql, Class requiredType, Object... args) throws DataAccessException; /** @@ -502,6 +510,7 @@ public interface CqlOperations { * @throws DataAccessException if there is any problem executing the query. * @see #query(String, ResultSetExtractor, Object...) */ + @Nullable T query(Statement statement, ResultSetExtractor resultSetExtractor) throws DataAccessException; /** @@ -509,7 +518,7 @@ public interface CqlOperations { * {@link RowCallbackHandler}. *

* 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 {@code null} as argument array. + * {@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 rowCallbackHandler object that will extract results, one row at a time, must not be {@literal null}. @@ -605,6 +614,7 @@ public interface CqlOperations { * @throws DataAccessException if there is any problem executing the query. * @see #queryForObject(String, Class, Object[]) */ + @Nullable T queryForObject(Statement statement, Class requiredType) throws DataAccessException; /** @@ -621,6 +631,7 @@ public interface CqlOperations { * @throws DataAccessException if there is any problem executing the query. * @see #queryForObject(String, RowMapper, Object[]) */ + @Nullable T queryForObject(Statement statement, RowMapper rowMapper) throws DataAccessException; /** @@ -684,6 +695,7 @@ public interface CqlOperations { * @return a result object returned by the action, or {@literal null}. * @throws DataAccessException if there is any problem */ + @Nullable T execute(PreparedStatementCreator preparedStatementCreator, PreparedStatementCallback action) throws DataAccessException; @@ -696,6 +708,7 @@ public interface CqlOperations { * @return an arbitrary result object, as returned by the {@link ResultSetExtractor} * @throws DataAccessException if there is any problem */ + @Nullable T query(PreparedStatementCreator preparedStatementCreator, ResultSetExtractor resultSetExtractor) throws DataAccessException; @@ -729,14 +742,15 @@ public interface CqlOperations { * * @param preparedStatementCreator object that can create a {@link PreparedStatement} given a * {@link com.datastax.driver.core.Session}, must not be {@literal null}. - * @param preparedStatementBinder 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 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}. * @throws DataAccessException if there is any problem */ - T query(PreparedStatementCreator preparedStatementCreator, PreparedStatementBinder preparedStatementBinder, + @Nullable + T query(PreparedStatementCreator preparedStatementCreator, @Nullable PreparedStatementBinder psb, ResultSetExtractor resultSetExtractor) throws DataAccessException; /** @@ -745,13 +759,13 @@ public interface CqlOperations { * * @param preparedStatementCreator object that can create a {@link PreparedStatement} given a * {@link com.datastax.driver.core.Session}, must not be {@literal null}. - * @param preparedStatementBinder 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 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 rowCallbackHandler object that will extract results, one row at a time, must not be {@literal null}. * @throws DataAccessException if there is any problem executing the query. */ - void query(PreparedStatementCreator preparedStatementCreator, PreparedStatementBinder preparedStatementBinder, + void query(PreparedStatementCreator preparedStatementCreator, @Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException; /** @@ -760,14 +774,14 @@ public interface CqlOperations { * * @param preparedStatementCreator object that can create a {@link PreparedStatement} given a * {@link com.datastax.driver.core.Session}, must not be {@literal null}. - * @param preparedStatementBinder 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 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 rowMapper object that will map one object per row, must not be {@literal null}. * @return the result {@link List}, containing mapped objects. * @throws DataAccessException if there is any problem executing the query. */ - List query(PreparedStatementCreator preparedStatementCreator, PreparedStatementBinder preparedStatementBinder, + List query(PreparedStatementCreator preparedStatementCreator, @Nullable PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException; // ------------------------------------------------------------------------- @@ -788,8 +802,7 @@ public interface CqlOperations { * * @param hostMapper The implementation to use for host mapping. * @return Collection generated by the provided HostMapper. - * @throws DataAccessException + * @throws DataAccessException if there is any problem executing the query. */ Collection describeRing(HostMapper hostMapper) throws DataAccessException; - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlProvider.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlProvider.java index e6920173f..8d4d32886 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlProvider.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlProvider.java @@ -15,6 +15,8 @@ */ package org.springframework.data.cassandra.core.cql; +import org.springframework.lang.Nullable; + /** * Interface to be implemented by objects that can provide CQL strings. *

@@ -35,5 +37,6 @@ public interface CqlProvider { * * @return the CQL string, or {@literal null}. */ + @Nullable String getCql(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlStringUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlStringUtils.java index 55ab59594..87d69a22a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlStringUtils.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlStringUtils.java @@ -15,62 +15,68 @@ */ package org.springframework.data.cassandra.core.cql; +import org.springframework.lang.Nullable; + import com.datastax.driver.core.DataType; public class CqlStringUtils { - protected static final String DOUBLE_QUOTE = "\""; - protected static final String DOUBLE_DOUBLE_QUOTE = "\"\""; - protected static final String DOUBLE_SINGLE_QUOTE = "\'\'"; - protected static final String SINGLE_QUOTE = "\'"; - protected static final String EMPTY_STRING = ""; - protected static final String TYPE_PARAMETER_PREFIX = "<"; - protected static final String TYPE_PARAMETER_SUFFIX = ">"; - - public static StringBuilder noNull(StringBuilder builder) { - return (builder == null ? new StringBuilder() : builder); - } + private static final String DOUBLE_QUOTE = "\""; + private static final String DOUBLE_DOUBLE_QUOTE = "\"\""; + private static final String DOUBLE_SINGLE_QUOTE = "\'\'"; + private static final String SINGLE_QUOTE = "\'"; + private static final String EMPTY_STRING = ""; + private static final String TYPE_PARAMETER_PREFIX = "<"; + private static final String TYPE_PARAMETER_SUFFIX = ">"; /** * Renders the given string as a legal Cassandra string column or table option value, by escaping single quotes and - * encasing the result in single quotes. Given {@code null}, returns null. + * encasing the result in single quotes. Given {@literal null}, returns null. */ - public static String valuize(String candidate) { + @Nullable + public static String valuize(@Nullable String candidate) { return (candidate != null ? singleQuote(escapeSingle(candidate)) : null); } /** - * Doubles single quote characters (' -> ''). Given {@code null}, returns null. + * Doubles single quote characters (' -> ''). Given {@literal null}, returns null. */ - public static String escapeSingle(Object thing) { + @Nullable + public static String escapeSingle(@Nullable Object thing) { return (thing == null ? null : thing.toString().replace(SINGLE_QUOTE, DOUBLE_SINGLE_QUOTE)); } /** - * Doubles double quote characters (" -> ""). Given {@code null}, returns null. + * Doubles double quote characters (" -> ""). Given {@literal null}, returns null. */ - public static String escapeDouble(Object thing) { + @Nullable + public static String escapeDouble(@Nullable Object thing) { return (thing == null ? null : thing.toString().replace(DOUBLE_QUOTE, DOUBLE_DOUBLE_QUOTE)); } /** - * Surrounds given object's {@link Object#toString()} with single quotes. Given {@code null}, returns {@code null}. + * Surrounds given object's {@link Object#toString()} with single quotes. Given {@literal null}, returns + * {@literal null}. */ - public static String singleQuote(Object thing) { + @Nullable + public static String singleQuote(@Nullable Object thing) { return (thing == null ? null : SINGLE_QUOTE.concat(thing.toString()).concat(SINGLE_QUOTE)); } /** - * Surrounds given object's {@link Object#toString()} with double quotes. Given {@code null}, returns {@code null}. + * Surrounds given object's {@link Object#toString()} with double quotes. Given {@literal null}, returns + * {@literal null}. */ - public static String doubleQuote(Object thing) { + @Nullable + public static String doubleQuote(@Nullable Object thing) { return (thing == null ? null : DOUBLE_QUOTE.concat(thing.toString()).concat(DOUBLE_QUOTE)); } /** * Removed single quotes from quoted String option values */ - public static String removeSingleQuotes(Object thing) { + @Nullable + public static String removeSingleQuotes(@Nullable Object thing) { return (thing == null ? null : thing.toString().replaceAll(SINGLE_QUOTE, EMPTY_STRING)); } @@ -105,11 +111,13 @@ public class CqlStringUtils { return builder.append(TYPE_PARAMETER_SUFFIX).toString(); } - public static String unquote(String value) { + @Nullable + public static String unquote(@Nullable String value) { return unquote(value, "\""); } - public static String unquote(String value, String quoteChar) { + @Nullable + public static String unquote(@Nullable String value, String quoteChar) { if (value == null) { return null; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlTemplate.java index 5c2822727..979992f62 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CqlTemplate.java @@ -24,6 +24,7 @@ import java.util.function.Function; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.DataAccessUtils; import org.springframework.data.cassandra.SessionFactory; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.driver.core.BoundStatement; @@ -154,6 +155,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * @see org.springframework.data.cassandra.core.cqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.ResultSetExtractor) */ @Override + @Nullable public T query(String cql, ResultSetExtractor resultSetExtractor) throws DataAccessException { Assert.hasText(cql, "CQL must not be empty"); @@ -189,6 +191,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public List query(String cql, RowMapper rowMapper) throws DataAccessException { + // noinspection ConstantConditions return query(cql, newResultSetExtractor(rowMapper)); } @@ -198,6 +201,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public List> queryForList(String cql) throws DataAccessException { + // noinspection ConstantConditions return query(cql, newResultSetExtractor(newColumnMapRowMapper())); } @@ -207,6 +211,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public List queryForList(String cql, Class elementType) throws DataAccessException { + // noinspection ConstantConditions return query(cql, newResultSetExtractor(newSingleColumnRowMapper(elementType))); } @@ -243,6 +248,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public ResultSet queryForResultSet(String cql) throws DataAccessException { + // noinspection ConstantConditions return query(cql, rs -> rs); } @@ -307,6 +313,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public List query(Statement statement, RowMapper rowMapper) throws DataAccessException { + // noinspection ConstantConditions return query(statement, newResultSetExtractor(rowMapper)); } @@ -316,6 +323,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public List> queryForList(Statement statement) throws DataAccessException { + // noinspection ConstantConditions return query(statement, newResultSetExtractor(newColumnMapRowMapper())); } @@ -325,6 +333,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public List queryForList(Statement statement, Class elementType) throws DataAccessException { + // noinspection ConstantConditions return query(statement, newResultSetExtractor(newSingleColumnRowMapper(elementType))); } @@ -334,6 +343,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public Map queryForMap(Statement statement) throws DataAccessException { + // noinspection ConstantConditions return queryForObject(statement, newColumnMapRowMapper()); } @@ -361,6 +371,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public ResultSet queryForResultSet(Statement statement) throws DataAccessException { + // noinspection ConstantConditions return query(statement, rs -> rs); } @@ -391,14 +402,16 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * @see org.springframework.data.cassandra.core.cqlOperations#execute(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementBinder) */ @Override - public boolean execute(String cql, PreparedStatementBinder preparedStatementBinder) throws DataAccessException { - return query(new SimplePreparedStatementCreator(cql), preparedStatementBinder, ResultSet::wasApplied); + public boolean execute(String cql, @Nullable PreparedStatementBinder psb) throws DataAccessException { + // noinspection ConstantConditions + return query(new SimplePreparedStatementCreator(cql), psb, ResultSet::wasApplied); } /* * (non-Javadoc) * @see org.springframework.data.cassandra.core.cqlOperations#execute(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementCallback) */ + @Nullable @Override public T execute(String cql, PreparedStatementCallback action) throws DataAccessException { return execute(newPreparedStatementCreator(cql), action); @@ -410,6 +423,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public boolean execute(PreparedStatementCreator preparedStatementCreator) throws DataAccessException { + // noinspection ConstantConditions return query(preparedStatementCreator, ResultSet::wasApplied); } @@ -418,6 +432,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * @see org.springframework.data.cassandra.core.cqlOperations#execute(org.springframework.data.cassandra.core.cql.PreparedStatementCreator, org.springframework.data.cassandra.core.cql.PreparedStatementCallback) */ @Override + @Nullable public T execute(PreparedStatementCreator preparedStatementCreator, PreparedStatementCallback action) throws DataAccessException { @@ -468,7 +483,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { @Override public List query(PreparedStatementCreator preparedStatementCreator, RowMapper rowMapper) throws DataAccessException { - + // noinspection ConstantConditions return query(preparedStatementCreator, null, newResultSetExtractor(rowMapper)); } @@ -476,8 +491,9 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * (non-Javadoc) * @see org.springframework.data.cassandra.core.cqlOperations#query(org.springframework.data.cassandra.core.cql.PreparedStatementCreator, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.ResultSetExtractor) */ + @Nullable @Override - public T query(PreparedStatementCreator preparedStatementCreator, PreparedStatementBinder preparedStatementBinder, + public T query(PreparedStatementCreator preparedStatementCreator, @Nullable PreparedStatementBinder psb, ResultSetExtractor resultSetExtractor) throws DataAccessException { Assert.notNull(preparedStatementCreator, "PreparedStatementCreator must not be null"); @@ -496,8 +512,8 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { logger.debug("Executing prepared statement [{}]", preparedStatement); } - BoundStatement boundStatement = applyStatementSettings(preparedStatementBinder != null - ? preparedStatementBinder.bindValues(preparedStatement) : preparedStatement.bind()); + BoundStatement boundStatement = applyStatementSettings( + psb != null ? psb.bindValues(preparedStatement) : preparedStatement.bind()); ResultSet results = session.execute(boundStatement); @@ -513,10 +529,10 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * @see org.springframework.data.cassandra.core.cqlOperations#query(org.springframework.data.cassandra.core.cql.PreparedStatementCreator, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.RowCallbackHandler) */ @Override - public void query(PreparedStatementCreator preparedStatementCreator, PreparedStatementBinder preparedStatementBinder, + public void query(PreparedStatementCreator preparedStatementCreator, @Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException { - query(preparedStatementCreator, preparedStatementBinder, newResultSetExtractor(rowCallbackHandler)); + query(preparedStatementCreator, psb, newResultSetExtractor(rowCallbackHandler)); } /* @@ -525,9 +541,9 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public List query(PreparedStatementCreator preparedStatementCreator, - PreparedStatementBinder preparedStatementBinder, RowMapper rowMapper) throws DataAccessException { - - return query(preparedStatementCreator, preparedStatementBinder, newResultSetExtractor(rowMapper)); + @Nullable PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException { + // noinspection ConstantConditions + return query(preparedStatementCreator, psb, newResultSetExtractor(rowMapper)); } /* @@ -556,6 +572,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public List query(String cql, RowMapper rowMapper, Object... args) throws DataAccessException { + // noinspection ConstantConditions return query(newPreparedStatementCreator(cql), newPreparedStatementBinder(args), newResultSetExtractor(rowMapper)); } @@ -564,10 +581,10 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * @see org.springframework.data.cassandra.core.cqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.ResultSetExtractor) */ @Override - public T query(String cql, PreparedStatementBinder preparedStatementBinder, + public T query(String cql, @Nullable PreparedStatementBinder psb, ResultSetExtractor resultSetExtractor) throws DataAccessException { - return query(newPreparedStatementCreator(cql), preparedStatementBinder, resultSetExtractor); + return query(newPreparedStatementCreator(cql), psb, resultSetExtractor); } /* @@ -575,10 +592,9 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * @see org.springframework.data.cassandra.core.cqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.RowCallbackHandler) */ @Override - public void query(String cql, PreparedStatementBinder preparedStatementBinder, RowCallbackHandler rowCallbackHandler) + public void query(String cql, @Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException { - - query(newPreparedStatementCreator(cql), preparedStatementBinder, newResultSetExtractor(rowCallbackHandler)); + query(newPreparedStatementCreator(cql), psb, newResultSetExtractor(rowCallbackHandler)); } /* @@ -586,10 +602,10 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * @see org.springframework.data.cassandra.core.cqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.RowMapper) */ @Override - public List query(String cql, PreparedStatementBinder preparedStatementBinder, RowMapper rowMapper) + public List query(String cql, @Nullable PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException { - - return query(newPreparedStatementCreator(cql), preparedStatementBinder, newResultSetExtractor(rowMapper)); + // noinspection ConstantConditions + return query(newPreparedStatementCreator(cql), psb, newResultSetExtractor(rowMapper)); } /* @@ -598,6 +614,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public List> queryForList(String cql, Object... args) throws DataAccessException { + // noinspection ConstantConditions return query(newPreparedStatementCreator(cql), newPreparedStatementBinder(args), newResultSetExtractor(newColumnMapRowMapper())); } @@ -608,6 +625,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public List queryForList(String cql, Class elementType, Object... args) throws DataAccessException { + // noinspection ConstantConditions return query(newPreparedStatementCreator(cql), newPreparedStatementBinder(args), newResultSetExtractor(newSingleColumnRowMapper(elementType))); } @@ -646,6 +664,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { */ @Override public ResultSet queryForResultSet(String cql, Object... args) throws DataAccessException { + // noinspection ConstantConditions return query(newPreparedStatementCreator(cql), newPreparedStatementBinder(args), rs -> rs); } @@ -697,18 +716,22 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations { * Translate the given {@link DriverException} 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 {@code null}) + * @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 */ - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - protected DataAccessException translateException(String task, String cql, DriverException driverException) { + protected DataAccessException translateException(String task, @Nullable String cql, DriverException driverException) { return translate(task, cql, driverException); } private Session getCurrentSession() { - return getSessionFactory().getSession(); + + SessionFactory sessionFactory = getSessionFactory(); + + Assert.state(sessionFactory != null, "SessionFactory is null"); + + return sessionFactory.getSession(); } private class SimplePreparedStatementCreator implements PreparedStatementCreator, CqlProvider { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ExceptionTranslatingListenableFutureAdapter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ExceptionTranslatingListenableFutureAdapter.java index 5caf208d9..cd4875aaa 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ExceptionTranslatingListenableFutureAdapter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ExceptionTranslatingListenableFutureAdapter.java @@ -21,6 +21,7 @@ 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; @@ -38,6 +39,7 @@ import org.springframework.util.concurrent.SuccessCallback; class ExceptionTranslatingListenableFutureAdapter implements ListenableFuture { private final ListenableFuture adaptee; + private final ListenableFuture future; /** @@ -47,7 +49,7 @@ class ExceptionTranslatingListenableFutureAdapter implements ListenableFuture * @param adaptee must not be {@literal null}. * @param persistenceExceptionTranslator must not be {@literal null}. */ - public ExceptionTranslatingListenableFutureAdapter(ListenableFuture adaptee, + ExceptionTranslatingListenableFutureAdapter(ListenableFuture adaptee, PersistenceExceptionTranslator persistenceExceptionTranslator) { Assert.notNull(adaptee, "ListenableFuture must not be null"); @@ -65,7 +67,7 @@ class ExceptionTranslatingListenableFutureAdapter implements ListenableFuture listenableFuture.addCallback(new ListenableFutureCallback() { @Override - public void onSuccess(T result) { + public void onSuccess(@Nullable T result) { settableFuture.set(result); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/GuavaListenableFutureAdapter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/GuavaListenableFutureAdapter.java index 8b09edbcc..b4d838b59 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/GuavaListenableFutureAdapter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/GuavaListenableFutureAdapter.java @@ -21,6 +21,7 @@ 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; @@ -41,6 +42,7 @@ import com.google.common.util.concurrent.Futures; public class GuavaListenableFutureAdapter implements ListenableFuture { private final com.google.common.util.concurrent.ListenableFuture adaptee; + private final ListenableFuture future; /** @@ -68,7 +70,7 @@ public class GuavaListenableFutureAdapter implements ListenableFuture { Futures.addCallback(guavaFuture, new FutureCallback() { @Override - public void onSuccess(T result) { + public void onSuccess(@Nullable T result) { settableFuture.set(result); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/KeyspaceIdentifier.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/KeyspaceIdentifier.java index 2f0391f15..066097ad1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/KeyspaceIdentifier.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/KeyspaceIdentifier.java @@ -25,9 +25,7 @@ import org.springframework.util.Assert; * Keyspace identifiers are converted to lower case. To render, use any of the methods {@link #toCql()}, * {@link #toCql(StringBuilder)}, or {@link #toString()}. * - * @see #KeyspaceIdentifier(String) * @see #toCql() - * @see #toCql(StringBuilder) * @see #toString() * @author Matthew T. Adams */ @@ -36,6 +34,25 @@ public final class KeyspaceIdentifier implements Comparable public static final String REGEX = "(?i)[a-z][\\w]{0,47}"; public static final Pattern PATTERN = Pattern.compile(REGEX); + private final String identifier; + + /** + * Create a new {@link KeyspaceIdentifier}. + */ + private KeyspaceIdentifier(CharSequence identifier) { + + Assert.notNull(identifier, "Identifier must not be null"); + + String string = identifier.toString(); + Assert.hasText(string, "Identifier must not be empty"); + + if (!isIdentifier(string)) { + throw new IllegalArgumentException( + String.format("given string [%s] is not a valid keyspace identifier", identifier)); + } + this.identifier = string.toLowerCase(); + } + /** * Factory method for {@link KeyspaceIdentifier}. Convenient if imported statically. */ @@ -50,32 +67,6 @@ public final class KeyspaceIdentifier implements Comparable return PATTERN.matcher(chars).matches() && !ReservedKeyword.isReserved(chars); } - private String identifier; - - /** - * Create a new {@link KeyspaceIdentifier}. - */ - public KeyspaceIdentifier(CharSequence identifier) { - setIdentifier(identifier); - } - - /** - * Tests & sets the given identifier. - */ - private void setIdentifier(CharSequence identifier) { - - Assert.notNull(identifier, "Identifier must not be null"); - - String string = identifier.toString(); - Assert.hasText(string, "Identifier must not be empty"); - - if (!isIdentifier(string)) { - throw new IllegalArgumentException( - String.format("given string [%s] is not a valid keyspace identifier", identifier)); - } - this.identifier = string.toLowerCase(); - } - /** * Renders this identifier appropriately. */ @@ -85,10 +76,10 @@ public final class KeyspaceIdentifier implements Comparable /** * Appends the rendering of this identifier to the given {@link StringBuilder}, then returns that - * {@link StringBuilder}. If {@code null} is given, a new {@link StringBuilder} is created, appended to, and returned. + * {@link StringBuilder}. If {@literal null} is given, a new {@link StringBuilder} is created, appended to, and + * returned. */ public StringBuilder toCql(StringBuilder sb) { - sb = sb == null ? new StringBuilder() : sb; return sb.append(toCql()); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/PreparedStatementCallback.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/PreparedStatementCallback.java index 6bc733c7d..31cc54c00 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/PreparedStatementCallback.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/PreparedStatementCallback.java @@ -16,6 +16,7 @@ package org.springframework.data.cassandra.core.cql; import org.springframework.dao.DataAccessException; +import org.springframework.lang.Nullable; import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.Session; @@ -36,7 +37,7 @@ import com.datastax.driver.core.exceptions.DriverException; * @author Mark Paluch * @see AsyncCqlTemplate#execute(String, PreparedStatementCallback) * @see AsyncCqlTemplate#execute(AsyncPreparedStatementCreator, PreparedStatementCallback) - * @see CqlTemplate#execute(String, PreparedStatementCallback) + * @see CqlTemplate#execute(String, PreparedStatementCallback)} * @see CqlTemplate#execute(PreparedStatementCreator, PreparedStatementCallback) */ @FunctionalInterface @@ -58,10 +59,11 @@ public interface PreparedStatementCallback { * @throws DriverException if thrown by a session method, to be auto-converted to a {@link DataAccessException}. * @throws DataAccessException in case of custom exceptions. * @see AsyncCqlTemplate#queryForObject(String, Class, Object...) - * @see AsyncCqlTemplate#queryForList(String, Class, Object...) + * @see AsyncCqlOperations#queryForList(String, Class, Object...) * @see CqlTemplate#queryForObject(String, Class, Object...) * @see CqlTemplate#queryForList(String, Object...) */ + @Nullable T doInPreparedStatement(Session session, PreparedStatement preparedStatement) throws DriverException, DataAccessException; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptions.java index 9d73f0f67..dad353e92 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptions.java @@ -15,9 +15,10 @@ */ package org.springframework.data.cassandra.core.cql; -import java.util.Optional; +import java.time.Duration; import java.util.concurrent.TimeUnit; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.driver.core.ConsistencyLevel; @@ -33,30 +34,53 @@ import com.datastax.driver.core.policies.RetryPolicy; */ public class QueryOptions { - private ConsistencyLevel consistencyLevel; + private static final QueryOptions EMPTY = QueryOptions.builder().build(); - private RetryPolicy retryPolicy; + private final @Nullable ConsistencyLevel consistencyLevel; - private Boolean tracing; + private final @Nullable RetryPolicy retryPolicy; - private Integer fetchSize; + private final @Nullable Boolean tracing; - private Long readTimeout; + private final @Nullable Integer fetchSize; - /** - * Creates new {@link QueryOptions}. - */ - public QueryOptions() {} + private final Duration readTimeout; + + protected QueryOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, + @Nullable Boolean tracing, @Nullable Integer fetchSize, Duration readTimeout) { + + this.consistencyLevel = consistencyLevel; + this.retryPolicy = retryPolicy; + this.tracing = tracing; + this.fetchSize = fetchSize; + this.readTimeout = readTimeout; + } /** * Creates new {@link QueryOptions} for the given {@link ConsistencyLevel} and {@link RetryPolicy}. * * @param consistencyLevel the consistency level, may be {@literal null}. * @param retryPolicy the retry policy, may be {@literal null}. + * @deprecated since 2.0, use {@link #builder()}. */ - public QueryOptions(ConsistencyLevel consistencyLevel, RetryPolicy retryPolicy) { - setConsistencyLevel(consistencyLevel); - setRetryPolicy(retryPolicy); + @Deprecated + public QueryOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy) { + + this.consistencyLevel = consistencyLevel; + this.retryPolicy = retryPolicy; + this.tracing = false; + this.fetchSize = null; + this.readTimeout = Duration.ofMillis(-1); + } + + /** + * Create default {@link QueryOptions}. + * + * @return default {@link QueryOptions}. + * @since 2.0 + */ + public static QueryOptions empty() { + return EMPTY; } /** @@ -69,111 +93,45 @@ public class QueryOptions { return new QueryOptionsBuilder(); } - /** - * Sets the driver {@link ConsistencyLevel}. Setting both ({@link ConsistencyLevel} and {@link ConsistencyLevel driver - * ConsistencyLevel}) consistency levels is not supported. - * - * @param consistencyLevel the driver {@link ConsistencyLevel} to set. - * @since 1.5 - */ - public void setConsistencyLevel(ConsistencyLevel consistencyLevel) { - this.consistencyLevel = consistencyLevel; - } - /** * @return the the driver {@link ConsistencyLevel} * @since 1.5 */ + @Nullable protected ConsistencyLevel getConsistencyLevel() { return this.consistencyLevel; } - /** - * Sets the {@link RetryPolicy}. Setting both ({@link RetryPolicy} and {@link RetryPolicy driver RetryPolicy}) retry - * policies is not supported. - * - * @param retryPolicy the driver {@link RetryPolicy} to set. - * @since 1.5 - * @throws IllegalStateException if the {@link RetryPolicy} is set - */ - public void setRetryPolicy(RetryPolicy retryPolicy) { - this.retryPolicy = retryPolicy; - } - /** * @return the driver {@link RetryPolicy} * @since 1.5 */ + @Nullable protected RetryPolicy getRetryPolicy() { return this.retryPolicy; } - /** - * Sets the query fetch size for {@link com.datastax.driver.core.ResultSet} chunks. - *

- * The fetch size controls how much resulting rows will be retrieved simultaneously (the goal being to avoid loading - * too much results in memory for queries yielding large results). Please note that while value as low as 1 can be - * used, it is *highly* discouraged to use such a low value in practice as it will yield very poor performance. - * - * @param fetchSize the number of rows to fetch per chunking request. To disable chunking of the result set, use - * {@code fetchSize == Integer.MAX_VALUE}. Negative values are not allowed. - * @since 1.5 - * @see com.datastax.driver.core.QueryOptions#getFetchSize() - * @see com.datastax.driver.core.Cluster.Builder#withQueryOptions(com.datastax.driver.core.QueryOptions) - */ - public void setFetchSize(int fetchSize) { - - Assert.isTrue(fetchSize >= 0, "FetchSize must be greater than equal to zero"); - - this.fetchSize = fetchSize; - } - /** * @return the number of rows to fetch per chunking request. May be {@literal null} if not set. * @since 1.5 */ + @Nullable protected Integer getFetchSize() { return fetchSize; } - /** - * Sets the read timeout in milliseconds. Overrides the default per-host read timeout ( - * {@link SocketOptions#getReadTimeoutMillis()}). - * - * @param readTimeout the read timeout in milliseconds. Negative values are not allowed. If it is {@code 0}, the read - * timeout will be disabled for this statement. - * @since 1.5 - * @see SocketOptions#getReadTimeoutMillis() - * @see com.datastax.driver.core.Cluster.Builder#withSocketOptions(SocketOptions) - */ - public void setReadTimeout(long readTimeout) { - - Assert.isTrue(readTimeout >= 0, "ReadTimeout must be greater than equal to zero"); - - this.readTimeout = readTimeout; - } - /** * @return the read timeout in milliseconds. May be {@literal null} if not set. * @since 1.5 */ - protected Long getReadTimeout() { + protected Duration getReadTimeout() { return this.readTimeout; } - /** - * Enables statement tracing. - * - * @param tracing {@literal true} to enable statement tracing to the executed statements. - * @since 1.5 - */ - public void setTracing(boolean tracing) { - this.tracing = tracing; - } - /** * @return whether to enable tracing. May be {@literal null} if not set. */ + @Nullable protected Boolean getTracing() { return this.tracing; } @@ -186,15 +144,15 @@ public class QueryOptions { */ public static class QueryOptionsBuilder { - private ConsistencyLevel consistencyLevel; + protected @Nullable ConsistencyLevel consistencyLevel; - private RetryPolicy retryPolicy; + protected @Nullable RetryPolicy retryPolicy; - private Boolean tracing; + protected @Nullable Boolean tracing; - private Integer fetchSize; + protected @Nullable Integer fetchSize; - private Long readTimeout; + protected Duration readTimeout = Duration.ofMillis(-1); QueryOptionsBuilder() {} @@ -261,12 +219,7 @@ public class QueryOptions { * @see com.datastax.driver.core.Cluster.Builder#withSocketOptions(SocketOptions) */ public QueryOptionsBuilder readTimeout(long readTimeout) { - - Assert.isTrue(readTimeout >= 0, "ReadTimeout must be greater than equal to zero"); - - this.readTimeout = readTimeout; - - return this; + return readTimeout(Duration.ofMillis(readTimeout)); } /** @@ -278,13 +231,33 @@ public class QueryOptions { * @return {@code this} {@link QueryOptionsBuilder} * @see SocketOptions#getReadTimeoutMillis() * @see com.datastax.driver.core.Cluster.Builder#withSocketOptions(SocketOptions) + * @deprecated since 2.0, use {@link #readTimeout(Duration)}. */ + @Deprecated public QueryOptionsBuilder readTimeout(long readTimeout, TimeUnit timeUnit) { Assert.isTrue(readTimeout >= 0, "ReadTimeout must be greater than equal to zero"); Assert.notNull(timeUnit, "TimeUnit must not be null"); - this.readTimeout = timeUnit.toMillis(readTimeout); + return readTimeout(Duration.ofMillis(timeUnit.toMillis(readTimeout))); + } + + /** + * Sets the read timeout. Overrides the default per-host read timeout. + * + * @param readTimeout the read timeout. Negative values are not allowed. If it is {@code 0}, the read timeout will + * be disabled for this statement. + * @return {@code this} {@link QueryOptionsBuilder} + * @see SocketOptions#getReadTimeoutMillis() + * @see com.datastax.driver.core.Cluster.Builder#withSocketOptions(SocketOptions) + * @since 2.0 + */ + public QueryOptionsBuilder readTimeout(Duration readTimeout) { + + Assert.isTrue(!readTimeout.isZero() && !readTimeout.isNegative(), + "ReadTimeout must be greater than equal to zero"); + + this.readTimeout = readTimeout; return this; } @@ -317,21 +290,7 @@ public class QueryOptions { * @return a new {@link QueryOptions} with the configured values */ public QueryOptions build() { - return applyOptions(new QueryOptions()); - } - - protected T applyOptions(T options) { - - QueryOptions queryOptions = (QueryOptions) options; - - queryOptions.setConsistencyLevel(consistencyLevel); - queryOptions.setRetryPolicy(retryPolicy); - - Optional.ofNullable(this.fetchSize).ifPresent(queryOptions::setFetchSize); - Optional.ofNullable(this.readTimeout).ifPresent(queryOptions::setReadTimeout); - Optional.ofNullable(this.tracing).ifPresent(queryOptions::setTracing); - - return options; + return new QueryOptions(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtil.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtil.java index ad5448fe5..2b41b8fcc 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtil.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtil.java @@ -42,13 +42,11 @@ public abstract class QueryOptionsUtil { Assert.notNull(preparedStatement, "PreparedStatement must not be null"); - if (queryOptions != null) { - if (queryOptions.getConsistencyLevel() != null) { - preparedStatement.setConsistencyLevel(queryOptions.getConsistencyLevel()); - } - if (queryOptions.getRetryPolicy() != null) { - preparedStatement.setRetryPolicy(queryOptions.getRetryPolicy()); - } + if (queryOptions.getConsistencyLevel() != null) { + preparedStatement.setConsistencyLevel(queryOptions.getConsistencyLevel()); + } + if (queryOptions.getRetryPolicy() != null) { + preparedStatement.setRetryPolicy(queryOptions.getRetryPolicy()); } return preparedStatement; @@ -65,29 +63,27 @@ public abstract class QueryOptionsUtil { Assert.notNull(statement, "Statement must not be null"); - if (queryOptions != null) { - if (queryOptions.getConsistencyLevel() != null) { - statement.setConsistencyLevel(queryOptions.getConsistencyLevel()); - } + if (queryOptions.getConsistencyLevel() != null) { + statement.setConsistencyLevel(queryOptions.getConsistencyLevel()); + } - if (queryOptions.getRetryPolicy() != null) { - statement.setRetryPolicy(queryOptions.getRetryPolicy()); - } + if (queryOptions.getRetryPolicy() != null) { + statement.setRetryPolicy(queryOptions.getRetryPolicy()); + } - if (queryOptions.getFetchSize() != null) { - statement.setFetchSize(queryOptions.getFetchSize()); - } + if (queryOptions.getFetchSize() != null) { + statement.setFetchSize(queryOptions.getFetchSize()); + } - if (queryOptions.getReadTimeout() != null) { - statement.setReadTimeoutMillis(queryOptions.getReadTimeout().intValue()); - } + if (!queryOptions.getReadTimeout().isNegative()) { + statement.setReadTimeoutMillis(Math.toIntExact(queryOptions.getReadTimeout().toMillis())); + } - if (queryOptions.getTracing() != null) { - if (queryOptions.getTracing()) { - statement.enableTracing(); - } else { - statement.disableTracing(); - } + if (queryOptions.getTracing() != null) { + if (queryOptions.getTracing()) { + statement.enableTracing(); + } else { + statement.disableTracing(); } } @@ -105,12 +101,10 @@ public abstract class QueryOptionsUtil { Assert.notNull(insert, "Insert must not be null"); - if (writeOptions != null) { - addQueryOptions(insert, writeOptions); + addQueryOptions(insert, writeOptions); - if (writeOptions.getTtl() != null) { - insert.using(QueryBuilder.ttl(writeOptions.getTtl())); - } + if (!writeOptions.getTtl().isNegative()) { + insert.using(QueryBuilder.ttl(Math.toIntExact(writeOptions.getTtl().getSeconds()))); } return insert; @@ -127,12 +121,10 @@ public abstract class QueryOptionsUtil { Assert.notNull(update, "Update must not be null"); - if (writeOptions != null) { - addQueryOptions(update, writeOptions); + addQueryOptions(update, writeOptions); - if (writeOptions.getTtl() != null) { - update.using(QueryBuilder.ttl(writeOptions.getTtl())); - } + if (!writeOptions.getTtl().isNegative()) { + update.using(QueryBuilder.ttl(Math.toIntExact(writeOptions.getTtl().getSeconds()))); } return update; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCassandraAccessor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCassandraAccessor.java index cc537e072..d122c1221 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCassandraAccessor.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCassandraAccessor.java @@ -21,6 +21,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessException; import org.springframework.data.cassandra.ReactiveSession; import org.springframework.data.cassandra.ReactiveSessionFactory; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.driver.core.exceptions.DriverException; @@ -44,7 +45,7 @@ public abstract class ReactiveCassandraAccessor implements InitializingBean { private CqlExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator(); - private ReactiveSessionFactory sessionFactory; + private @Nullable ReactiveSessionFactory sessionFactory; /** * Sets the {@link ReactiveSessionFactory} to use. @@ -63,6 +64,7 @@ public abstract class ReactiveCassandraAccessor implements InitializingBean { * * @return the configured {@link ReactiveSessionFactory}. */ + @Nullable public ReactiveSessionFactory getSessionFactory() { return sessionFactory; } @@ -97,9 +99,7 @@ public abstract class ReactiveCassandraAccessor implements InitializingBean { */ @Override public void afterPropertiesSet() { - - Assert.notNull(sessionFactory != null, "ReactiveSessionFactory must not be null"); - Assert.notNull(exceptionTranslator != null, "CassandraExceptionTranslator must not be null"); + Assert.notNull(sessionFactory, "ReactiveSessionFactory must not be null"); } /** @@ -117,6 +117,7 @@ public abstract class ReactiveCassandraAccessor implements InitializingBean { * exception hierarchy * @see DataAccessException */ + @Nullable protected DataAccessException translateExceptionIfPossible(DriverException ex) { Assert.notNull(ex, "DriverException must not be null"); @@ -133,7 +134,7 @@ public abstract class ReactiveCassandraAccessor implements InitializingBean { * 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 {@code null}) + * @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} * @see org.springframework.dao.DataAccessException#getRootCause() @@ -141,7 +142,7 @@ public abstract class ReactiveCassandraAccessor implements InitializingBean { * "http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#dao-exceptions">Consistent * exception hierarchy */ - protected DataAccessException translate(String task, String cql, DriverException ex) { + protected DataAccessException translate(String task, @Nullable String cql, DriverException ex) { Assert.notNull(ex, "DriverException must not be null"); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlOperations.java index 7809303a6..7034fe642 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlOperations.java @@ -24,6 +24,7 @@ import org.reactivestreams.Publisher; import org.springframework.dao.DataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.cassandra.ReactiveResultSet; +import org.springframework.lang.Nullable; import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.Row; @@ -47,10 +48,10 @@ public interface ReactiveCqlOperations { // ------------------------------------------------------------------------- /** - * Execute a CQL data access operation, implemented as callback action working on a {@link ReactiveSession}. 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. + * Execute a CQL data access operation, implemented as callback action working on a + * {@link org.springframework.data.cassandra.ReactiveSession}. 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. *

* The callback action can return a result object, for example a domain object or a collection of domain objects. * @@ -409,8 +410,8 @@ public interface ReactiveCqlOperations { *

* The callback action can return a result object, for example a domain object or a collection of domain objects. * - * @param psc object that can create a {@link PreparedStatement} given a {@link ReactiveSession}, must not be - * {@literal null}. + * @param psc object that can create a {@link PreparedStatement} given a + * {@link org.springframework.data.cassandra.ReactiveSession}, must not be {@literal null}. * @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 @@ -436,8 +437,8 @@ public interface ReactiveCqlOperations { /** * Query using a prepared statement, reading the {@link ReactiveResultSet} with a {@link ReactiveResultSetExtractor}. * - * @param psc object that can create a {@link PreparedStatement} given a {@link ReactiveSession}, must not be - * {@literal null}. + * @param psc object that can create a {@link PreparedStatement} given a + * {@link org.springframework.data.cassandra.ReactiveSession}, must not be {@literal null}. * @param rse object that will extract results, must not be {@literal null}. * @return an arbitrary result object, as returned by the {@link ReactiveResultSetExtractor} * @throws DataAccessException if there is any problem @@ -455,7 +456,7 @@ public interface ReactiveCqlOperations { * @return an arbitrary result object, as returned by the {@link ReactiveResultSetExtractor}. * @throws DataAccessException if there is any problem */ - Flux query(String cql, PreparedStatementBinder psb, ReactiveResultSetExtractor rse) + Flux query(String cql, @Nullable PreparedStatementBinder psb, ReactiveResultSetExtractor rse) throws DataAccessException; /** @@ -471,7 +472,7 @@ public interface ReactiveCqlOperations { * @return an arbitrary result object, as returned by the {@link ResultSetExtractor}. * @throws DataAccessException if there is any problem */ - Flux query(ReactivePreparedStatementCreator psc, PreparedStatementBinder psb, + Flux query(ReactivePreparedStatementCreator psc, @Nullable PreparedStatementBinder psb, ReactiveResultSetExtractor rse) throws DataAccessException; /** @@ -490,8 +491,8 @@ public interface ReactiveCqlOperations { /** * Query using a prepared statement, mapping each row to a Java object via a {@link RowMapper}. * - * @param psc object that can create a {@link PreparedStatement} given a {@link ReactiveSession}, must not be - * {@literal null}. + * @param psc object that can create a {@link PreparedStatement} given a + * {@link org.springframework.data.cassandra.ReactiveSession}, must not be {@literal null}. * @param rowMapper object that will map one object per row, must not be {@literal null}. * @return the result {@link Flux}, containing mapped objects. * @throws DataAccessException if there is any problem executing the query. @@ -510,7 +511,8 @@ public interface ReactiveCqlOperations { * @return the result {@link Flux}, containing mapped objects. * @throws DataAccessException if there is any problem executing the query. */ - Flux query(String cql, PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException; + Flux query(String cql, @Nullable PreparedStatementBinder psb, RowMapper rowMapper) + throws DataAccessException; /** * Query using a prepared statement and a {@link PreparedStatementBinder} implementation that knows how to bind values @@ -525,7 +527,7 @@ public interface ReactiveCqlOperations { * @return the result {@link Flux}, containing mapped objects. * @throws DataAccessException if there is any problem executing the query. */ - Flux query(ReactivePreparedStatementCreator psc, PreparedStatementBinder psb, RowMapper rowMapper) + Flux query(ReactivePreparedStatementCreator psc, @Nullable PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException; /** @@ -682,7 +684,7 @@ public interface ReactiveCqlOperations { * @return boolean value whether the statement was applied. * @throws DataAccessException if there is any problem issuing the execution. */ - Mono execute(String cql, PreparedStatementBinder psb) throws DataAccessException; + Mono execute(String cql, @Nullable PreparedStatementBinder psb) throws DataAccessException; /** * Issue a single CQL operation (such as an insert, update or delete statement) via a prepared statement, binding the diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlTemplate.java index 6dc9d405b..36f16711d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlTemplate.java @@ -29,6 +29,7 @@ import org.springframework.data.cassandra.ReactiveResultSet; import org.springframework.data.cassandra.ReactiveSession; import org.springframework.data.cassandra.ReactiveSessionFactory; import org.springframework.data.cassandra.core.cql.session.DefaultReactiveSessionFactory; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.driver.core.BoundStatement; @@ -95,13 +96,13 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * 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 RetryPolicy retryPolicy; + private @Nullable RetryPolicy retryPolicy; /** * 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 com.datastax.driver.core.ConsistencyLevel consistencyLevel; + private @Nullable com.datastax.driver.core.ConsistencyLevel consistencyLevel; /** * Construct a new {@link ReactiveCqlTemplate Note: The {@link ReactiveSessionFactory} has to be set before using the @@ -132,6 +133,7 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * be {@literal null}. */ public ReactiveCqlTemplate(ReactiveSessionFactory reactiveSessionFactory) { + setSessionFactory(reactiveSessionFactory); afterPropertiesSet(); } @@ -145,13 +147,14 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * @see Statement#setConsistencyLevel(ConsistencyLevel) * @see RetryPolicy */ - public void setConsistencyLevel(ConsistencyLevel consistencyLevel) { + public void setConsistencyLevel(@Nullable ConsistencyLevel consistencyLevel) { this.consistencyLevel = consistencyLevel; } /** * @return the {@link ConsistencyLevel} specified for this {@link ReactiveCqlTemplate}. */ + @Nullable public ConsistencyLevel getConsistencyLevel() { return consistencyLevel; } @@ -183,13 +186,14 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * @see Statement#setRetryPolicy(RetryPolicy) * @see RetryPolicy */ - public void setRetryPolicy(RetryPolicy retryPolicy) { + public void setRetryPolicy(@Nullable RetryPolicy retryPolicy) { this.retryPolicy = retryPolicy; } /** * @return the {@link RetryPolicy} specified for this {@link ReactiveCqlTemplate}. */ + @Nullable public RetryPolicy getRetryPolicy() { return retryPolicy; } @@ -479,8 +483,9 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * @return an arbitrary result object, as returned by the {@link ReactiveResultSetExtractor} * @throws DataAccessException if there is any problem */ - public Flux query(ReactivePreparedStatementCreator psc, PreparedStatementBinder preparedStatementBinder, - ReactiveResultSetExtractor rse) throws DataAccessException { + public Flux query(ReactivePreparedStatementCreator psc, + @Nullable PreparedStatementBinder preparedStatementBinder, ReactiveResultSetExtractor rse) + throws DataAccessException { Assert.notNull(psc, "ReactivePreparedStatementCreator must not be null"); Assert.notNull(rse, "ReactiveResultSetExtractor object must not be null"); @@ -506,7 +511,6 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re @Override public Flux query(ReactivePreparedStatementCreator psc, ReactiveResultSetExtractor rse) throws DataAccessException { - return query(psc, null, rse); } @@ -514,9 +518,8 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * @see org.springframework.data.cassandra.core.cql.ReactiveCqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.ReactiveResultSetExtractor) */ @Override - public Flux query(String cql, PreparedStatementBinder psb, ReactiveResultSetExtractor rse) + public Flux query(String cql, @Nullable PreparedStatementBinder psb, ReactiveResultSetExtractor rse) throws DataAccessException { - return query(new SimpleReactivePreparedStatementCreator(cql), psb, rse); } @@ -540,7 +543,8 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * @see org.springframework.data.cassandra.core.cql.ReactiveCqlOperations#query(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.RowMapper) */ @Override - public Flux query(String cql, PreparedStatementBinder psb, RowMapper rowMapper) throws DataAccessException { + public Flux query(String cql, @Nullable PreparedStatementBinder psb, RowMapper rowMapper) + throws DataAccessException { return query(cql, psb, new ReactiveRowMapperResultSetExtractor<>(rowMapper)); } @@ -548,8 +552,8 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * @see org.springframework.data.cassandra.core.cql.ReactiveCqlOperations#query(org.springframework.data.cassandra.core.cql.ReactivePreparedStatementCreator, org.springframework.data.cassandra.core.cql.PreparedStatementBinder, org.springframework.data.cassandra.core.cql.RowMapper) */ @Override - public Flux query(ReactivePreparedStatementCreator psc, PreparedStatementBinder psb, RowMapper rowMapper) - throws DataAccessException { + public Flux query(ReactivePreparedStatementCreator psc, @Nullable PreparedStatementBinder psb, + RowMapper rowMapper) throws DataAccessException { return query(psc, psb, new ReactiveRowMapperResultSetExtractor<>(rowMapper)); } @@ -636,7 +640,7 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * @see org.springframework.data.cassandra.core.cql.ReactiveCqlOperations#execute(java.lang.String, org.springframework.data.cassandra.core.cql.PreparedStatementBinder) */ @Override - public Mono execute(String cql, PreparedStatementBinder psb) throws DataAccessException { + public Mono execute(String cql, @Nullable PreparedStatementBinder psb) throws DataAccessException { return query(new SimpleReactivePreparedStatementCreator(cql), psb, resultSet -> Mono.just(resultSet.wasApplied())) .next(); } @@ -699,7 +703,7 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * Create a reusable {@link Mono} given a {@link ReactiveStatementCallback} without exception translation. * * @param callback must not be {@literal null}. - * @return a reusable {@link Mono} wrapping the {@link ReactiveStatementCallback }. + * @return a reusable {@link Mono} wrapping the {@link ReactiveStatementCallback}. */ protected Mono createMono(Statement statement, ReactiveStatementCallback callback) { @@ -731,11 +735,11 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * Exception translation {@link Function} intended for {@link Mono#otherwise(Function)} usage. * * @param task readable text describing the task being attempted - * @param cql CQL query or update that caused the problem (may be {@code null}) + * @param cql CQL query or update that caused the problem (may be {@literal null}) * @return the exception translation {@link Function} * @see CqlProvider */ - protected Function translateException(String task, String cql) { + protected Function translateException(String task, @Nullable String cql) { return throwable -> throwable instanceof DriverException ? translate(task, cql, (DriverException) throwable) : throwable; } @@ -828,17 +832,23 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re } private ReactiveSession getSession() { - return getSessionFactory().getSession(); + + ReactiveSessionFactory sessionFactory = getSessionFactory(); + + Assert.state(sessionFactory != null, "SessionFactory is null"); + + return sessionFactory.getSession(); } /** * Determine CQL from potential provider object. * * @param cqlProvider object that's potentially a {@link CqlProvider} - * @return the CQL string, or {@code null} + * @return the CQL string, or {@literal null} * @see CqlProvider */ - private static String getCql(Object cqlProvider) { + @Nullable + private static String getCql(@Nullable Object cqlProvider) { return Optional.ofNullable(cqlProvider) // .filter(o -> o instanceof CqlProvider) // @@ -847,7 +857,7 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re .orElse(null); } - private class SimpleReactivePreparedStatementCreator implements ReactivePreparedStatementCreator, CqlProvider { + class SimpleReactivePreparedStatementCreator implements ReactivePreparedStatementCreator, CqlProvider { private final String cql; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ResultSetExtractor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ResultSetExtractor.java index 4db93212d..03f4a0bd8 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ResultSetExtractor.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ResultSetExtractor.java @@ -16,6 +16,7 @@ package org.springframework.data.cassandra.core.cql; import org.springframework.dao.DataAccessException; +import org.springframework.lang.Nullable; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.exceptions.DriverException; @@ -52,5 +53,6 @@ public interface ResultSetExtractor { * there's no need to catch {@link DriverException}) * @throws DataAccessException in case of custom exceptions */ + @Nullable T extractData(ResultSet resultSet) throws DriverException, DataAccessException; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RingMemberHostMapper.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RingMemberHostMapper.java index f422bd4d8..b74a64441 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RingMemberHostMapper.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RingMemberHostMapper.java @@ -29,7 +29,6 @@ import com.datastax.driver.core.exceptions.DriverException; * * @author David Webb * @author Mark Paluch - * @param */ public enum RingMemberHostMapper implements HostMapper { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowCallbackHandler.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowCallbackHandler.java index bfe17c006..ce2e988d0 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowCallbackHandler.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowCallbackHandler.java @@ -39,15 +39,15 @@ import com.datastax.driver.core.exceptions.DriverException; public interface RowCallbackHandler { /** - * Implementations must implement this method to process each row of data in the {@link ResultSet}. This method is - * only supposed to extract values of the current row. + * Implementations must implement this method to process each row of data in the + * {@link com.datastax.driver.core.ResultSet}. This method is only supposed to extract values of the current row. *

* Exactly what the implementation chooses to do is up to it: A trivial implementation might simply count rows, while * another implementation might build an XML document. * * @param row the {@link Row} to process (pre-initialized for the current row). * @throws DriverException if a {@link DriverException} is encountered getting column values (that is, there's no need - * to catch {@link DriverException}) + * to catch {@link DriverException}). */ void processRow(Row row) throws DriverException; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowMapper.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowMapper.java index 61ff83c9f..c9e99724c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowMapper.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowMapper.java @@ -15,6 +15,8 @@ */ package org.springframework.data.cassandra.core.cql; +import org.springframework.lang.Nullable; + import com.datastax.driver.core.Row; import com.datastax.driver.core.exceptions.DriverException; @@ -46,5 +48,6 @@ public interface RowMapper { * @throws DriverException if a {@link DriverException} is encountered getting column values (that is, there's no need * to catch {@link DriverException}) */ + @Nullable T mapRow(Row row, int rowNum) throws DriverException; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowUtils.java index 1d82aa742..c5facca1c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowUtils.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/RowUtils.java @@ -19,6 +19,8 @@ import java.math.BigDecimal; import java.nio.ByteBuffer; import java.util.UUID; +import org.springframework.lang.Nullable; + import com.datastax.driver.core.LocalDate; import com.datastax.driver.core.Row; import com.datastax.driver.core.TupleValue; @@ -44,10 +46,11 @@ public abstract class RowUtils { * * @param row is the {@link Row} holding the data * @param index is the column index - * @param requiredType the required value type (may be {@code null}) + * @param requiredType the required value type (may be {@literal null}) * @return the value object */ - public static Object getRowValue(Row row, int index, Class requiredType) { + @Nullable + public static Object getRowValue(Row row, int index, @Nullable Class requiredType) { if (requiredType == null) { return row.getObject(index); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SessionCallback.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SessionCallback.java index 03540172c..e8ec8023c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SessionCallback.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SessionCallback.java @@ -47,7 +47,7 @@ public interface SessionCallback { * template. * * @param session active Cassandra Session, must not be {@literal null}. - * @return a result object, or {@code null} if none. + * @return a result object, or {@literal null} if none. * @throws DriverException if thrown by a Session method, to be auto-converted to a {@link DataAccessException}. * @throws DataAccessException in case of custom exceptions. * @see CqlTemplate#queryForObject(String, Class) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SimplePreparedStatementCreator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SimplePreparedStatementCreator.java index 0fe334f00..24859d108 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SimplePreparedStatementCreator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SimplePreparedStatementCreator.java @@ -44,6 +44,7 @@ public class SimplePreparedStatementCreator implements PreparedStatementCreator, public SimplePreparedStatementCreator(String cql) { Assert.notNull(cql, "CQL is required to create a PreparedStatement"); + this.cql = cql; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SingleColumnRowMapper.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SingleColumnRowMapper.java index adde84b1d..9e07b7a67 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SingleColumnRowMapper.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/SingleColumnRowMapper.java @@ -16,6 +16,7 @@ package org.springframework.data.cassandra.core.cql; import org.springframework.dao.TypeMismatchDataAccessException; +import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.NumberUtils; @@ -41,7 +42,7 @@ import com.datastax.driver.core.exceptions.DriverException; */ public class SingleColumnRowMapper implements RowMapper { - private Class requiredType; + private @Nullable Class requiredType; /** * Create a new {@link SingleColumnRowMapper} for bean-style configuration. @@ -119,13 +120,14 @@ public class SingleColumnRowMapper implements RowMapper { * * @param row is the {@link Row} holding the data, must not be {@literal null}. * @param index is the column index - * @param requiredType the type that each result object is expected to match (or {@code null} if none specified). + * @param requiredType the type that each result object is expected to match (or {@literal null} if none specified). * @return the Object value. * @throws DriverException in case of extraction failure * @see RowUtils#getRowValue(Row, int, Class) * @see #getColumnValue(Row, int) */ - protected Object getColumnValue(Row row, int index, Class requiredType) throws DriverException { + @Nullable + protected Object getColumnValue(Row row, int index, @Nullable Class requiredType) throws DriverException { if (requiredType != null) { return RowUtils.getRowValue(row, index, requiredType); @@ -148,6 +150,7 @@ public class SingleColumnRowMapper implements RowMapper { * @throws DriverException in case of extraction failure. * @see RowUtils#getRowValue(Row, int, Class) */ + @Nullable protected Object getColumnValue(Row row, int index) { return RowUtils.getRowValue(row, index, null); } @@ -160,8 +163,8 @@ public class SingleColumnRowMapper implements RowMapper { * the value will be converted into a Number, either through number conversion or through String parsing (depending on * the value type). * - * @param value the column value as extracted from {@code getColumnValue()} (never {@code null}) - * @param requiredType the type that each result object is expected to match (never {@code null}) + * @param value the column value as extracted from {@code getColumnValue()} (never {@literal null}) + * @param requiredType the type that each result object is expected to match (never {@literal null}) * @return the converted value * @see #getColumnValue(Row, int, Class) */ diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/WriteOptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/WriteOptions.java index 2a9723c1f..e3d64679c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/WriteOptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/WriteOptions.java @@ -15,8 +15,12 @@ */ package org.springframework.data.cassandra.core.cql; +import java.time.Duration; import java.util.concurrent.TimeUnit; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + import com.datastax.driver.core.ConsistencyLevel; import com.datastax.driver.core.policies.RetryPolicy; @@ -30,20 +34,19 @@ import com.datastax.driver.core.policies.RetryPolicy; */ public class WriteOptions extends QueryOptions { - private Integer ttl; + private static final WriteOptions EMPTY = new WriteOptionsBuilder().build(); - /** - * Creates new {@link WriteOptions}. - */ - public WriteOptions() {} + private final Duration ttl; /** * Creates new {@link WriteOptions} for the given {@link ConsistencyLevel} and {@link RetryPolicy}. * * @param consistencyLevel the consistency level, may be {@literal null}. * @param retryPolicy the retry policy, may be {@literal null}. + * @deprecated since 2.0, use {@link #builder()} or {@link #empty()}. */ - public WriteOptions(ConsistencyLevel consistencyLevel, RetryPolicy retryPolicy) { + @Deprecated + public WriteOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy) { this(consistencyLevel, retryPolicy, null); } @@ -53,10 +56,32 @@ public class WriteOptions extends QueryOptions { * @param consistencyLevel the consistency level, may be {@literal null}. * @param retryPolicy the retry policy, may be {@literal null}. * @param ttl the ttl, may be {@literal null}. + * @deprecated since 2.0, use {@link #builder()}. */ - public WriteOptions(ConsistencyLevel consistencyLevel, RetryPolicy retryPolicy, Integer ttl) { + @Deprecated + public WriteOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, + @Nullable Integer ttl) { + super(consistencyLevel, retryPolicy); - setTtl(ttl); + + this.ttl = ttl == null ? Duration.ofMillis(-1) : Duration.ofSeconds(ttl); + } + + protected WriteOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, + @Nullable Boolean tracing, @Nullable Integer fetchSize, Duration readTimeout, Duration ttl) { + + super(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout); + this.ttl = ttl; + } + + /** + * Create default {@link WriteOptions}. + * + * @return default {@link WriteOptions}. + * @since 2.0 + */ + public static WriteOptions empty() { + return EMPTY; } /** @@ -72,19 +97,10 @@ public class WriteOptions extends QueryOptions { /** * @return the time to live, if set. */ - public Integer getTtl() { + public Duration getTtl() { return this.ttl; } - /** - * Sets the time to live for write operations. - * - * @param ttl the ttl to set. - */ - public void setTtl(Integer ttl) { - this.ttl = ttl; - } - /** * Builder for {@link QueryOptions}. * @@ -93,7 +109,7 @@ public class WriteOptions extends QueryOptions { */ public static class WriteOptionsBuilder extends QueryOptionsBuilder { - private Integer ttl; + protected Duration ttl = Duration.ofMillis(-1); protected WriteOptionsBuilder() {} @@ -138,10 +154,16 @@ public class WriteOptions extends QueryOptions { * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#readTimeout(long, java.util.concurrent.TimeUnit) */ @Override + @Deprecated public WriteOptionsBuilder readTimeout(long readTimeout, TimeUnit timeUnit) { return (WriteOptionsBuilder) super.readTimeout(readTimeout, timeUnit); } + @Override + public WriteOptionsBuilder readTimeout(Duration readTimeout) { + return (WriteOptionsBuilder) super.readTimeout(readTimeout); + } + /* * (non-Javadoc) * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#tracing(boolean) @@ -161,13 +183,34 @@ public class WriteOptions extends QueryOptions { } /** - * Sets the time to live for write operations. + * Sets the time to live in seconds for write operations. * * @param ttl the time to live. * @return {@code this} {@link WriteOptionsBuilder} */ public WriteOptionsBuilder ttl(int ttl) { + + Assert.isTrue(ttl >= 0, "TTL must be greater than equal to zero"); + + this.ttl = Duration.ofSeconds(ttl); + + return this; + } + + /** + * Sets the time to live in seconds for write operations. + * + * @param ttl the time to live. + * @return {@code this} {@link WriteOptionsBuilder} + * @since 2.0 + */ + public WriteOptionsBuilder ttl(Duration ttl) { + + Assert.notNull(ttl, "TTL must not be null"); + Assert.isTrue(!ttl.isNegative(), "TTL must be greater than equal to zero"); + this.ttl = ttl; + return this; } @@ -177,17 +220,7 @@ public class WriteOptions extends QueryOptions { * @return a new {@link WriteOptions} with the configured values */ public WriteOptions build() { - return applyOptions(new WriteOptions()); - } - - @Override - protected T applyOptions(T queryOptions) { - - WriteOptions writeOptions = (WriteOptions) queryOptions; - - writeOptions.setTtl(this.ttl); - - return super.applyOptions(queryOptions); + return new WriteOptions(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout, ttl); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/AbstractResultSetConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/AbstractResultSetConverter.java index 10d35332b..9c46d3230 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/AbstractResultSetConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/AbstractResultSetConverter.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; import com.datastax.driver.core.ResultSet; @@ -59,6 +60,7 @@ public abstract class AbstractResultSetConverter implements Converter implements Converter implements Converter> list = converter.convert(source); + if (list == null) { + return getNullResultSetValue(); + } + if (list.size() == 1) { Map map = list.get(0); @@ -97,8 +100,9 @@ public abstract class AbstractResultSetConverter implements Converter> resultSet) { - doThrow("result set"); - return null; + + throw new IllegalArgumentException( + String.format("Cannot convert %s to desired type [%s]", "result set", getType().getName())); } /** @@ -106,12 +110,8 @@ public abstract class AbstractResultSetConverter implements Converter row) { - doThrow("row"); - return null; - } - void doThrow(String string) { throw new IllegalArgumentException( - String.format("Cannot convert %s to desired type [%s]", string, getType().getName())); + String.format("Cannot convert %s to desired type [%s]", "row", getType().getName())); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToArrayConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToArrayConverter.java index f3e210cc4..9b14d2f71 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToArrayConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToArrayConverter.java @@ -51,10 +51,6 @@ public class ResultSetToArrayConverter implements Converter @Override public Object[] convert(ResultSet resultSet) { - if (resultSet == null) { - return null; - } - List list = new ArrayList<>(); for (Row row : resultSet) { list.add(rowConverter.convert(row)); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToByteBufferConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToByteBufferConverter.java index e7a9ec2d1..0336c737b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToByteBufferConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToByteBufferConverter.java @@ -37,7 +37,8 @@ public class ResultSetToByteBufferConverter extends AbstractResultSetConverter> convert(ResultSet resultSet) { - if (resultSet == null) { - return null; - } - List> list = new ArrayList<>(); for (Row row : resultSet) { list.add(rowConverter.convert(row)); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToListOfStringConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToListOfStringConverter.java index 9dcc0b654..03adeaf0b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToListOfStringConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToListOfStringConverter.java @@ -15,7 +15,7 @@ */ package org.springframework.data.cassandra.core.cql.converter; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -39,11 +39,7 @@ public class ResultSetToListOfStringConverter extends AbstractResultSetConverter */ @Override protected List doConvertSingleValue(Object object) { - - List list = new ArrayList<>(); - - list.add(object == null ? null : object.toString()); - return list; + return Collections.singletonList(object.toString()); } /* (non-Javadoc) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToStringConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToStringConverter.java index 0382f399d..650ed302a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToStringConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/ResultSetToStringConverter.java @@ -36,7 +36,7 @@ public class ResultSetToStringConverter extends AbstractResultSetConverter> { @Override public List convert(Row row) { - if (row == null) { - return null; - } - ColumnDefinitions cols = row.getColumnDefinitions(); return cols.asList().stream() // .map(Definition::getName).map(name -> row.isNull(name) ? null : row.getObject(name)) // diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/RowToMapConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/RowToMapConverter.java index 1b0ebd2c8..261f88b82 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/RowToMapConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/RowToMapConverter.java @@ -44,10 +44,6 @@ public enum RowToMapConverter implements Converter> { @Override public Map convert(Row row) { - if (row == null) { - return null; - } - ColumnDefinitions cols = row.getColumnDefinitions(); Map map = new HashMap<>(cols.size()); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/package-info.java index b5d266d70..b396729eb 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/converter/package-info.java @@ -1,4 +1,7 @@ /** * CQL specific converters. */ +@NonNullApi package org.springframework.data.cassandra.core.cql.converter; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/AddColumnCqlGenerator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/AddColumnCqlGenerator.java index e2126be2e..896d08961 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/AddColumnCqlGenerator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/AddColumnCqlGenerator.java @@ -15,8 +15,6 @@ */ package org.springframework.data.cassandra.core.cql.generator; -import static org.springframework.data.cassandra.core.cql.CqlStringUtils.*; - import org.springframework.data.cassandra.core.cql.keyspace.AddColumnSpecification; /** @@ -39,7 +37,7 @@ public class AddColumnCqlGenerator extends ColumnChangeCqlGenerator { - public static String toCql(AlterKeyspaceSpecification specification) { - return new AlterKeyspaceCqlGenerator(specification).toCql(); - } - public AlterKeyspaceCqlGenerator(AlterKeyspaceSpecification specification) { super(specification); } + public static String toCql(AlterKeyspaceSpecification specification) { + return new AlterKeyspaceCqlGenerator(specification).toCql(); + } + @Override public StringBuilder toCql(StringBuilder cql) { - cql = noNull(cql); preambleCql(cql); optionsCql(cql); @@ -49,13 +46,12 @@ public class AlterKeyspaceCqlGenerator extends KeyspaceOptionsCqlGenerator options = spec().getOptions(); @@ -97,7 +93,5 @@ public class AlterKeyspaceCqlGenerator extends KeyspaceOptionsCqlGenerator { + /** + * Create a new {@literal {@link AlterTableCqlGenerator}. @param specification must not be {@literal null}. + */ + public AlterTableCqlGenerator(AlterTableSpecification specification) { + super(specification); + } + /** * Generates a CQL statement from the given {@code specification}. * @@ -48,18 +53,8 @@ public class AlterTableCqlGenerator extends TableOptionsCqlGenerator getCqlGeneratorFor(ColumnChangeSpecification change) { + private ColumnChangeCqlGenerator getCqlGeneratorFor(ColumnChangeSpecification change) { if (change instanceof AddColumnSpecification) { return new AddColumnCqlGenerator((AddColumnSpecification) change); @@ -121,14 +114,12 @@ public class AlterTableCqlGenerator extends TableOptionsCqlGenerator options = spec().getOptions(); - if (options == null || options.isEmpty()) { - return cql; + if (options.isEmpty()) { + return; } cql.append("WITH "); @@ -169,7 +160,5 @@ public class AlterTableCqlGenerator extends TableOptionsCqlGenerator { - public static String toCql(AlterUserTypeSpecification specification) { - return new AlterUserTypeCqlGenerator(specification).toCql(); - } - /** * Create a new {@link AlterUserTypeCqlGenerator} for a {@link AlterUserTypeSpecification}. * @@ -50,6 +44,10 @@ public class AlterUserTypeCqlGenerator extends UserTypeNameCqlGenerator { - public static String toCql(CreateIndexSpecification specification) { - return new CreateIndexCqlGenerator(specification).toCql(); - } - public CreateIndexCqlGenerator(CreateIndexSpecification specification) { super(specification); } + public static String toCql(CreateIndexSpecification specification) { + return new CreateIndexCqlGenerator(specification).toCql(); + } + @Override public StringBuilder toCql(StringBuilder cql) { - cql = noNull(cql); - cql.append("CREATE").append(spec().isCustom() ? " CUSTOM" : "").append(" INDEX") .append(spec().getIfNotExists() ? " IF NOT EXISTS" : ""); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/CreateKeyspaceCqlGenerator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/CreateKeyspaceCqlGenerator.java index cb1cd5f46..1614f9d16 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/CreateKeyspaceCqlGenerator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/CreateKeyspaceCqlGenerator.java @@ -15,8 +15,6 @@ */ package org.springframework.data.cassandra.core.cql.generator; -import static org.springframework.data.cassandra.core.cql.CqlStringUtils.*; - import java.util.Map; import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification; @@ -30,21 +28,19 @@ import org.springframework.data.cassandra.core.cql.keyspace.Option; * @author Matthew T. Adams * @author Alex Shvid */ -public class CreateKeyspaceCqlGenerator extends KeyspaceCqlGenerator { - - public static String toCql(CreateKeyspaceSpecification specification) { - return new CreateKeyspaceCqlGenerator(specification).toCql(); - } +public class CreateKeyspaceCqlGenerator extends KeyspaceOptionsCqlGenerator { public CreateKeyspaceCqlGenerator(CreateKeyspaceSpecification specification) { super(specification); } + public static String toCql(CreateKeyspaceSpecification specification) { + return new CreateKeyspaceCqlGenerator(specification).toCql(); + } + @Override public StringBuilder toCql(StringBuilder cql) { - cql = noNull(cql); - preambleCql(cql); optionsCql(cql); @@ -53,14 +49,13 @@ public class CreateKeyspaceCqlGenerator extends KeyspaceCqlGenerator { - - public static String toCql(CreateTableSpecification specification) { - return new CreateTableCqlGenerator(specification).toCql(); - } +public class CreateTableCqlGenerator extends TableOptionsCqlGenerator> { public CreateTableCqlGenerator(CreateTableSpecification specification) { super(specification); } + public static String toCql(CreateTableSpecification specification) { + return new CreateTableCqlGenerator(specification).toCql(); + } + + @Override + protected CreateTableSpecification spec() { + return (CreateTableSpecification) super.spec(); + } + @Override public StringBuilder toCql(StringBuilder cql) { - cql = noNull(cql); - preambleCql(cql); columnsAndOptionsCql(cql); @@ -55,15 +59,12 @@ public class CreateTableCqlGenerator extends TableCqlGenerator options = spec().getOptions(); - if (ordering != null || !options.isEmpty()) { + if (!options.isEmpty()) { // option preamble boolean first = true; cql.append(" WITH "); // end option preamble - if (ordering != null) { + if (StringUtils.hasText(ordering)) { cql.append(ordering); first = false; } + if (!options.isEmpty()) { for (String name : options.keySet()) { // append AND if we're not on first option @@ -153,17 +155,16 @@ public class CreateTableCqlGenerator extends TableCqlGenerator columns) { - StringBuilder ordering = null; + + StringBuilder ordering = new StringBuilder(); boolean first = true; for (ColumnSpecification col : columns) { if (col.getOrdering() != null) { // then ordering specified - if (ordering == null) { // then initialize ordering clause + if (StringUtils.isEmpty(ordering)) { // then initialize ordering clause ordering = new StringBuilder().append("CLUSTERING ORDER BY ("); } if (first) { @@ -174,9 +175,11 @@ public class CreateTableCqlGenerator extends TableCqlGenerator { - public static String toCql(CreateUserTypeSpecification specification) { - return new CreateUserTypeCqlGenerator(specification).toCql(); - } - /** * Create a new {@link CreateUserTypeCqlGenerator} for a given {@link CreateUserTypeSpecification}. * @@ -44,6 +38,10 @@ public class CreateUserTypeCqlGenerator extends UserTypeNameCqlGenerator { - public static String toCql(DropUserTypeSpecification specification) { - return new DropUserTypeCqlGenerator(specification).toCql(); - } - /** * Create a new {@link DropUserTypeCqlGenerator} for a given {@link DropUserTypeSpecification}. * @@ -42,13 +36,17 @@ public class DropUserTypeCqlGenerator extends UserTypeNameCqlGenerator> public abstract StringBuilder toCql(StringBuilder cql); - private IndexNameSpecification specification; + private final IndexNameSpecification specification; public IndexNameCqlGenerator(IndexNameSpecification specification) { - setSpecification(specification); - } - - protected void setSpecification(IndexNameSpecification specification) { Assert.notNull(specification, "IndexNameSpecification must not be null"); this.specification = specification; @@ -53,6 +49,6 @@ public abstract class IndexNameCqlGenerator> } public String toCql() { - return toCql(null).toString(); + return toCql(new StringBuilder()).toString(); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/KeyspaceCqlGenerator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/KeyspaceCqlGenerator.java deleted file mode 100644 index 25e4b7ffe..000000000 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/KeyspaceCqlGenerator.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.cassandra.core.cql.generator; - -import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceSpecification; - -/** - * Base class that contains behavior common to CQL generation for table operations. - * - * @author Matthew T. Adams - * @param subtype of this class for which this is a CQL generator. - */ -public abstract class KeyspaceCqlGenerator> - extends KeyspaceOptionsCqlGenerator> { - - public KeyspaceCqlGenerator(KeyspaceSpecification specification) { - super(specification); - } - - @SuppressWarnings("unchecked") - protected T spec() { - return (T) getSpecification(); - } -} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/KeyspaceNameCqlGenerator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/KeyspaceNameCqlGenerator.java index bc182f34a..0d77c9f48 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/KeyspaceNameCqlGenerator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/KeyspaceNameCqlGenerator.java @@ -28,13 +28,9 @@ public abstract class KeyspaceNameCqlGenerator specification; + private final KeyspaceActionSpecification specification; public KeyspaceNameCqlGenerator(KeyspaceActionSpecification specification) { - setSpecification(specification); - } - - protected void setSpecification(KeyspaceActionSpecification specification) { Assert.notNull(specification, "KeyspaceActionSpecification must not be null"); this.specification = specification; @@ -53,6 +49,6 @@ public abstract class KeyspaceNameCqlGenerator valueMap, StringBuilder cql) { - cql = noNull(cql); + void optionValueMap(Map valueMap, StringBuilder cql) { - if (valueMap == null || valueMap.isEmpty()) { - return cql; + if (valueMap.isEmpty()) { + return; } // else option value is a non-empty map @@ -73,6 +72,6 @@ public abstract class KeyspaceOptionsCqlGenerator> - extends TableOptionsCqlGenerator> { - - public TableCqlGenerator(TableSpecification specification) { - super(specification); - } - - @SuppressWarnings("unchecked") - protected T spec() { - return (T) getSpecification(); - } -} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/TableNameCqlGenerator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/TableNameCqlGenerator.java index 49c1945be..98102b0e1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/TableNameCqlGenerator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/TableNameCqlGenerator.java @@ -24,22 +24,18 @@ import org.springframework.util.Assert; * @param subtype of TableNameSpecification. * @author Mark Paluch */ -public abstract class TableNameCqlGenerator> { +public abstract class TableNameCqlGenerator { - public abstract StringBuilder toCql(StringBuilder cql); + private TableNameSpecification specification; - private TableNameSpecification specification; - - public TableNameCqlGenerator(TableNameSpecification specification) { - setSpecification(specification); - } - - protected void setSpecification(TableNameSpecification specification) { + public TableNameCqlGenerator(TableNameSpecification specification) { Assert.notNull(specification, "TableNameSpecification must not be null"); this.specification = specification; } + public abstract StringBuilder toCql(StringBuilder cql); + @SuppressWarnings("unchecked") public T getSpecification() { return (T) specification; @@ -53,6 +49,6 @@ public abstract class TableNameCqlGenerator> } public String toCql() { - return toCql(null).toString(); + return toCql(new StringBuilder()).toString(); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/TableOptionsCqlGenerator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/TableOptionsCqlGenerator.java index 2e879a749..d8a8231cb 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/TableOptionsCqlGenerator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/generator/TableOptionsCqlGenerator.java @@ -29,7 +29,7 @@ import org.springframework.data.cassandra.core.cql.keyspace.TableOptionsSpecific * @param T The subtype of this class for which this is a CQL generator. */ public abstract class TableOptionsCqlGenerator> - extends TableNameCqlGenerator> { + extends TableNameCqlGenerator { public TableOptionsCqlGenerator(TableOptionsSpecification specification) { super(specification); @@ -37,14 +37,13 @@ public abstract class TableOptionsCqlGenerator valueMap, StringBuilder cql) { - cql = noNull(cql); + void optionValueMap(Map valueMap, StringBuilder cql) { - if (valueMap == null || valueMap.isEmpty()) { - return cql; + if (valueMap.isEmpty()) { + return; } // else option value is a non-empty map @@ -73,6 +72,6 @@ public abstract class TableOptionsCqlGenerator> { - public abstract StringBuilder toCql(StringBuilder cql); - - private UserTypeNameSpecification specification; + private final UserTypeNameSpecification specification; /** * Create a new {@link UserTypeNameCqlGenerator}. @@ -39,21 +37,14 @@ public abstract class UserTypeNameCqlGenerator specification) { - setSpecification(specification); - } - - /** - * Sets the {@link UserTypeNameSpecification}. - * - * @param specification must not be {@literal null}. - */ - protected final void setSpecification(UserTypeNameSpecification specification) { Assert.notNull(specification, "UserTypeNameSpecification must not be null"); this.specification = specification; } + public abstract StringBuilder toCql(StringBuilder cql); + @SuppressWarnings("unchecked") public T getSpecification() { return (T) specification; @@ -67,6 +58,6 @@ public abstract class UserTypeNameCqlGenerator { - /** - * Entry point into the {@link AlterKeyspaceSpecification}'s fluent API to alter a keyspace. Convenient if imported - * statically. - */ - public static AlterKeyspaceSpecification alterKeyspace() { - return new AlterKeyspaceSpecification(); + private AlterKeyspaceSpecification(KeyspaceIdentifier name) { + super(name); } /** @@ -35,14 +34,11 @@ public class AlterKeyspaceSpecification extends KeyspaceOptionsSpecification { /** - * Entry point into the {@link AlterTableSpecification}'s fluent API to alter a table. Convenient if imported - * statically. + * The list of column changes. */ - public static AlterTableSpecification alterTable() { - return new AlterTableSpecification(); + private List changes = new ArrayList<>(); + + private AlterTableSpecification(CqlIdentifier name) { + super(name); } /** @@ -52,7 +53,7 @@ public class AlterTableSpecification extends TableOptionsSpecification changes = new ArrayList<>(); - /* * Adds a {@code DROP} to the list of column changes. * @@ -75,7 +71,7 @@ public class AlterTableSpecification extends TableOptionsSpecification changes = new ArrayList<>(); - /** - * Entry point into the {@link AlterUserTypeSpecification}'s fluent API to alter a type. Convenient if imported - * statically. - */ - public static AlterUserTypeSpecification alterType() { - return new AlterUserTypeSpecification(); - } - /** * Entry point into the {@link AlterUserTypeSpecification}'s fluent API to alter a type. Convenient if imported * statically. @@ -55,8 +47,12 @@ public class AlterUserTypeSpecification extends UserTypeNameSpecificationtrue, then sets the column's - * {@link #keyType} to {@link PrimaryKeyType#PARTITIONED}, else sets it to {@code null}. + * {@link #ordering} to {@literal null} and, if the given boolean is true, then sets the column's + * {@link #keyType} to {@link PrimaryKeyType#PARTITIONED}, else sets it to {@literal null}. * * @return this */ @@ -125,7 +128,7 @@ public class ColumnSpecification { /** * Toggles the identification of this column as a clustered key column. If the given boolean is {@code true}, then * sets the column's {@link #keyType} to {@link PrimaryKeyType#PARTITIONED} and {@link #ordering} to the given - * {@link Ordering} , else sets both {@link #keyType} and {@link #ordering} to {@code null}. + * {@link Ordering} , else sets both {@link #keyType} and {@link #ordering} to {@literal null}. * * @return this */ @@ -159,24 +162,27 @@ public class ColumnSpecification { return name; } + @Nullable public DataType getType() { return type; } + @Nullable public PrimaryKeyType getKeyType() { return keyType; } + @Nullable public Ordering getOrdering() { return ordering; } public String toCql() { - return toCql(null).toString(); + return toCql(new StringBuilder()).toString(); } public StringBuilder toCql(StringBuilder cql) { - return noNull(cql).append(name).append(" ").append(type); + return cql.append(name).append(" ").append(type); } /* (non-Javadoc) @@ -184,7 +190,7 @@ public class ColumnSpecification { */ @Override public String toString() { - return toCql(null).append(" /* keyType=").append(keyType).append(", ordering=").append(ordering).append(" */ ") - .toString(); + return toCql(new StringBuilder()).append(" /* keyType=").append(keyType).append(", ordering=").append(ordering) + .append(" */ ").toString(); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/ColumnTypeChangeSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/ColumnTypeChangeSpecification.java index 653c27323..e31bd2ce7 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/ColumnTypeChangeSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/ColumnTypeChangeSpecification.java @@ -15,8 +15,6 @@ */ package org.springframework.data.cassandra.core.cql.keyspace; -import static org.springframework.data.cassandra.core.cql.CqlIdentifier.*; - import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.util.Assert; @@ -33,26 +31,16 @@ public abstract class ColumnTypeChangeSpecification extends ColumnChangeSpecific private final DataType type; /** - * Create a new {@link ColumnTypeChangeSpecification} for the given {@code name} and {@link type} - * - * @param name must not be empty or {@literal null}. - * @param type must not be {@literal null}. - */ - public ColumnTypeChangeSpecification(String name, DataType type) { - this(cqlId(name), type); - } - - /** - * Create a new {@link ColumnTypeChangeSpecification} for the given {@code name} and {@link type} + * Create a new {@link ColumnTypeChangeSpecification} for the given {@code name} and {@link DataType} * * @param name must not be {@literal null}. * @param type must not be {@literal null}. */ public ColumnTypeChangeSpecification(CqlIdentifier name, DataType type) { + super(name); Assert.notNull(type, "DataType must not be null"); - this.type = type; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/CreateIndexSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/CreateIndexSpecification.java index f01ae692d..43ff9ccac 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/CreateIndexSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/CreateIndexSpecification.java @@ -15,7 +15,7 @@ */ package org.springframework.data.cassandra.core.cql.keyspace; -import static org.springframework.data.cassandra.core.cql.CqlIdentifier.cqlId; +import static org.springframework.data.cassandra.core.cql.CqlIdentifier.*; import java.util.Collection; import java.util.Collections; @@ -23,6 +23,7 @@ import java.util.LinkedHashMap; import java.util.Map; import org.springframework.data.cassandra.core.cql.CqlIdentifier; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -40,13 +41,13 @@ public class CreateIndexSpecification extends IndexNameSpecification options = new LinkedHashMap<>(); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/CreateKeyspaceSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/CreateKeyspaceSpecification.java index 924db87d0..5b9bcf39b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/CreateKeyspaceSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/CreateKeyspaceSpecification.java @@ -15,18 +15,20 @@ */ package org.springframework.data.cassandra.core.cql.keyspace; +import lombok.EqualsAndHashCode; + import org.springframework.data.cassandra.core.cql.KeyspaceIdentifier; import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceOption.ReplicationStrategy; import org.springframework.data.cassandra.util.MapBuilder; -public class CreateKeyspaceSpecification extends KeyspaceSpecification { +@EqualsAndHashCode(callSuper = true) +public class CreateKeyspaceSpecification extends KeyspaceOptionsSpecification + implements KeyspaceDescriptor { - /** - * Entry point into the {@link CreateKeyspaceSpecification}'s fluent API to create a keyspace. Convenient if imported - * statically. - */ - public static CreateKeyspaceSpecification createKeyspace() { - return new CreateKeyspaceSpecification(); + private boolean ifNotExists = false; + + private CreateKeyspaceSpecification(KeyspaceIdentifier name) { + super(name); } /** @@ -34,7 +36,7 @@ public class CreateKeyspaceSpecification extends KeyspaceSpecification type; + private final Class type; - private boolean requiresValue; + private final boolean requiresValue; - private boolean escapesValue; + private final boolean escapesValue; - private boolean quotesValue; + private final boolean quotesValue; public DefaultOption(String name, Class type, boolean requiresValue, boolean escapesValue, boolean quotesValue) { - setName(name); - setType(type); + Assert.hasText(name, "Name must not be null or empty"); + Assert.notNull(type, "Type must not be null"); + + if (type.isInterface() && !(Map.class.isAssignableFrom(type) || Collection.class.isAssignableFrom(type))) { + throw new IllegalArgumentException("given type [" + type.getName() + "] must be a class, Map or Collection"); + } + + this.name = name; + this.type = type; this.requiresValue = requiresValue; this.escapesValue = escapesValue; @@ -53,24 +60,10 @@ public class DefaultOption implements Option { } - protected void setName(String name) { - - Assert.hasText(name, "Name must not be null or empty"); - this.name = name; - } - - protected void setType(Class type) { - if (type != null) { - if (type.isInterface() && !(Map.class.isAssignableFrom(type) || Collection.class.isAssignableFrom(type))) { - throw new IllegalArgumentException("given type [" + type.getName() + "] must be a class, Map or Collection"); - } - } - this.type = type; - } - @SuppressWarnings({ "unchecked", "rawtypes" }) public boolean isCoerceable(Object value) { - if (value == null || type == null) { + + if (getType().equals(Void.class)) { return true; } @@ -88,9 +81,7 @@ public class DefaultOption implements Option { String name = value instanceof Enum ? name = ((Enum) value).name() : value.toString(); Enum.valueOf((Class) type, name); return true; - } catch (NullPointerException x) { - return false; - } catch (IllegalArgumentException x) { + } catch (IllegalArgumentException | NullPointerException x) { return false; } } @@ -103,7 +94,7 @@ public class DefaultOption implements Option { } ctor.newInstance(value.toString()); return true; - } catch (InstantiationException e) {} catch (IllegalAccessException e) {} catch (IllegalArgumentException e) {} catch (InvocationTargetException e) {} catch (NoSuchMethodException e) {} catch (SecurityException e) {} + } catch (Exception e) {} return false; } @@ -125,7 +116,7 @@ public class DefaultOption implements Option { * @see org.springframework.data.cassandra.core.cql.keyspace.Option#takesValue() */ public boolean takesValue() { - return type != null; + return type != Void.class; } /* (non-Javadoc) @@ -152,7 +143,7 @@ public class DefaultOption implements Option { /* (non-Javadoc) * @see org.springframework.data.cassandra.core.cql.keyspace.Option#checkValue(java.lang.Object) */ - public void checkValue(Object value) { + public void checkValue(@Nullable Object value) { if (takesValue()) { if (value == null) { if (requiresValue) { @@ -166,7 +157,7 @@ public class DefaultOption implements Option { } // else value is not coerceable into the expected type throw new IllegalArgumentException( - "Option [" + getName() + "] takes value coerceable to type [" + getType().getName() + "]"); + "Option [" + getName() + "] takes value coerceable to type [" + getType() + "]"); } // else this option doesn't take a value if (value != null) { @@ -174,10 +165,12 @@ public class DefaultOption implements Option { } } - public String toString(Object value) { + public String toString(@Nullable Object value) { + if (value == null) { - return null; + return ""; } + checkValue(value); String string = value.toString(); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DefaultTableDescriptor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DefaultTableDescriptor.java deleted file mode 100644 index 57ef43ded..000000000 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DefaultTableDescriptor.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2013-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.cassandra.core.cql.keyspace; - -/** - * Convenient default implementation of {@link TableDescriptor} as an extension of {@link TableSpecification} that - * doesn't require the use of generics. - * - * @author Matthew T. Adams - */ -public class DefaultTableDescriptor extends TableSpecification { - - /** - * Factory method to produce a new {@link DefaultTableDescriptor}. Convenient if imported statically. - */ - public static DefaultTableDescriptor table() { - return new DefaultTableDescriptor(); - } -} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropColumnSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropColumnSpecification.java index 76c65b80d..91a50c278 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropColumnSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropColumnSpecification.java @@ -25,17 +25,13 @@ import org.springframework.data.cassandra.core.cql.CqlIdentifier; public class DropColumnSpecification extends ColumnChangeSpecification { public static DropColumnSpecification dropColumn(String name) { - return new DropColumnSpecification(name); + return new DropColumnSpecification(CqlIdentifier.cqlId(name)); } public static DropColumnSpecification dropColumn(CqlIdentifier name) { return new DropColumnSpecification(name); } - public DropColumnSpecification(String name) { - super(name); - } - public DropColumnSpecification(CqlIdentifier name) { super(name); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropIndexSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropIndexSpecification.java index 00f7ef3c7..52cffffa5 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropIndexSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropIndexSpecification.java @@ -15,6 +15,8 @@ */ package org.springframework.data.cassandra.core.cql.keyspace; +import org.springframework.data.cassandra.core.cql.CqlIdentifier; + /** * Builder class that supports the construction of {@code DROP INDEX} specifications. * @@ -23,31 +25,23 @@ package org.springframework.data.cassandra.core.cql.keyspace; */ public class DropIndexSpecification extends IndexNameSpecification { - // private boolean ifExists; + private DropIndexSpecification(CqlIdentifier name) { + super(name); + } /** * Entry point into the {@link DropIndexSpecification}'s fluent API to drop a table. Convenient if imported * statically. */ - public static DropIndexSpecification dropIndex() { - return new DropIndexSpecification(); + public static DropIndexSpecification dropIndex(String name) { + return new DropIndexSpecification(CqlIdentifier.cqlId(name)); } - /* - * In CQL 3.1 this is supported so we can uncomment the exposure then. - * In the meantime, it will always be false and tests will pass. + /** + * Entry point into the {@link DropIndexSpecification}'s fluent API to drop a table. Convenient if imported + * statically. */ - - // public DropIndexSpecification ifExists() { - // return ifExists(true); - // } - // - // public DropIndexSpecification ifExists(boolean ifExists) { - // this.ifExists = ifExists; - // return this; - // } - // - // public boolean getIfExists() { - // return ifExists; - // } + public static DropIndexSpecification dropIndex(CqlIdentifier name) { + return new DropIndexSpecification(name); + } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropKeyspaceSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropKeyspaceSpecification.java index c0c864cc3..55aa827d0 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropKeyspaceSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/DropKeyspaceSpecification.java @@ -15,16 +15,17 @@ */ package org.springframework.data.cassandra.core.cql.keyspace; +import lombok.EqualsAndHashCode; + import org.springframework.data.cassandra.core.cql.KeyspaceIdentifier; +@EqualsAndHashCode(callSuper = true) public class DropKeyspaceSpecification extends KeyspaceActionSpecification { - /** - * Entry point into the {@link DropKeyspaceSpecification}'s fluent API to drop a keyspace. Convenient if imported - * statically. - */ - public static DropKeyspaceSpecification dropKeyspace() { - return new DropKeyspaceSpecification(); + private boolean ifExists; + + private DropKeyspaceSpecification(KeyspaceIdentifier name) { + super(name); } /** @@ -40,19 +41,7 @@ public class DropKeyspaceSpecification extends KeyspaceActionSpecification { +public class DropTableSpecification extends TableNameSpecification { - // private boolean ifExists; - - // Added in Cassandra 2.0. - - // public DropTableSpecification ifExists() { - // return ifExists(true); - // } - // - // public DropTableSpecification ifExists(boolean ifExists) { - // this.ifExists = ifExists; - // return this; - // } - // - // public boolean getIfExists() { - // return ifExists; - // } - - /** - * Entry point into the {@link DropTableSpecification}'s fluent API to drop a table. Convenient if imported - * statically. - */ - public static DropTableSpecification dropTable() { - return new DropTableSpecification(); + private DropTableSpecification(CqlIdentifier name) { + super(name); } /** @@ -56,7 +35,7 @@ public class DropTableSpecification extends TableNameSpecification /** * The name of the index. */ - private CqlIdentifier name; + private @Nullable CqlIdentifier name; + + protected IndexNameSpecification() {} + + protected IndexNameSpecification(CqlIdentifier name) { + + Assert.notNull(name, "CqlIdentifier must not be null"); + this.name = name; + } /** * Sets the index name. @@ -54,6 +63,7 @@ public abstract class IndexNameSpecification return (T) this; } + @Nullable public CqlIdentifier getName() { return this.name; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceActionSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceActionSpecification.java index 2b0ce95ae..f02c0cfaf 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceActionSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceActionSpecification.java @@ -15,7 +15,7 @@ */ package org.springframework.data.cassandra.core.cql.keyspace; -import static org.springframework.data.cassandra.core.cql.KeyspaceIdentifier.*; +import lombok.EqualsAndHashCode; import org.springframework.data.cassandra.core.cql.KeyspaceIdentifier; import org.springframework.util.Assert; @@ -27,68 +27,22 @@ import org.springframework.util.Assert; * @author David Webb * @param The subtype of the {@link KeyspaceActionSpecification} */ +@EqualsAndHashCode public abstract class KeyspaceActionSpecification> { /** * The name of the keyspace. */ - private KeyspaceIdentifier name; + private final KeyspaceIdentifier name; - /** - * Sets the keyspace name. - * - * @return this - */ - public T name(String name) { - return name(ksId(name)); - } - - /** - * Sets the keyspace name. - * - * @return this - */ - @SuppressWarnings("unchecked") - public T name(KeyspaceIdentifier name) { + protected KeyspaceActionSpecification(KeyspaceIdentifier name) { Assert.notNull(name, "KeyspaceIdentifier must not be null"); this.name = name; - return (T) this; } public KeyspaceIdentifier getName() { return name; } - - /** - * Equality incorporates the exact type to distinguish between instances based on this type. - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object o) { - - if (this == o) - return true; - if (!(o instanceof KeyspaceActionSpecification) || !getClass().equals(o.getClass())) - return false; - - KeyspaceActionSpecification that = (KeyspaceActionSpecification) o; - - return name != null ? name.equals(that.name) : that.name == null; - } - - /** - * Hash code incorporates the exact type to distinguish between instances based on this type. - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - - int result = name != null ? name.hashCode() : 0; - result = 31 * result + (getClass().hashCode()); - return result; - } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceOption.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceOption.java index 53463ad5d..b1881e462 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceOption.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceOption.java @@ -18,6 +18,7 @@ package org.springframework.data.cassandra.core.cql.keyspace; import java.util.Map; public enum KeyspaceOption implements Option { + REPLICATION("replication", Map.class, true, false, false), DURABLE_WRITES("durable_writes", Boolean.class, false, false, false); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceOptionsSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceOptionsSpecification.java index 22816c273..0ccd8a09b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceOptionsSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/KeyspaceOptionsSpecification.java @@ -17,11 +17,15 @@ package org.springframework.data.cassandra.core.cql.keyspace; import static org.springframework.data.cassandra.core.cql.CqlStringUtils.*; +import lombok.EqualsAndHashCode; + import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import org.springframework.data.cassandra.core.cql.CqlStringUtils; +import org.springframework.data.cassandra.core.cql.KeyspaceIdentifier; +import org.springframework.lang.Nullable; /** * Abstract builder class to support the construction of table specifications that have table options, that is, those @@ -36,15 +40,14 @@ import org.springframework.data.cassandra.core.cql.CqlStringUtils; * @author John McPeek * @param The subtype of the {@link KeyspaceOptionsSpecification}. */ +@EqualsAndHashCode(callSuper = true) public abstract class KeyspaceOptionsSpecification> extends KeyspaceActionSpecification> { protected Map options = new LinkedHashMap<>(); - @Override - @SuppressWarnings("unchecked") - public T name(String name) { - return (T) super.name(name); + protected KeyspaceOptionsSpecification(KeyspaceIdentifier name) { + super(name); } /** @@ -53,7 +56,7 @@ public abstract class KeyspaceOptionsSpecification - * Options that have {@code null} values are considered single string options where the name of the option is the + * Options that have {@literal null} values are considered single string options where the name of the option is the * string to be used. Otherwise, the result of {@link Object#toString()} is considered to be the value of the option * with the given name. The value, after conversion to string, may have embedded single quotes escaped according to * parameter {@code escape} and may be single-quoted according to parameter quote. * * @param name The name of the option - * @param value The value of the option. If {@code null}, the value is ignored and the option is considered to be + * @param value The value of the option. If {@literal null}, the value is ignored and the option is considered to be * composed of only the name, otherwise the value's {@link Object#toString()} value is used. * @param escape Whether to escape the value via {@link CqlStringUtils#escapeSingle(Object)}. Ignored if given value * is an instance of a {@link Map}. @@ -89,7 +92,8 @@ public abstract class KeyspaceOptionsSpecification extends KeyspaceOptionsSpecification> - implements KeyspaceDescriptor {} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/Option.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/Option.java index 3a43affd7..1b5c7919d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/Option.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/Option.java @@ -15,6 +15,8 @@ */ package org.springframework.data.cassandra.core.cql.keyspace; +import org.springframework.lang.Nullable; + /** * Interface to represent option types. * @@ -66,10 +68,10 @@ public interface Option { * First ensures that the given value is coerceable into the type expected by this option, then returns the result of * {@link Object#toString()} called on the given value. If this option is escaping quotes ({@link #escapesValue()} is * {@code true}), then single quotes will be escaped, and if this option is quoting values ( {@link #quotesValue()} is - * {@code true}), then the value will be surrounded by single quotes. Given {@code null}, returns null. + * {@code true}), then the value will be surrounded by single quotes. Given {@literal null}, returns an empty string. * * @see #escapesValue() * @see #quotesValue() */ - String toString(Object value); + String toString(@Nullable Object value); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/RenameColumnSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/RenameColumnSpecification.java index f35fee17f..e3563f26b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/RenameColumnSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/RenameColumnSpecification.java @@ -48,6 +48,7 @@ public class RenameColumnSpecification extends ColumnChangeSpecification { * @param to must not be {@literal null}. */ RenameColumnSpecification(CqlIdentifier from, CqlIdentifier to) { + super(from); Assert.notNull(to, "Target name must not be null"); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableNameSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableNameSpecification.java index c217b4026..539b1d0a3 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableNameSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableNameSpecification.java @@ -15,8 +15,6 @@ */ package org.springframework.data.cassandra.core.cql.keyspace; -import static org.springframework.data.cassandra.core.cql.CqlIdentifier.cqlId; - import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.util.Assert; @@ -24,32 +22,19 @@ import org.springframework.util.Assert; * Abstract builder class to support the construction of table specifications. * * @author Matthew T. Adams - * @param The subtype of the {@link TableNameSpecification} */ -public abstract class TableNameSpecification> { +public abstract class TableNameSpecification { /** * The name of the table. */ - private CqlIdentifier name; + private final CqlIdentifier name; - /** - * Sets the table name. - * - * @return this - */ - public T name(String name) { - return name(cqlId(name)); - } - - @SuppressWarnings("unchecked") - public T name(CqlIdentifier name) { + protected TableNameSpecification(CqlIdentifier name) { Assert.notNull(name, "CqlIdentifier must not be null"); this.name = name; - - return (T) this; } public CqlIdentifier getName() { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableOption.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableOption.java index de56365aa..311f485e5 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableOption.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableOption.java @@ -36,7 +36,7 @@ public enum TableOption implements Option { /** * {@code COMPACT STORAGE} */ - COMPACT_STORAGE("COMPACT STORAGE", null, false, false, false), + COMPACT_STORAGE("COMPACT STORAGE", Void.class, false, false, false), /** * {@code compaction}. Value is a Map<CompactionOption,Object>. * @@ -46,7 +46,7 @@ public enum TableOption implements Option { /** * {@code compression}. Value is a Map<CompressionOption,Object>. * - * @see {@link CompressionOption} + * @see CompressionOption */ COMPRESSION("compression", Map.class, true, false, false), /** diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableOptionsSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableOptionsSpecification.java index 1dce824a9..5c06fdd7a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableOptionsSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/TableOptionsSpecification.java @@ -23,6 +23,7 @@ import java.util.Map; import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.data.cassandra.core.cql.CqlStringUtils; +import org.springframework.lang.Nullable; /** * Abstract builder class to support the construction of table specifications that have table options, that is, those @@ -35,23 +36,15 @@ import org.springframework.data.cassandra.core.cql.CqlStringUtils; * are introduced without having to update the code immediately. * * @author Matthew T. Adams + * @author Mark Paluch * @param The subtype of the {@link TableOptionsSpecification}. */ -public abstract class TableOptionsSpecification> - extends TableNameSpecification> { +public abstract class TableOptionsSpecification> extends TableNameSpecification { - protected Map options = new LinkedHashMap<>(); + private final Map options = new LinkedHashMap<>(); - @Override - @SuppressWarnings("unchecked") - public T name(String name) { - return (T) super.name(name); - } - - @Override - @SuppressWarnings("unchecked") - public T name(CqlIdentifier name) { - return (T) super.name(name); + protected TableOptionsSpecification(CqlIdentifier name) { + super(name); } /** @@ -60,7 +53,7 @@ public abstract class TableOptionsSpecification - * Options that have {@code null} values are considered single string options where the name of the option is the + * Options that have {@literal null} values are considered single string options where the name of the option is the * string to be used. Otherwise, the result of {@link Object#toString()} is considered to be the value of the option * with the given name. The value, after conversion to string, may have embedded single quotes escaped according to * parameter {@code escape} and may be single-quoted according to parameter quote. * * @param name The name of the option - * @param value The value of the option. If {@code null}, the value is ignored and the option is considered to be + * @param value The value of the option. If {@literal null}, the value is ignored and the option is considered to be * composed of only the name, otherwise the value's {@link Object#toString()} value is used. * @param escape Whether to escape the value via {@link CqlStringUtils#escapeSingle(Object)}. Ignored if given value * is an instance of a {@link Map}. @@ -96,7 +89,7 @@ public abstract class TableOptionsSpecification extends TableOptionsSpecification nonKeyColumns = new ArrayList<>(); + protected TableSpecification(CqlIdentifier name) { + super(name); + } + /** * Adds the given non-key column to the table. Must be specified after all primary key columns. * @@ -226,7 +230,7 @@ public class TableSpecification extends TableOptionsSpecification { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/UserTypeNameSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/UserTypeNameSpecification.java index d6c53a5fa..f1691d179 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/UserTypeNameSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/UserTypeNameSpecification.java @@ -29,32 +29,13 @@ import org.springframework.util.Assert; */ public abstract class UserTypeNameSpecification> { - private CqlIdentifier name; + private final CqlIdentifier name; - /** - * Sets the type name. - * - * @param name must not be empty or {@literal null}. - * @return this - */ - public T name(String name) { - return name(CqlIdentifier.cqlId(name)); - } - - /** - * Sets the type name. - * - * @param name must not be {@literal null}. - * @return this - */ - @SuppressWarnings("unchecked") - public T name(CqlIdentifier name) { + protected UserTypeNameSpecification(CqlIdentifier name) { Assert.notNull(name, "Name must not be null"); this.name = name; - - return (T) this; } /** diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/UserTypeSpecification.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/UserTypeSpecification.java index 9ed85818e..fefff907b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/UserTypeSpecification.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/UserTypeSpecification.java @@ -38,6 +38,10 @@ public class UserTypeSpecification> extends U private final List fields = new ArrayList<>(); + protected UserTypeSpecification(CqlIdentifier name) { + super(name); + } + /** * Adds the given field to the type. * @@ -59,7 +63,7 @@ public class UserTypeSpecification> extends U @SuppressWarnings("unchecked") public T field(CqlIdentifier name, DataType type) { - fields.add(new FieldSpecification().name(name).type(type)); + fields.add(FieldSpecification.of(name, type)); return (T) this; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/package-info.java index a0458112c..f97a5ebce 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/keyspace/package-info.java @@ -1,4 +1,7 @@ /** * Keyspace object action specifications such as {@code CREATE TABLE}, {@code DROP INDEX}. */ +@NonNullApi package org.springframework.data.cassandra.core.cql.keyspace; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/package-info.java index 888ebc3cd..68dc08402 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/package-info.java @@ -1,4 +1,7 @@ /** * CQL core support. */ +@NonNullApi package org.springframework.data.cassandra.core.cql; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/AbstractRoutingSessionFactory.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/AbstractRoutingSessionFactory.java index 7d3f466c3..a1b6b2444 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/AbstractRoutingSessionFactory.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/AbstractRoutingSessionFactory.java @@ -20,6 +20,7 @@ import java.util.Map; import org.springframework.beans.factory.InitializingBean; import org.springframework.data.cassandra.SessionFactory; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.driver.core.Session; @@ -38,17 +39,17 @@ import com.datastax.driver.core.Session; */ public abstract class AbstractRoutingSessionFactory implements SessionFactory, InitializingBean { - private Map targetSessionFactories; + private @Nullable Map targetSessionFactories; - private Object defaultTargetSessionFactory; + private @Nullable Object defaultTargetSessionFactory; private boolean lenientFallback = true; private SessionFactoryLookup sessionFactoryLookup = new MapSessionFactoryLookup(); - private Map resolvedSessionFactories; + private @Nullable Map resolvedSessionFactories; - private SessionFactory resolvedDefaultSessionFactory; + private @Nullable SessionFactory resolvedDefaultSessionFactory; /** * Specify the map of target session factories, with the lookup key as key. @@ -61,6 +62,9 @@ public abstract class AbstractRoutingSessionFactory implements SessionFactory, I * {@link #determineCurrentLookupKey()}. */ public void setTargetSessionFactories(Map targetSessionFactories) { + + Assert.notNull(targetSessionFactories, "Target SessionFactories must not be null"); + this.targetSessionFactories = targetSessionFactories; } @@ -74,6 +78,9 @@ public abstract class AbstractRoutingSessionFactory implements SessionFactory, I * match the {@link #determineCurrentLookupKey()} current lookup key. */ public void setDefaultTargetSessionFactory(Object defaultTargetSessionFactory) { + + Assert.notNull(defaultTargetSessionFactory, "Default target SessionFactory must not be null"); + this.defaultTargetSessionFactory = defaultTargetSessionFactory; } @@ -85,7 +92,7 @@ public abstract class AbstractRoutingSessionFactory implements SessionFactory, I * {@link SessionFactory} map - simply falling back to the default {@link SessionFactory} in that case. *

* Switch this flag to {@literal false} if you would prefer the fallback to only apply if the lookup key was - * {@code null}. Lookup keys without a {@link SessionFactory} entry will then lead to an + * {@literal null}. Lookup keys without a {@link SessionFactory} entry will then lead to an * {@link IllegalStateException}. * * @param lenientFallback {@literal true} to accepting lookup keys without a corresponding entry in the target. @@ -107,7 +114,7 @@ public abstract class AbstractRoutingSessionFactory implements SessionFactory, I * @param sessionFactoryLookup the {@link SessionFactoryLookup}. Defaults to {@link MapSessionFactoryLookup} if * {@literal null}. */ - public void setSessionFactoryLookup(SessionFactoryLookup sessionFactoryLookup) { + public void setSessionFactoryLookup(@Nullable SessionFactoryLookup sessionFactoryLookup) { this.sessionFactoryLookup = (sessionFactoryLookup != null ? sessionFactoryLookup : new MapSessionFactoryLookup()); } @@ -174,10 +181,10 @@ public abstract class AbstractRoutingSessionFactory implements SessionFactory, I } else if (sessionFactory instanceof String) { return this.sessionFactoryLookup.getSessionFactory((String) sessionFactory); } else { - throw new IllegalArgumentException(String - .format( - "Illegal session factory value. Only [org.springframework.data.cassandra.core.cql.session.SessionFactory]" - + " and String supported: %s", sessionFactory)); + throw new IllegalArgumentException(String.format( + "Illegal session factory value. Only [org.springframework.data.cassandra.core.cql.session.SessionFactory]" + + " and String supported: %s", + sessionFactory)); } } @@ -214,5 +221,6 @@ public abstract class AbstractRoutingSessionFactory implements SessionFactory, I * * @return the current lookup key. The returned key needs to match the stored lookup key type. */ + @Nullable protected abstract Object determineCurrentLookupKey(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/BeanFactorySessionFactoryLookup.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/BeanFactorySessionFactoryLookup.java index 50ea066c0..5b92e1018 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/BeanFactorySessionFactoryLookup.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/BeanFactorySessionFactoryLookup.java @@ -19,6 +19,7 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.data.cassandra.SessionFactory; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -32,7 +33,7 @@ import org.springframework.util.Assert; */ public class BeanFactorySessionFactoryLookup implements SessionFactoryLookup, BeanFactoryAware { - private BeanFactory beanFactory; + private @Nullable BeanFactory beanFactory; /** * Create a new instance of {@link BeanFactorySessionFactoryLookup}. diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/MapSessionFactoryLookup.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/MapSessionFactoryLookup.java index a631aaa12..d2a5dd1c7 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/MapSessionFactoryLookup.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/MapSessionFactoryLookup.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.data.cassandra.SessionFactory; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -65,11 +66,11 @@ public class MapSessionFactoryLookup implements SessionFactoryLookup { * Set the {@link Map} of {@link SessionFactory session factories}; the keys are {@link String Strings}, the values * are actual {@link SessionFactory} instances. *

- * If the supplied {@link Map} is {@code null}, then this method call effectively has no effect. + * If the supplied {@link Map} is {@literal null}, then this method call effectively has no effect. * * @param sessionFactories {@link Map} of {@link SessionFactory session factories}. */ - public void setSessionFactories(Map sessionFactories) { + public void setSessionFactories(@Nullable Map sessionFactories) { if (sessionFactories != null) { this.sessionFactories.putAll(sessionFactories); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/SessionFactoryLookup.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/SessionFactoryLookup.java index 83403a6eb..919d7d614 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/SessionFactoryLookup.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/SessionFactoryLookup.java @@ -35,7 +35,7 @@ public interface SessionFactoryLookup { * their backing store. * * @param sessionFactoryName the name of the {@link SessionFactory}. - * @return the {@link SessionFactory} (never {@code null}). + * @return the {@link SessionFactory} (never {@literal null}). * @throws SessionFactoryLookupFailureException if the lookup failed. */ SessionFactory getSessionFactory(String sessionFactoryName) throws SessionFactoryLookupFailureException; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/package-info.java index 63632332f..1fb3f11e3 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/lookup/package-info.java @@ -1,6 +1,7 @@ /** * Provides a strategy for looking up {@link org.springframework.data.cassandra.SessionFactory}. - * - * @author Mark Paluch */ +@NonNullApi package org.springframework.data.cassandra.core.cql.session.lookup; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/package-info.java index 926b07f30..a23644ba3 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/session/package-info.java @@ -1,7 +1,8 @@ /** * Provides utility classes for simple {@link com.datastax.driver.core.Session} access and various session * implementations. - * - * @author Mark Paluch */ +@NonNullApi package org.springframework.data.cassandra.core.cql.session; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/support/CachedPreparedStatementCreator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/support/CachedPreparedStatementCreator.java index 16e511448..6e4244282 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/support/CachedPreparedStatementCreator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/support/CachedPreparedStatementCreator.java @@ -47,7 +47,7 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator * Create a new {@link CachedPreparedStatementCreator}. * * @param cache must not be {@literal null}. - * @param statement may be {@literal null} of {@code cql} is provided. + * @param statement must not be {@literal null}. */ protected CachedPreparedStatementCreator(PreparedStatementCache cache, RegularStatement statement) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/support/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/support/package-info.java new file mode 100644 index 000000000..5e96407c3 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/support/package-info.java @@ -0,0 +1,7 @@ +/** + * Support classes for the CQL framework. Provides prepared statement caching. + */ +@NonNullApi +package org.springframework.data.cassandra.core.cql.support; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntity.java index 92368f81d..525bb371f 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicCassandraPersistentEntity.java @@ -32,6 +32,7 @@ import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.util.TypeInformation; import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -52,9 +53,7 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity forceQuote = Optional.empty(); @@ -92,10 +91,6 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity String.format("Cannot determine default name for %s", this)); + + return cqlId(name, forceQuote); } /* (non-Javadoc) @@ -140,9 +139,7 @@ public class BasicCassandraPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity implements CassandraPersistentProperty, ApplicationContextAware { - private final UserTypeResolver userTypeResolver; + private final @Nullable UserTypeResolver userTypeResolver; - private StandardEvaluationContext spelContext; + private @Nullable StandardEvaluationContext spelContext; /** * Whether this property has been explicitly instructed to force quote column names. @@ -77,7 +78,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP /** * An unmodifiable list of this property's column names. */ - private CqlIdentifier columnName; + private @Nullable CqlIdentifier columnName; /** * Create a new {@link BasicCassandraPersistentProperty}. @@ -101,7 +102,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP * @param userTypeResolver resolver for user-defined types. */ public BasicCassandraPersistentProperty(Property property, CassandraPersistentEntity owner, - SimpleTypeHolder simpleTypeHolder, UserTypeResolver userTypeResolver) { + SimpleTypeHolder simpleTypeHolder, @Nullable UserTypeResolver userTypeResolver) { super(property, owner, simpleTypeHolder); @@ -140,12 +141,15 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP this.columnName = determineColumnName(); } + Assert.state(this.columnName != null, () -> String.format("Cannot determine column name for %s", this)); + return this.columnName; } /* (non-Javadoc) * @see org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty#getPrimaryKeyOrdering() */ + @Nullable @Override public Ordering getPrimaryKeyOrdering() { @@ -171,6 +175,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP return dataType; } + @Nullable private DataType findDataType() { CassandraType cassandraType = findAnnotation(CassandraType.class); @@ -213,20 +218,20 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP switch (type) { case MAP: ensureTypeArguments(annotation.typeArguments().length, 2); - return DataType.map(getDataTypeFor(annotation.typeArguments()[0]), - getDataTypeFor(annotation.typeArguments()[1])); + return DataType.map(CassandraSimpleTypeHolder.getDataTypeFor(annotation.typeArguments()[0]), + CassandraSimpleTypeHolder.getDataTypeFor(annotation.typeArguments()[1])); case LIST: ensureTypeArguments(annotation.typeArguments().length, 1); if (annotation.typeArguments()[0] == Name.UDT) { return DataType.list(getUserType(annotation)); } - return DataType.list(getDataTypeFor(annotation.typeArguments()[0])); + return DataType.list(CassandraSimpleTypeHolder.getDataTypeFor(annotation.typeArguments()[0])); case SET: ensureTypeArguments(annotation.typeArguments().length, 1); if (annotation.typeArguments()[0] == Name.UDT) { return DataType.set(getUserType(annotation)); } - return DataType.set(getDataTypeFor(annotation.typeArguments()[0])); + return DataType.set(CassandraSimpleTypeHolder.getDataTypeFor(annotation.typeArguments()[0])); case UDT: return getUserType(annotation); default: @@ -242,6 +247,8 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP getType(), getOwner().getName())); } + Assert.state(this.userTypeResolver != null, "UserTypeResolver is null"); + CqlIdentifier identifier = CqlIdentifier.cqlId(annotation.userTypeName()); UserType userType = this.userTypeResolver.resolveType(identifier); @@ -253,26 +260,13 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP return userType; } - private DataType getDataTypeFor(DataType.Name dataTypeName) { - - DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(dataTypeName); - - if (dataType == null) { - throw new InvalidDataAccessApiUsageException(String.format( - "Only primitive types are allowed inside Collections for property [%1$s] of type [%2$s] in entity [%3$s]", - getName(), getType(), getOwner().getName())); - } - - return dataType; - } - private DataType getDataTypeFor(Class javaType) { DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(javaType); if (dataType == null) { throw new InvalidDataAccessApiUsageException(String.format( - "Only primitive types are allowed inside Collections for property [%1$s] of type ['%2$s'] in entity [%3$s]", + "Only primitive types are allowed inside Collections for property [%1$s] of type ['%2$s'] in entity [%3$s]", getName(), getType(), getOwner().getName())); } @@ -281,9 +275,9 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP private void ensureTypeArguments(int args, int expected) { if (args != expected) { - throw new InvalidDataAccessApiUsageException(String.format( - "Expected [%1$s] typed arguments for property ['%2$s'] of type ['%3$s'] in entity [%4$s]", - expected, getName(), getType(), getOwner().getName())); + throw new InvalidDataAccessApiUsageException( + String.format("Expected [%1$s] typed arguments for property ['%2$s'] of type ['%3$s'] in entity [%4$s]", + expected, getName(), getType(), getOwner().getName())); } } @@ -325,6 +319,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP return (annotation != null && PrimaryKeyType.CLUSTERED.equals(annotation.type())); } + @Nullable private CqlIdentifier determineColumnName() { if (isCompositePrimaryKey()) { // then the id type has @PrimaryKeyClass @@ -367,7 +362,8 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP return createColumnName(defaultName, overriddenName, forceQuote); } - private CqlIdentifier createColumnName(String defaultName, String overriddenName, boolean forceQuote) { + @Nullable + private CqlIdentifier createColumnName(String defaultName, @Nullable String overriddenName, boolean forceQuote) { String name = defaultName; @@ -375,7 +371,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP name = (this.spelContext != null ? SpelUtils.evaluate(overriddenName, this.spelContext) : overriddenName); } - return cqlId(name, forceQuote); + return name == null ? null : cqlId(name, forceQuote); } /* (non-Javadoc) @@ -402,10 +398,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP if (changed) { CqlIdentifier columnName = getColumnName(); - - if (columnName != null) { - setColumnName(cqlId(columnName.getUnquoted(), forceQuote)); - } + setColumnName(cqlId(columnName.getUnquoted(), forceQuote)); } } @@ -422,7 +415,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP */ @Override protected Association createAssociation() { - return new Association<>(this, null); + return new Association<>(this, this); } /* (non-Javadoc) @@ -439,12 +432,11 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP @Override public AnnotatedType findAnnotatedType(Class annotationType) { - return Optionals.toStream(Optional.ofNullable(getField()).map(Field::getAnnotatedType), - Optional.ofNullable(getGetter()).map(Method::getAnnotatedReturnType), - Optional.ofNullable(getSetter()).map(it -> it.getParameters()[0].getAnnotatedType())) - .filter(it -> hasAnnotation(it, annotationType, getTypeInformation())) - .findFirst() - .orElse(null); + return Optionals + .toStream(Optional.ofNullable(getField()).map(Field::getAnnotatedType), + Optional.ofNullable(getGetter()).map(Method::getAnnotatedReturnType), + Optional.ofNullable(getSetter()).map(it -> it.getParameters()[0].getAnnotatedType())) + .filter(it -> hasAnnotation(it, annotationType, getTypeInformation())).findFirst().orElse(null); } private static boolean hasAnnotation(AnnotatedType type, Class annotationType, diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicMapId.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicMapId.java index d0d2a31bd..7a828acee 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicMapId.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/BasicMapId.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -84,7 +85,7 @@ public class BasicMapId implements MapId { * @see org.springframework.data.cassandra.core.mapping.MapId#with(java.lang.String, java.io.Object) */ @Override - public BasicMapId with(String name, Object value) { + public BasicMapId with(String name, @Nullable Object value) { put(name, value); return this; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java index 80308e5af..e3cb2aeb7 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java @@ -51,6 +51,7 @@ import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.Optionals; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -78,11 +79,11 @@ public class CassandraMappingContext private Mapping mapping = new Mapping(); - private UserTypeResolver userTypeResolver; + private @Nullable UserTypeResolver userTypeResolver; - private ApplicationContext context; + private @Nullable ApplicationContext context; - private ClassLoader beanClassLoader; + private @Nullable ClassLoader beanClassLoader; // useful caches private final Map>> entitySetsByTableName = new HashMap<>(); @@ -110,21 +111,19 @@ public class CassandraMappingContext private void processMappingOverrides() { - this.mapping.getEntityMappings().stream() - .filter(Objects::nonNull) - .forEach(entityMapping -> { + this.mapping.getEntityMappings().stream().filter(Objects::nonNull).forEach(entityMapping -> { - Class entityClass = getEntityClass(entityMapping.getEntityClassName()); - CassandraPersistentEntity entity = getRequiredPersistentEntity(entityClass); + Class entityClass = getEntityClass(entityMapping.getEntityClassName()); + CassandraPersistentEntity entity = getRequiredPersistentEntity(entityClass); - String entityTableName = entityMapping.getTableName(); + String entityTableName = entityMapping.getTableName(); - if (StringUtils.hasText(entityTableName)) { - entity.setTableName(cqlId(entityTableName, Boolean.valueOf(entityMapping.getForceQuote()))); - } + if (StringUtils.hasText(entityTableName)) { + entity.setTableName(cqlId(entityTableName, Boolean.valueOf(entityMapping.getForceQuote()))); + } - processMappingOverrides(entity, entityMapping); - }); + processMappingOverrides(entity, entityMapping); + }); } private Class getEntityClass(String entityClassName) { @@ -138,8 +137,8 @@ public class CassandraMappingContext private static void processMappingOverrides(CassandraPersistentEntity entity, EntityMapping entityMapping) { - entityMapping.getPropertyMappings().forEach((key, propertyMapping) -> - processMappingOverride(entity, propertyMapping)); + entityMapping.getPropertyMappings() + .forEach((key, propertyMapping) -> processMappingOverride(entity, propertyMapping)); } private static void processMappingOverride(CassandraPersistentEntity entity, PropertyMapping mapping) { @@ -292,6 +291,8 @@ public class CassandraMappingContext BasicCassandraPersistentEntity entity; if (userDefinedType != null) { + Assert.state(this.userTypeResolver != null, "UserTypeResolver must not be null"); + entity = new CassandraUserTypePersistentEntity<>(typeInformation, this.verifier, this.userTypeResolver); } else { @@ -351,12 +352,9 @@ public class CassandraMappingContext private boolean hasReferencedUserType(CqlIdentifier identifier) { - return getPersistentEntities().stream() - .flatMap(entity -> StreamSupport.stream(entity.spliterator(), false)) + return getPersistentEntities().stream().flatMap(entity -> StreamSupport.stream(entity.spliterator(), false)) .flatMap(it -> Optionals.toStream(Optional.ofNullable(it.findAnnotation(CassandraType.class)))) - .map(CassandraType::userTypeName) - .filter(StringUtils::hasText) - .map(CqlIdentifier::cqlId) + .map(CassandraType::userTypeName).filter(StringUtils::hasText).map(CqlIdentifier::cqlId) .anyMatch(identifier::equals); } @@ -373,7 +371,7 @@ public class CassandraMappingContext Assert.notNull(entity, "CassandraPersistentEntity must not be null"); - CreateTableSpecification specification = createTable().name(entity.getTableName()); + CreateTableSpecification specification = createTable(entity.getTableName()); for (CassandraPersistentProperty property : entity) { @@ -506,10 +504,8 @@ public class CassandraMappingContext } return this.customConversions.getCustomWriteTarget(property.getType()) - .map(CassandraSimpleTypeHolder::getDataTypeFor) - .orElseGet(() -> this.customConversions.getCustomWriteTarget(property.getActualType()) - .filter(it -> !property.isMapLike()) - .map(it -> { + .map(CassandraSimpleTypeHolder::getDataTypeFor).orElseGet(() -> this.customConversions + .getCustomWriteTarget(property.getActualType()).filter(it -> !property.isMapLike()).map(it -> { if (property.isCollectionLike()) { @@ -545,6 +541,7 @@ public class CassandraMappingContext return entity != null && entity.isUserDefinedType() ? dataTypeProvider.getDataType(entity) : getDataType(type); } + @Nullable private DataType getUserDataType(CassandraPersistentProperty property, DataTypeProvider dataTypeProvider, CassandraPersistentEntity persistentEntity) { @@ -580,8 +577,7 @@ public class CassandraMappingContext */ public DataType getDataType(Class type) { - return this.customConversions.getCustomWriteTarget(type) - .map(CassandraSimpleTypeHolder::getDataTypeFor) + return this.customConversions.getCustomWriteTarget(type).map(CassandraSimpleTypeHolder::getDataTypeFor) .orElseGet(() -> getDataTypeFor(type)); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraPersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraPersistentEntity.java index 122f8f2f0..79622521e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraPersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraPersistentEntity.java @@ -15,9 +15,9 @@ */ package org.springframework.data.cassandra.core.mapping; -import org.springframework.context.ApplicationContextAware; import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.data.mapping.PersistentEntity; +import org.springframework.lang.Nullable; import com.datastax.driver.core.UserType; @@ -28,8 +28,7 @@ import com.datastax.driver.core.UserType; * @author Matthew T. Adams * @author Mark Paluch */ -public interface CassandraPersistentEntity - extends PersistentEntity, ApplicationContextAware { +public interface CassandraPersistentEntity extends PersistentEntity { /** * Returns whether this entity represents a composite primary key. @@ -67,5 +66,6 @@ public interface CassandraPersistentEntity * @since 1.5 * @see UserDefinedType */ + @Nullable UserType getUserType(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraPersistentProperty.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraPersistentProperty.java index 880432a23..9adedac87 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraPersistentProperty.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraPersistentProperty.java @@ -23,6 +23,7 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.data.cassandra.core.cql.Ordering; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.lang.Nullable; import com.datastax.driver.core.DataType; @@ -47,6 +48,7 @@ public interface CassandraPersistentProperty * The ordering (ascending or descending) for the column. Valid only for primary key columns; returns null for * non-primary key columns. */ + @Nullable Ordering getPrimaryKeyOrdering(); /** @@ -114,5 +116,6 @@ public interface CassandraPersistentProperty * @return the annotated type or {@literal null}. * @since 2.0 */ + @Nullable AnnotatedType findAnnotatedType(Class annotationType); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraSimpleTypeHolder.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraSimpleTypeHolder.java index ba99ab294..e3a88f7d3 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraSimpleTypeHolder.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraSimpleTypeHolder.java @@ -24,6 +24,7 @@ import java.util.UUID; import java.util.stream.Collectors; import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.lang.Nullable; import com.datastax.driver.core.CodecRegistry; import com.datastax.driver.core.DataType; @@ -90,9 +91,7 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { Map nameToDataType = new HashMap<>(16); - DataType.allPrimitiveTypes().forEach(dataType -> { - nameToDataType.put(dataType.getName(), dataType); - }); + DataType.allPrimitiveTypes().forEach(dataType -> nameToDataType.put(dataType.getName(), dataType)); return nameToDataType; } @@ -159,7 +158,8 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { * @param javaClass must not be {@literal null}. * @return */ + @Nullable public static DataType getDataTypeFor(Class javaClass) { - return (javaClass.isEnum() ? DataType.varchar() : classToDataType.get(javaClass)); + return (javaClass.isEnum() ? DataType.varchar() : classToDataType.get(javaClass)); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraUserTypePersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraUserTypePersistentEntity.java index 0f091d6ce..4592158a9 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraUserTypePersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraUserTypePersistentEntity.java @@ -18,6 +18,7 @@ package org.springframework.data.cassandra.core.mapping; import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.data.mapping.MappingException; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.driver.core.UserType; @@ -36,7 +37,7 @@ public class CassandraUserTypePersistentEntity extends BasicCassandraPersiste private final Object lock = new Object(); - private volatile UserType userType; + private volatile @Nullable UserType userType; /** * Create a new {@link CassandraUserTypePersistentEntity}. diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/EntityMapping.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/EntityMapping.java index 3b071b860..e9eb9a235 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/EntityMapping.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/EntityMapping.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -38,7 +39,7 @@ public class EntityMapping { /** * The name of the entity's class. */ - private String entityClassName; + private @Nullable String entityClassName; /** * Whether to force the table name to be quoted. @@ -60,6 +61,7 @@ public class EntityMapping { setForceQuote(forceQuote); } + @Nullable public String getEntityClassName() { return entityClassName; } @@ -84,7 +86,7 @@ public class EntityMapping { return Collections.unmodifiableMap(propertyMappings); } - public void setPropertyMappings(Map propertyMappings) { + public void setPropertyMappings(@Nullable Map propertyMappings) { this.propertyMappings = (propertyMappings != null ? new HashMap<>(propertyMappings) : Collections.emptyMap()); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/IdInterfaceException.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/IdInterfaceException.java index 381e0a0cd..89ff77cf3 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/IdInterfaceException.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/IdInterfaceException.java @@ -18,6 +18,7 @@ package org.springframework.data.cassandra.core.mapping; import java.lang.reflect.Method; import org.springframework.data.mapping.MappingException; +import org.springframework.lang.Nullable; /** * Exception thrown on incorrect mapping of an Id interface. @@ -30,13 +31,14 @@ public class IdInterfaceException extends MappingException { private static final long serialVersionUID = -1635695314254522703L; private final String idInterfaceName; - private final String method; - public IdInterfaceException(Class idInterface, Method method, String message) { + private final @Nullable String method; + + public IdInterfaceException(Class idInterface, @Nullable Method method, String message) { this(idInterface.getClass().getName(), method == null ? null : method.toString(), message); } - public IdInterfaceException(String idInterfaceName, String method, String message) { + public IdInterfaceException(String idInterfaceName, @Nullable String method, String message) { super(message); this.idInterfaceName = idInterfaceName; this.method = method; @@ -46,6 +48,7 @@ public class IdInterfaceException extends MappingException { return idInterfaceName; } + @Nullable public String getMethod() { return method; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapId.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapId.java index 54bb36892..d0c3e7815 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapId.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapId.java @@ -17,6 +17,8 @@ package org.springframework.data.cassandra.core.mapping; import java.util.Map; +import org.springframework.lang.Nullable; + /** * Interface that represents the id of a persistent entity, where the keys correspond to the entity's JavaBean * properties. @@ -33,5 +35,5 @@ public interface MapId extends Map { * @param value The property value. * @return {@code this} */ - MapId with(String name, Object value); + MapId with(String name, @Nullable Object value); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapIdFactory.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapIdFactory.java index 022a58d6a..6b9db942b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapIdFactory.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapIdFactory.java @@ -59,6 +59,7 @@ public class MapIdFactory { * @see IdInterfaceValidator#validate(Class) */ public static T id(Class idInterface, ClassLoader loader) { + if (MapId.class.equals(idInterface)) { return (T) new BasicMapId(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapIdProxyDelegate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapIdProxyDelegate.java index 30311c657..e6906cbf7 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapIdProxyDelegate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/MapIdProxyDelegate.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; +import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** @@ -45,12 +46,14 @@ class MapIdProxyDelegate implements InvocationHandler { private MapId delegate = new BasicMapId(); private Class idInterface; - public MapIdProxyDelegate(Class idInterface) { + MapIdProxyDelegate(Class idInterface) { this.idInterface = idInterface; } + @Nullable @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (isMapIdMethod(method)) { return method.invoke(delegate, args); } @@ -69,11 +72,12 @@ class MapIdProxyDelegate implements InvocationHandler { return invokeGetter(method); } - public boolean isMapIdMethod(Method method) { + private boolean isMapIdMethod(Method method) { return MAP_ID_SIGNATURES.containsKey(new Signature(method, true)); } - public Object invokeGetter(Method method) { + private Object invokeGetter(Method method) { + String name = method.getName(); if (name.startsWith("get")) { if (name.length() == 3) { @@ -86,7 +90,8 @@ class MapIdProxyDelegate implements InvocationHandler { return delegate.get(name); } - public void invokeSetter(Method method, Object value) { + private void invokeSetter(Method method, @Nullable Object value) { + String name = method.getName(); int minLength = 1; boolean isSet = name.startsWith("set"); @@ -111,73 +116,74 @@ class MapIdProxyDelegate implements InvocationHandler { delegate.put(name, value); } -} -class Signature { - String name; - Class[] argTypes; - Class returnType; + static class Signature { + private String name; + private @Nullable Class[] argTypes; + private @Nullable Class returnType; - Signature(Method method, boolean includeReturnType) { - this(method.getName(), method.getParameterTypes(), includeReturnType ? method.getReturnType() : null); - } + Signature(Method method, boolean includeReturnType) { + this(method.getName(), method.getParameterTypes(), includeReturnType ? method.getReturnType() : null); + } - Signature(String name, Class[] argTypes, Class returnType) { - this.name = name; - this.argTypes = argTypes; - this.returnType = returnType; - } + Signature(String name, Class[] argTypes, @Nullable Class returnType) { - @Override - public String toString() { - return String.format("%s %s(%s)", returnType, name, Arrays.toString(argTypes)); - } + this.name = name; + this.argTypes = argTypes; + this.returnType = returnType; + } - @Override - public boolean equals(Object that) { - if (that == null) { - return false; + @Override + public String toString() { + return String.format("%s %s(%s)", returnType, name, Arrays.toString(argTypes)); } - if (this == that) { - return true; - } - if (!(that instanceof Signature)) { - return false; - } - Signature that_ = (Signature) that; - if (!this.name.equals(that_.name)) { - return false; - } - if ((this.argTypes == null && that_.argTypes != null) || (this.argTypes != null && that_.argTypes == null)) { - return false; - } - if (this.argTypes != null) { - if (this.argTypes.length != that_.argTypes.length) { + + @Override + public boolean equals(Object that) { + if (that == null) { return false; } - for (int i = 0; i < this.argTypes.length; i++) { - if (!this.argTypes[i].equals(that_.argTypes[i])) { + if (this == that) { + return true; + } + if (!(that instanceof Signature)) { + return false; + } + Signature that_ = (Signature) that; + if (!this.name.equals(that_.name)) { + return false; + } + if ((this.argTypes == null && that_.argTypes != null) || (this.argTypes != null && that_.argTypes == null)) { + return false; + } + if (this.argTypes != null) { + if (this.argTypes.length != that_.argTypes.length) { return false; } + for (int i = 0; i < this.argTypes.length; i++) { + if (!this.argTypes[i].equals(that_.argTypes[i])) { + return false; + } + } } + if (this.returnType == null) { + return that_.returnType == null; + } + return this.returnType.equals(that_.returnType); } - if (this.returnType == null) { - return that_.returnType == null; - } - return this.returnType.equals(that_.returnType); - } - @Override - public int hashCode() { - int hash = 37 ^ name.hashCode(); - if (argTypes != null) { - for (Class c : argTypes) { - hash ^= c.hashCode(); + @Override + public int hashCode() { + int hash = 37 ^ name.hashCode(); + if (argTypes != null) { + for (Class c : argTypes) { + hash ^= c.hashCode(); + } } + if (returnType != null) { + hash ^= returnType.hashCode(); + } + return hash; } - if (returnType != null) { - hash ^= returnType.hashCode(); - } - return hash; } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/Mapping.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/Mapping.java index c4f42ba92..48965dd6d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/Mapping.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/Mapping.java @@ -19,6 +19,8 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import org.springframework.lang.Nullable; + public class Mapping { private Set entityMappings = new HashSet<>(); @@ -27,7 +29,7 @@ public class Mapping { return Collections.unmodifiableSet(entityMappings); } - public void setEntityMappings(Set mappings) { + public void setEntityMappings(@Nullable Set mappings) { if (mappings == null) { entityMappings.clear(); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/PropertyMapping.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/PropertyMapping.java index 6128e9064..afd8083ed 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/PropertyMapping.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/PropertyMapping.java @@ -15,6 +15,7 @@ */ package org.springframework.data.cassandra.core.mapping; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -26,14 +27,17 @@ import org.springframework.util.ObjectUtils; */ public class PropertyMapping { - private String columnName; + private @Nullable String columnName; - private String forceQuote; + private @Nullable String forceQuote; private String propertyName; public PropertyMapping(String propertyName) { - setPropertyName(propertyName); + + Assert.notNull(propertyName, "Property name must not be null"); + + this.propertyName = propertyName; } public PropertyMapping(String propertyName, String columnName) { @@ -41,11 +45,15 @@ public class PropertyMapping { } public PropertyMapping(String propertyName, String columnName, String forceQuote) { - setPropertyName(propertyName); + + Assert.notNull(propertyName, "Property name must not be null"); + this.propertyName = propertyName; + setColumnName(columnName); setForceQuote(forceQuote); } + @Nullable public String getColumnName() { return columnName; } @@ -56,6 +64,7 @@ public class PropertyMapping { this.columnName = columnName; } + @Nullable public String getForceQuote() { return forceQuote; } @@ -68,12 +77,6 @@ public class PropertyMapping { return propertyName; } - public void setPropertyName(String propertyName) { - - Assert.notNull(propertyName, "Property name must not be null"); - this.propertyName = propertyName; - } - /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/SimpleUserTypeResolver.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/SimpleUserTypeResolver.java index 4fa4e86b5..e80f0eb36 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/SimpleUserTypeResolver.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/SimpleUserTypeResolver.java @@ -16,6 +16,7 @@ package org.springframework.data.cassandra.core.mapping; import org.springframework.data.cassandra.core.cql.CqlIdentifier; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.driver.core.Cluster; @@ -52,6 +53,7 @@ public class SimpleUserTypeResolver implements UserTypeResolver { /* (non-Javadoc) * @see org.springframework.data.cassandra.core.mapping.UserTypeResolver#resolveType(org.springframework.data.cassandra.core.cql.CqlIdentifier) */ + @Nullable @Override public UserType resolveType(CqlIdentifier typeName) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/UserTypeResolver.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/UserTypeResolver.java index 7bdddaf5d..ec5849ad2 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/UserTypeResolver.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/UserTypeResolver.java @@ -16,6 +16,7 @@ package org.springframework.data.cassandra.core.mapping; import org.springframework.data.cassandra.core.cql.CqlIdentifier; +import org.springframework.lang.Nullable; import com.datastax.driver.core.UserType; @@ -34,5 +35,6 @@ public interface UserTypeResolver { * @param typeName must not be {@literal null}. * @return the type or {@literal null}, if not found. */ + @Nullable UserType resolveType(CqlIdentifier typeName); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/package-info.java index e4495da63..39114460e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/package-info.java @@ -1,4 +1,7 @@ /** * Infrastructure for the Apache Cassandra row-to-object mapping subsystem. */ +@NonNullApi package org.springframework.data.cassandra.core.mapping; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/package-info.java index 63b7e7a95..b6bf72456 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/package-info.java @@ -1,4 +1,7 @@ /** * Apache Cassandra core support. */ +@NonNullApi package org.springframework.data.cassandra.core; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Columns.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Columns.java index 13d4ddb33..96c90d194 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Columns.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Columns.java @@ -185,7 +185,7 @@ public class Columns implements Iterable { * Returns a new {@link Columns} consisting of the {@link ColumnName}s of the current {@link Columns} combined with * the given ones. Existing {@link ColumnName}s are overwritten if specified within {@code columns}. * - * @param columns can be {@literal null}. + * @param columns must not be {@literal null}. * @return a new {@link Columns} with the merged result of the configured and given {@link Columns}. */ public Columns and(Columns columns) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Criteria.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Criteria.java index 897851ecf..e00cd3b47 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Criteria.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Criteria.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.Collection; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -34,7 +35,7 @@ public class Criteria implements CriteriaDefinition { private final ColumnName columnName; - private Predicate predicate; + private @Nullable Predicate predicate; private Criteria(ColumnName columnName, Predicate predicate) { @@ -82,10 +83,10 @@ public class Criteria implements CriteriaDefinition { /** * Create a criterion using equality. * - * @param value the value to match against. + * @param value the value to match against, may be {@literal null}. * @return {@literal this} {@link Criteria} object. */ - public CriteriaDefinition is(Object value) { + public CriteriaDefinition is(@Nullable Object value) { this.predicate = new Predicate(Operators.EQ, value); return this; @@ -231,6 +232,7 @@ public class Criteria implements CriteriaDefinition { /** * @return the {@link Predicate}. */ + @SuppressWarnings({ "ConstantConditions", "NullableProblems" }) // Predicate not accessible when in null state. public Predicate getPredicate() { return predicate; } @@ -256,8 +258,7 @@ public class Criteria implements CriteriaDefinition { protected boolean simpleCriteriaEquals(CriteriaDefinition left, CriteriaDefinition right) { - boolean keyEqual = left.getColumnName() == null ? right.getColumnName() == null - : left.getColumnName().equals(right.getColumnName()); + boolean keyEqual = left.getColumnName().equals(right.getColumnName()); boolean criteriaEqual = left.getPredicate().equals(right.getPredicate()); return keyEqual && criteriaEqual; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/CriteriaDefinition.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/CriteriaDefinition.java index 6a4902c48..334bd6f4b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/CriteriaDefinition.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/CriteriaDefinition.java @@ -17,6 +17,7 @@ package org.springframework.data.cassandra.core.query; import lombok.EqualsAndHashCode; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -51,7 +52,7 @@ public interface CriteriaDefinition { private final Operator operator; - private final Object value; + private final @Nullable Object value; /** * Create a new {@link Predicate} given {@code operator} and {@code value}. @@ -59,7 +60,7 @@ public interface CriteriaDefinition { * @param operator must not be {@literal null}. * @param value the match value. */ - public Predicate(Operator operator, Object value) { + public Predicate(Operator operator, @Nullable Object value) { Assert.notNull(operator, "Operator must not be null"); @@ -77,6 +78,7 @@ public interface CriteriaDefinition { /** * @return the match value. */ + @Nullable public Object getValue() { return value; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/SerializationUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/SerializationUtils.java index a12b5633a..8015c7c5c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/SerializationUtils.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/SerializationUtils.java @@ -22,6 +22,7 @@ import java.util.Set; import org.springframework.core.convert.converter.Converter; import org.springframework.data.cassandra.core.query.CriteriaDefinition.Operator; +import org.springframework.lang.Nullable; import com.datastax.driver.core.CodecRegistry; import com.datastax.driver.core.TypeCodec; @@ -44,10 +45,11 @@ abstract class SerializationUtils { * but falling back to the given object's {@link Object#toString()} method if it's not serializable. Useful for * printing raw {@link Criteria}s containing complex values before actually converting them into Mongo native types. * - * @param criteria - * @return + * @param criteria may be {@literal null}. + * @return may be {@literal null}. */ - public static String serializeToCqlSafely(CriteriaDefinition criteria) { + @Nullable + public static String serializeToCqlSafely(@Nullable CriteriaDefinition criteria) { if (criteria == null) { return null; @@ -65,10 +67,11 @@ abstract class SerializationUtils { * but falling back to the given object's {@link Object#toString()} method if it's not serializable. Useful for * printing raw {@link Criteria}s containing complex values before actually converting them into Mongo native types. * - * @param value value to serialize to CQL. - * @return the value as a serialized CQL {@link String}. + * @param value value to serialize to CQL, may be {@literal null}. + * @return the value as a serialized CQL {@link String}, may be {@literal null}. */ - public static String serializeToCqlSafely(Object value) { + @Nullable + public static String serializeToCqlSafely(@Nullable Object value) { if (value == null) { return null; @@ -89,7 +92,7 @@ abstract class SerializationUtils { } } - private static String serialize(Object value) { + private static String serialize(@Nullable Object value) { if (value == null) { return "null"; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Update.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Update.java index 752df716c..79a5334f0 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Update.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Update.java @@ -24,6 +24,7 @@ import java.util.LinkedHashMap; import java.util.Map; import org.springframework.data.cassandra.core.query.Update.AddToOp.Mode; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -65,7 +66,8 @@ public class Update { Assert.notNull(assignmentOps, "Update operations must not be null"); Map updateOperations = assignmentOps instanceof Collection - ? new LinkedHashMap<>(((Collection) assignmentOps).size()) : new LinkedHashMap<>(); + ? new LinkedHashMap<>(((Collection) assignmentOps).size()) + : new LinkedHashMap<>(); assignmentOps.forEach(assignmentOp -> updateOperations.put(assignmentOp.getColumnName(), assignmentOp)); @@ -85,11 +87,11 @@ public class Update { * Set the {@code columnName} to {@code value}. * * @param columnName must not be {@literal null}. - * @param value value to set on column with name. + * @param value value to set on column with name, may be {@literal null}. * @return a new {@link Update} object containing the merge result of the existing assignments and the current * assignment. */ - public Update set(String columnName, Object value) { + public Update set(String columnName, @Nullable Object value) { return add(new SetOp(ColumnName.from(columnName), value)); } @@ -550,14 +552,15 @@ public class Update { */ public static class SetOp extends AssignmentOp { - private final Object value; + private final @Nullable Object value; - public SetOp(ColumnName columnName, Object value) { + public SetOp(ColumnName columnName, @Nullable Object value) { super(columnName); this.value = value; } + @Nullable public Object getValue() { return value; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/package-info.java new file mode 100644 index 000000000..df963afe8 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/package-info.java @@ -0,0 +1,7 @@ +/** + * Apache Cassandra specific query and update support. + */ +@NonNullApi +package org.springframework.data.cassandra.core.query; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/package-info.java index 0779e28bb..154bb0875 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/package-info.java @@ -1,4 +1,7 @@ /** * Spring Data's Cassandra abstraction. */ +@NonNullApi package org.springframework.data.cassandra; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/Query.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/Query.java index ef1fd3269..b42addad0 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/Query.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/Query.java @@ -48,5 +48,4 @@ public @interface Query { * @since 2.0 */ boolean allowFiltering() default false; - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/CassandraRepositoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/CassandraRepositoryBean.java index ba184393f..a2976bb62 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/CassandraRepositoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/CassandraRepositoryBean.java @@ -27,6 +27,7 @@ import org.springframework.data.cassandra.core.CassandraOperations; import org.springframework.data.cassandra.repository.support.CassandraRepositoryFactory; import org.springframework.data.repository.cdi.CdiRepositoryBean; import org.springframework.data.repository.config.CustomRepositoryImplementationDetector; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -49,8 +50,8 @@ public class CassandraRepositoryBean extends CdiRepositoryBean { * implementations {@link CustomRepositoryImplementationDetector}, can be {@literal null}. */ public CassandraRepositoryBean(Bean operations, Set qualifiers, - Class repositoryType, BeanManager beanManager, CustomRepositoryImplementationDetector detector) { - super(qualifiers, repositoryType, beanManager, Optional.of(detector)); + Class repositoryType, BeanManager beanManager, @Nullable CustomRepositoryImplementationDetector detector) { + super(qualifiers, repositoryType, beanManager, Optional.ofNullable(detector)); Assert.notNull(operations, "Cannot create repository with 'null' for CassandraOperations."); this.cassandraOperationsBean = operations; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/CassandraRepositoryExtension.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/CassandraRepositoryExtension.java index 703ac0d57..7f4295e1f 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/CassandraRepositoryExtension.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/CassandraRepositoryExtension.java @@ -66,6 +66,7 @@ public class CassandraRepositoryExtension extends CdiRepositoryExtensionSupport * @param beanManager The BeanManager instance. */ void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) { + for (Map.Entry, Set> entry : getRepositoryTypes()) { Class repositoryType = entry.getKey(); @@ -95,5 +96,4 @@ public class CassandraRepositoryExtension extends CdiRepositoryExtensionSupport return new CassandraRepositoryBean<>(cassandraOperationsBean, qualifiers, repositoryType, beanManager, getCustomImplementationDetector()); } - } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/package-info.java index e4e61b1a9..5eb9bef58 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/cdi/package-info.java @@ -1,4 +1,7 @@ /** * CDI support for Apache Cassandra specific repository implementation. */ +@NonNullApi package org.springframework.data.cassandra.repository.cdi; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryNamespaceHandler.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryNamespaceHandler.java index 42051494a..be319aae8 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryNamespaceHandler.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryNamespaceHandler.java @@ -16,13 +16,13 @@ package org.springframework.data.cassandra.repository.config; import org.springframework.data.cassandra.config.CassandraNamespaceHandler; -import org.springframework.data.cassandra.repository.config.CassandraRepositoryConfigurationExtension; import org.springframework.data.repository.config.RepositoryBeanDefinitionParser; /** * Namespace handler for spring-data-cassandra. * * @author Oliver Gierke + * @author Mark Paluch */ class CassandraRepositoryNamespaceHandler extends CassandraNamespaceHandler { @@ -32,7 +32,7 @@ class CassandraRepositoryNamespaceHandler extends CassandraNamespaceHandler { */ @Override public void init() { - + super.init(); registerBeanDefinitionParser("repositories", diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/EnableCassandraRepositories.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/EnableCassandraRepositories.java index 0a975e28a..6462e428c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/EnableCassandraRepositories.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/EnableCassandraRepositories.java @@ -82,39 +82,30 @@ public @interface EnableCassandraRepositories { * Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So * for a repository named {@code UserRepository} the corresponding implementation class will be looked up scanning for * {@code UserRepositoryImpl}. - * - * @return */ String repositoryImplementationPostfix() default "Impl"; /** * Configures the location of where to find the Spring Data named queries properties file. Will default to * {@code META-INF/cassandra-named-queries.properties}. - * - * @return */ String namedQueriesLocation() default ""; /** * Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to * {@link Key#CREATE_IF_NOT_FOUND}. - * - * @return */ Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND; /** * Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to * {@link CassandraRepositoryFactoryBean}. - * - * @return */ Class repositoryFactoryBeanClass() default CassandraRepositoryFactoryBean.class; /** * Configure the repository base class to be used to create repository proxies for this particular configuration. * - * @return * @since 1.3 */ Class repositoryBaseClass() default DefaultRepositoryBaseClass.class; @@ -122,8 +113,6 @@ public @interface EnableCassandraRepositories { /** * Configures the name of the {@link CassandraTemplate} bean to be used with the repositories detected. Defaults to * {@link DefaultBeanNames#DATA_TEMPLATE} - * - * @return */ String cassandraTemplateRef() default "cassandraTemplate"; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/EnableReactiveCassandraRepositories.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/EnableReactiveCassandraRepositories.java index bf5275cad..4c31bc1eb 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/EnableReactiveCassandraRepositories.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/EnableReactiveCassandraRepositories.java @@ -80,47 +80,35 @@ public @interface EnableReactiveCassandraRepositories { * Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So * for a repository named {@code UserRepository} the corresponding implementation class will be looked up scanning for * {@code UserRepositoryImpl}. - * - * @return */ String repositoryImplementationPostfix() default "Impl"; /** * Configures the location of where to find the Spring Data named queries properties file. Will default to * {@code META-INF/cassandra-named-queries.properties}. - * - * @return */ String namedQueriesLocation() default ""; /** * Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to * {@link Key#CREATE_IF_NOT_FOUND}. - * - * @return */ Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND; /** * Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to * {@link ReactiveCassandraRepositoryFactoryBean}. - * - * @return */ Class repositoryFactoryBeanClass() default ReactiveCassandraRepositoryFactoryBean.class; /** * Configure the repository base class to be used to create repository proxies for this particular configuration. - * - * @return */ Class repositoryBaseClass() default DefaultRepositoryBaseClass.class; /** * Configures the name of the {@link org.springframework.data.cassandra.core.ReactiveCassandraTemplate} bean to be * used with the repositories detected. - * - * @return */ String reactiveCassandraTemplateRef() default "reactiveCassandraTemplate"; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/package-info.java index d4f69df5c..5ada97c02 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/package-info.java @@ -1,4 +1,7 @@ /** * Support infrastructure for the configuration of Apache Cassandra specific repositories. */ +@NonNullApi package org.springframework.data.cassandra.repository.config; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/package-info.java index 746fd0703..0ba7c1c98 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/package-info.java @@ -1,4 +1,7 @@ /** * Apache Cassandra specific repository implementation. */ +@NonNullApi package org.springframework.data.cassandra.repository; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractCassandraQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractCassandraQuery.java index 2ff9a7cc3..cc4a0664e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractCassandraQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractCassandraQuery.java @@ -44,6 +44,7 @@ import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -124,6 +125,7 @@ public abstract class AbstractCassandraQuery implements RepositoryQuery { /* (non-Javadoc) * @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[]) */ + @Nullable @Override public Object execute(Object[] parameters) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParameterAccessor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParameterAccessor.java index 6274bdfcc..7e97fcada 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParameterAccessor.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParameterAccessor.java @@ -17,6 +17,7 @@ package org.springframework.data.cassandra.repository.query; import org.springframework.data.cassandra.core.mapping.CassandraType; import org.springframework.data.repository.query.ParameterAccessor; +import org.springframework.lang.Nullable; import com.datastax.driver.core.DataType; @@ -50,6 +51,7 @@ public interface CassandraParameterAccessor extends ParameterAccessor { * @see org.springframework.data.cassandra.core.mapping.CassandraSimpleTypeHolder * @see org.springframework.data.cassandra.core.mapping.CassandraType */ + @Nullable CassandraType findCassandraType(int index); /** diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParameters.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParameters.java index 9efd716f2..a3c2abe01 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParameters.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParameters.java @@ -29,7 +29,7 @@ import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.util.QueryExecutionConverters; import org.springframework.data.repository.util.ReactiveWrappers; -import org.springframework.util.Assert; +import org.springframework.lang.Nullable; /** * Custom extension of {@link Parameters} discovering additional properties of query method parameters. @@ -75,7 +75,7 @@ public class CassandraParameters extends Parameters parameterType; protected CassandraParameter(MethodParameter parameter) { @@ -85,14 +85,7 @@ public class CassandraParameters extends Parameters T getAnnotation(Class annotationClass) { return methodParameter.getParameterAnnotation(annotationClass); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParametersParameterAccessor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParametersParameterAccessor.java index 7aba40b7f..9d949ed5f 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParametersParameterAccessor.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraParametersParameterAccessor.java @@ -22,6 +22,7 @@ import org.springframework.data.cassandra.core.mapping.CassandraSimpleTypeHolder import org.springframework.data.cassandra.core.mapping.CassandraType; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.lang.Nullable; import com.datastax.driver.core.DataType; @@ -54,6 +55,7 @@ public class CassandraParametersParameterAccessor extends ParametersParameterAcc * (non-Javadoc) * @see org.springframework.data.cassandra.repository.query.CassandraParameterAccessor#findCassandraType(int) */ + @Nullable public CassandraType findCassandraType(int index) { return getParameters().getParameter(index).getCassandraType(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryCreator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryCreator.java index 6cc475880..09c3aad39 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryCreator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryCreator.java @@ -104,6 +104,8 @@ class CassandraQueryCreator extends AbstractQueryCreator type); /** @@ -132,9 +134,13 @@ interface CassandraQueryExecution { /* (non-Javadoc) * @see org.springframework.data.cassandra.repository.query.CassandraQueryExecution#execute(java.lang.String, java.lang.Class) */ + @Nullable @Override public Object execute(Statement statement, Class type) { - return converter.convert(delegate.execute(statement, type)); + + Object result = delegate.execute(statement, type); + + return result != null ? converter.convert(result) : null; } } @@ -154,7 +160,7 @@ interface CassandraQueryExecution { * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) */ @Override - public Object convert(Object source) { + public Object convert(@Nullable Object source) { ReturnedType returnedType = processor.getReturnedType(); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryMethod.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryMethod.java index b04454030..7ef27b58c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryMethod.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryMethod.java @@ -29,6 +29,7 @@ import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -51,7 +52,7 @@ public class CassandraQueryMethod extends QueryMethod { private final Optional query; - private CassandraEntityMetadata entityMetadata; + private @Nullable CassandraEntityMetadata entityMetadata; /** * Create a new {@link CassandraQueryMethod} from the given {@link Method}. @@ -148,16 +149,29 @@ public class CassandraQueryMethod extends QueryMethod { * Returns the query string declared in a {@link Query} annotation or {@literal null} if neither the annotation found * nor the attribute was specified. * - * @return + * @return the query string or {@literal null} if no query string present. */ + @Nullable public String getAnnotatedQuery() { return query.map(Query::value).orElse(null); } /** - * Returns the {@link Query} annotation that is applied to the method or {@code null} if none available. + * Returns the required query string declared in a {@link Query} annotation or throws {@link IllegalStateException} if + * neither the annotation found nor the attribute was specified. * - * @return + * @return the query string. + * @throws IllegalStateException in case query method has no annotated query. + */ + public String getRequiredAnnotatedQuery() { + return query.map(Query::value) + .orElseThrow(() -> new IllegalStateException("Query method " + this + " has no annotated query")); + } + + /** + * Returns the {@link Query} annotation that is applied to the method or {@literal null} if none available. + * + * @return the optional query annotation. */ Optional getQueryAnnotation() { return query; @@ -179,6 +193,8 @@ public class CassandraQueryMethod extends QueryMethod { * @return true is the method returns a {@link ResultSet}. */ public boolean isResultSetQuery() { - return ResultSet.class.isAssignableFrom(getReturnType().getActualType().getType()); + + TypeInformation actualType = getReturnType().getActualType(); + return actualType != null && ResultSet.class.isAssignableFrom(actualType.getType()); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ConvertingParameterAccessor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ConvertingParameterAccessor.java index 0a5b5ae5b..e385ea931 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ConvertingParameterAccessor.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ConvertingParameterAccessor.java @@ -28,6 +28,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import com.datastax.driver.core.CodecRegistry; import com.datastax.driver.core.DataType; @@ -100,10 +101,7 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor { */ @Override public DataType getDataType(int index) { - - DataType dataType = delegate.getDataType(index); - - return (dataType != null ? dataType : converter.getMappingContext().getDataType(getParameterType(index))); + return delegate.getDataType(index); } /* (non-Javadoc) @@ -138,7 +136,8 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor { } @SuppressWarnings("unchecked") - private Object potentiallyConvert(int index, Object bindableValue) { + @Nullable + private Object potentiallyConvert(int index, @Nullable Object bindableValue) { if (bindableValue == null) { return null; @@ -148,14 +147,15 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor { } @SuppressWarnings("unchecked") - private Object potentiallyConvert(int index, Object bindableValue, CassandraPersistentProperty property) { + @Nullable + private Object potentiallyConvert(int index, @Nullable Object bindableValue, CassandraPersistentProperty property) { return (bindableValue == null ? null : converter.convertToColumnType(bindableValue, findTypeInformation(index, bindableValue, property))); } private TypeInformation findTypeInformation(int index, Object bindableValue, - CassandraPersistentProperty property) { + @Nullable CassandraPersistentProperty property) { if (delegate.findCassandraType(index) != null) { TypeCodec typeCodec = CodecRegistry.DEFAULT_INSTANCE.codecFor(getDataType(index, property)); @@ -182,7 +182,7 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor { * @param property {@link CassandraPersistentProperty}. * @return the {@link DataType} */ - DataType getDataType(int index, CassandraPersistentProperty property) { + DataType getDataType(int index, @Nullable CassandraPersistentProperty property) { CassandraType cassandraType = delegate.findCassandraType(index); @@ -268,6 +268,7 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor { * (non-Javadoc) * @see java.util.Iterator#next() */ + @Nullable public Object next() { return potentiallyConvert(index++, delegate.next()); } @@ -283,6 +284,7 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor { /* (non-Javadoc) * @see org.springframework.data.cassandra.repository.query.ConvertingParameterAccessor.PotentiallyConvertingIterator#nextConverted(org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty) */ + @Nullable @Override public Object nextConverted(CassandraPersistentProperty property) { return potentiallyConvert(index++, delegate.next(), property); @@ -301,7 +303,7 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor { * * @return the converted object, may be {@literal null}. */ + @Nullable Object nextConverted(CassandraPersistentProperty property); - } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/DtoInstantiatingConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/DtoInstantiatingConverter.java index 75da7ae54..e1944b9d4 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/DtoInstantiatingConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/DtoInstantiatingConverter.java @@ -28,6 +28,7 @@ import org.springframework.data.mapping.PreferredConstructor.Parameter; import org.springframework.data.mapping.SimplePropertyHandler; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.ParameterValueProvider; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -82,14 +83,14 @@ class DtoInstantiatingConverter implements Converter { Object dto = instantiator.createInstance(targetEntity, new ParameterValueProvider() { @Override + @Nullable public Object getParameterValue(Parameter parameter) { - // TODO: Fix generics - if (parameter != null) { - return sourceAccessor.getProperty(sourceEntity.getRequiredPersistentProperty(parameter.getName())); + if (parameter.getName() == null) { + throw new IllegalArgumentException(String.format("Parameter %s does not have a name", parameter)); } - return null; + return sourceAccessor.getProperty(sourceEntity.getRequiredPersistentProperty(parameter.getName())); } }); @@ -99,7 +100,7 @@ class DtoInstantiatingConverter implements Converter { targetEntity.doWithProperties((SimplePropertyHandler) property -> { - if (!constructor.isConstructorParameter(property)) { + if (constructor != null && !constructor.isConstructorParameter(property)) { return; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ExpressionEvaluatingParameterBinder.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ExpressionEvaluatingParameterBinder.java index 49658d290..b2ab2c7b1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ExpressionEvaluatingParameterBinder.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ExpressionEvaluatingParameterBinder.java @@ -24,6 +24,7 @@ import org.springframework.data.repository.query.Parameter; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -46,7 +47,7 @@ class ExpressionEvaluatingParameterBinder { * @param expressionParser must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}. */ - public ExpressionEvaluatingParameterBinder(SpelExpressionParser expressionParser, + ExpressionEvaluatingParameterBinder(SpelExpressionParser expressionParser, EvaluationContextProvider evaluationContextProvider) { Assert.notNull(expressionParser, "ExpressionParser must not be null"); @@ -88,6 +89,7 @@ class ExpressionEvaluatingParameterBinder { * @param binding must not be {@literal null}. * @return the value used for the given {@link ParameterBinding}. */ + @Nullable private Object getParameterValueForBinding(CassandraParameterAccessor parameterAccessor, CassandraParameters parameters, ParameterBinding binding) { @@ -120,6 +122,7 @@ class ExpressionEvaluatingParameterBinder { * @param parameterValues must not be {@literal null}. * @return the value of the {@code expressionString} evaluation. */ + @Nullable private Object evaluateExpression(String expressionString, CassandraParameters parameters, Object[] parameterValues) { EvaluationContext evaluationContext = evaluationContextProvider.getEvaluationContext(parameters, parameterValues); @@ -162,7 +165,7 @@ class ExpressionEvaluatingParameterBinder { * * @return never {@literal null}. */ - public List getBindings() { + List getBindings() { return Collections.unmodifiableList(bindings); } @@ -171,7 +174,7 @@ class ExpressionEvaluatingParameterBinder { * * @return the {@link CassandraParameters} associated with the {@link CassandraQueryMethod}. */ - public CassandraParameters getParameters() { + CassandraParameters getParameters() { return queryMethod.getParameters(); } @@ -180,7 +183,7 @@ class ExpressionEvaluatingParameterBinder { * * @return the {@link CassandraQueryMethod} used in the expression evaluation context. */ - public CassandraQueryMethod getQueryMethod() { + CassandraQueryMethod getQueryMethod() { return queryMethod; } } @@ -194,49 +197,52 @@ class ExpressionEvaluatingParameterBinder { private final boolean quoted; private final int parameterIndex; - private final String expression; - private final String parameterName; + private final @Nullable String expression; + private final @Nullable String parameterName; - private ParameterBinding(int parameterIndex, boolean quoted, String expression, String parameterName) { + private ParameterBinding(int parameterIndex, boolean quoted, @Nullable String expression, + @Nullable String parameterName) { this.parameterIndex = parameterIndex; this.quoted = quoted; this.expression = expression; this.parameterName = parameterName; } - public static ParameterBinding expression(String expression, boolean quoted) { + static ParameterBinding expression(String expression, boolean quoted) { return new ParameterBinding(-1, quoted, expression, null); } - public static ParameterBinding indexed(int parameterIndex) { + static ParameterBinding indexed(int parameterIndex) { return new ParameterBinding(parameterIndex, false, null, null); } - public static ParameterBinding named(String name) { + static ParameterBinding named(String name) { return new ParameterBinding(-1, false, null, name); } - public boolean isNamed() { + boolean isNamed() { return (parameterName != null); } - public int getParameterIndex() { + int getParameterIndex() { return parameterIndex; } - public String getParameter() { + String getParameter() { return ("?" + (isExpression() ? "expr" : "") + parameterIndex); } - public String getExpression() { + @Nullable + String getExpression() { return expression; } - public boolean isExpression() { + boolean isExpression() { return (this.expression != null); } - public String getParameterName() { + @Nullable + String getParameterName() { return parameterName; } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveCassandraParameterAccessor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveCassandraParameterAccessor.java index fe7dad3a0..0c85507fd 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveCassandraParameterAccessor.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveCassandraParameterAccessor.java @@ -38,7 +38,8 @@ class ReactiveCassandraParameterAccessor extends CassandraParametersParameterAcc private final List> subscriptions; - public ReactiveCassandraParameterAccessor(CassandraQueryMethod method, Object[] values) { + @SuppressWarnings("ConstantConditions") + ReactiveCassandraParameterAccessor(CassandraQueryMethod method, Object[] values) { super(method, values); @@ -62,7 +63,7 @@ class ReactiveCassandraParameterAccessor extends CassandraParametersParameterAcc /* (non-Javadoc) * @see org.springframework.data.repository.query.ParametersParameterAccessor#getValue(int) */ - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "ConstantConditions" }) @Override protected T getValue(int index) { return (subscriptions.get(index) != null ? (T) subscriptions.get(index).block() : super.getValue(index)); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveCassandraQueryExecution.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveCassandraQueryExecution.java index d4327227c..bcb90ce1e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveCassandraQueryExecution.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveCassandraQueryExecution.java @@ -97,6 +97,7 @@ interface ReactiveCassandraQueryExecution { * (non-Javadoc) * @see org.springframework.data.cassandra.repository.query.ReactiveCassandraQueryExecution#execute(java.lang.String, java.lang.Class) */ + @SuppressWarnings("ConstantConditions") @Override public Object execute(Statement statement, Class type) { return converter.convert(delegate.execute(statement, type)); @@ -128,7 +129,7 @@ interface ReactiveCassandraQueryExecution { return source; } - if (source != null && returnedType.isInstance(source)) { + if (returnedType.isInstance(source)) { return source; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveStringBasedCassandraQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveStringBasedCassandraQuery.java index 5a288e21d..35a83dfd2 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveStringBasedCassandraQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveStringBasedCassandraQuery.java @@ -56,7 +56,7 @@ public class ReactiveStringBasedCassandraQuery extends AbstractReactiveCassandra ReactiveCassandraOperations operations, SpelExpressionParser expressionParser, EvaluationContextProvider evaluationContextProvider) { - this(queryMethod.getAnnotatedQuery(), queryMethod, operations, expressionParser, evaluationContextProvider); + this(queryMethod.getRequiredAnnotatedQuery(), queryMethod, operations, expressionParser, evaluationContextProvider); } /** diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/SimpleCassandraEntityMetadata.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/SimpleCassandraEntityMetadata.java index 7e26f0cdd..c0762589c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/SimpleCassandraEntityMetadata.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/SimpleCassandraEntityMetadata.java @@ -38,7 +38,7 @@ class SimpleCassandraEntityMetadata implements CassandraEntityMetadata { * @param type must not be {@literal null}. * @param entity must not be {@literal null} or empty. */ - public SimpleCassandraEntityMetadata(Class type, CassandraPersistentEntity entity) { + SimpleCassandraEntityMetadata(Class type, CassandraPersistentEntity entity) { Assert.notNull(type, "Type must not be null"); Assert.notNull(entity, "Collection entity must not be null or empty"); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQuery.java index 4771892df..33dc874a1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQuery.java @@ -54,7 +54,7 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery { public StringBasedCassandraQuery(CassandraQueryMethod queryMethod, CassandraOperations operations, SpelExpressionParser expressionParser, EvaluationContextProvider evaluationContextProvider) { - this(queryMethod.getAnnotatedQuery(), queryMethod, operations, expressionParser, evaluationContextProvider); + this(queryMethod.getRequiredAnnotatedQuery(), queryMethod, operations, expressionParser, evaluationContextProvider); } /** diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedQuery.java index 616ae7781..381262cad 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedQuery.java @@ -23,6 +23,7 @@ import java.util.regex.Pattern; import org.springframework.data.cassandra.repository.query.ExpressionEvaluatingParameterBinder.BindingContext; import org.springframework.data.cassandra.repository.query.ExpressionEvaluatingParameterBinder.ParameterBinding; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -50,7 +51,7 @@ class StringBasedQuery { * @param query must not be empty. * @param parameterBinder must not be {@literal null}. */ - public StringBasedQuery(String query, ExpressionEvaluatingParameterBinder parameterBinder) { + StringBasedQuery(String query, ExpressionEvaluatingParameterBinder parameterBinder) { Assert.hasText(query, "Query must not be empty"); Assert.notNull(parameterBinder, "ExpressionEvaluatingParameterBinder must not be null"); @@ -62,8 +63,7 @@ class StringBasedQuery { } - /* (non-Javadoc) */ - protected ExpressionEvaluatingParameterBinder getParameterBinder() { + private ExpressionEvaluatingParameterBinder getParameterBinder() { return this.parameterBinder; } @@ -79,7 +79,7 @@ class StringBasedQuery { * @param queryMethod must not be {@literal null}. * @return the bound String query containing formatted parameters. */ - public SimpleStatement bindQuery(CassandraParameterAccessor parameterAccessor, CassandraQueryMethod queryMethod) { + SimpleStatement bindQuery(CassandraParameterAccessor parameterAccessor, CassandraQueryMethod queryMethod) { Assert.notNull(parameterAccessor, "CassandraParameterAccessor must not be null"); Assert.notNull(queryMethod, "CassandraQueryMethod must not be null"); @@ -236,6 +236,7 @@ class StringBasedQuery { return result.append(input.subSequence(currentPosition, input.length())).toString(); } + @Nullable private static Matcher findNextBindingOrExpression(String input, int position) { List matchers = new ArrayList<>(); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/UnsupportedCassandraQueryOperatorException.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/UnsupportedCassandraQueryOperatorException.java deleted file mode 100644 index ef2475f13..000000000 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/UnsupportedCassandraQueryOperatorException.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.springframework.data.cassandra.repository.query; - -import org.springframework.dao.InvalidDataAccessApiUsageException; - -public class UnsupportedCassandraQueryOperatorException extends InvalidDataAccessApiUsageException { - - public UnsupportedCassandraQueryOperatorException(String msg) { - super(msg); - // TODO Auto-generated constructor stub - } - - public UnsupportedCassandraQueryOperatorException(String msg, Throwable cause) { - super(msg, cause); - // TODO Auto-generated constructor stub - } - -} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/package-info.java index f56efb649..af4c4c3ba 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/package-info.java @@ -1,4 +1,7 @@ /** * Query derivation mechanism for Apache Cassandra specific repositories. */ +@NonNullApi package org.springframework.data.cassandra.repository.query; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/CassandraRepositoryFactory.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/CassandraRepositoryFactory.java index 7940dcb33..663eb6a62 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/CassandraRepositoryFactory.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/CassandraRepositoryFactory.java @@ -37,6 +37,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -105,7 +106,7 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport { * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(Key, EvaluationContextProvider) */ @Override - protected Optional getQueryLookupStrategy(Key key, + protected Optional getQueryLookupStrategy(@Nullable Key key, EvaluationContextProvider evaluationContextProvider) { return Optional.of(new CassandraQueryLookupStrategy(operations, evaluationContextProvider, mappingContext)); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/CassandraRepositoryFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/CassandraRepositoryFactoryBean.java index 13a4384a7..c7de34a4b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/CassandraRepositoryFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/CassandraRepositoryFactoryBean.java @@ -15,11 +15,13 @@ */ package org.springframework.data.cassandra.repository.support; +import org.springframework.data.cassandra.core.CassandraOperations; import org.springframework.data.cassandra.core.CassandraTemplate; import org.springframework.data.cassandra.repository.CassandraRepository; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactorySupport; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -33,7 +35,7 @@ import org.springframework.util.Assert; public class CassandraRepositoryFactoryBean, S, ID> extends RepositoryFactoryBeanSupport { - private CassandraTemplate cassandraTemplate; + private @Nullable CassandraOperations cassandraOperations; /** * Create a new {@link CassandraRepositoryFactoryBean} for the given repository interface. @@ -46,7 +48,10 @@ public class CassandraRepositoryFactoryBean, S, ID> @Override protected RepositoryFactorySupport createRepositoryFactory() { - return new CassandraRepositoryFactory(cassandraTemplate); + + Assert.state(cassandraOperations != null, "CassandraOperations must not be null"); + + return new CassandraRepositoryFactory(cassandraOperations); } /** @@ -56,7 +61,8 @@ public class CassandraRepositoryFactoryBean, S, ID> * on Apache Cassandra. */ public void setCassandraTemplate(CassandraTemplate cassandraTemplate) { - this.cassandraTemplate = cassandraTemplate; + + this.cassandraOperations = cassandraTemplate; setMappingContext(cassandraTemplate.getConverter().getMappingContext()); } @@ -68,6 +74,6 @@ public class CassandraRepositoryFactoryBean, S, ID> super.afterPropertiesSet(); - Assert.notNull(cassandraTemplate, "CassandraTemplate must not be null!"); + Assert.notNull(cassandraOperations, "CassandraOperations must not be null!"); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java index f38b79fa9..826acd0ff 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/MappingCassandraEntityInformation.java @@ -22,6 +22,7 @@ import org.springframework.data.cassandra.core.mapping.CassandraPersistentProper import org.springframework.data.cassandra.core.mapping.MapId; import org.springframework.data.cassandra.repository.query.CassandraEntityInformation; import org.springframework.data.repository.core.support.AbstractEntityInformation; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -57,16 +58,15 @@ public class MappingCassandraEntityInformation extends AbstractEntityInfo */ @SuppressWarnings("unchecked") @Override + @Nullable public ID getId(T entity) { Assert.notNull(entity, "Entity must not be null"); CassandraPersistentProperty idProperty = entityMetadata.getIdProperty(); - if (idProperty != null) { - return (ID) entityMetadata.getIdentifierAccessor(entity).getIdentifier(); - } - return (ID) converter.getId(entity, entityMetadata); + return idProperty != null ? (ID) entityMetadata.getIdentifierAccessor(entity).getIdentifier() + : (ID) converter.getId(entity, entityMetadata); } /* (non-Javadoc) @@ -77,7 +77,7 @@ public class MappingCassandraEntityInformation extends AbstractEntityInfo public Class getIdType() { if (entityMetadata.getIdProperty() != null) { - return (Class) entityMetadata.getIdProperty().getType(); + return (Class) entityMetadata.getRequiredIdProperty().getType(); } return (Class) MapId.class; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactory.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactory.java index ba8fce999..18fcee77f 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactory.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactory.java @@ -36,6 +36,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -88,7 +89,7 @@ public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactor * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider) */ @Override - protected Optional getQueryLookupStrategy(Key key, + protected Optional getQueryLookupStrategy(@Nullable Key key, EvaluationContextProvider evaluationContextProvider) { return Optional.of(new CassandraQueryLookupStrategy(operations, evaluationContextProvider, mappingContext)); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactoryBean.java index 53cb3c7e8..3a3d7b210 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactoryBean.java @@ -20,6 +20,7 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactorySupport; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -29,14 +30,14 @@ import org.springframework.util.Assert; * @author Mark Paluch * @since 2.0 * @see org.springframework.data.repository.reactive.ReactiveSortingRepository - * @see org.springframework.data.repository.reactive.RxJava1SortingRepository + * @see org.springframework.data.repository.reactive.RxJava2SortingRepository */ public class ReactiveCassandraRepositoryFactoryBean, S, ID> extends RepositoryFactoryBeanSupport { private boolean mappingContextConfigured = false; - private ReactiveCassandraOperations operations; + private @Nullable ReactiveCassandraOperations operations; /** * Create a new {@link ReactiveCassandraRepositoryFactoryBean} for the given repository interface. @@ -73,6 +74,9 @@ public class ReactiveCassandraRepositoryFactoryBean, */ @Override protected final RepositoryFactorySupport createRepositoryFactory() { + + Assert.state(operations != null, "ReactiveCassandraOperations must not be null"); + return getFactoryInstance(operations); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/package-info.java index 8c0a91ed0..f59e986f2 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/package-info.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/package-info.java @@ -1,4 +1,7 @@ /** * Support infrastructure for query derivation of Apache Cassandra specific repositories. */ +@NonNullApi package org.springframework.data.cassandra.repository.support; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/MapBuilder.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/MapBuilder.java index cea42c691..bfc82b6a8 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/MapBuilder.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/MapBuilder.java @@ -21,6 +21,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -34,7 +35,7 @@ import org.springframework.util.Assert; */ public class MapBuilder implements Map { - private Map map; + private final Map map; /** * Create a new {@link MapBuilder}. @@ -131,6 +132,7 @@ public class MapBuilder implements Map { * @see java.util.Map#get(java.lang.Object) */ @Override + @Nullable public V get(Object key) { return map.get(key); } @@ -147,6 +149,7 @@ public class MapBuilder implements Map { * @see java.util.Map#remove(java.lang.Object) */ @Override + @Nullable public V remove(Object key) { return map.remove(key); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/SpelUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/SpelUtils.java index f371a7915..5cd0a75e3 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/SpelUtils.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/SpelUtils.java @@ -1,3 +1,18 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.data.cassandra.util; import org.springframework.expression.EvaluationContext; @@ -5,7 +20,13 @@ import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; +/** + * Evaluates a SpEL expression. + * + * @author Mark Paluch + */ public class SpelUtils { public static final SpelExpressionParser DEFAULT_PARSER = new SpelExpressionParser(); @@ -13,6 +34,7 @@ public class SpelUtils { /** * Evaluates the given value against the given context as a string. */ + @Nullable public static String evaluate(CharSequence value, EvaluationContext context) { return evaluate(value, context, DEFAULT_PARSER); } @@ -20,6 +42,7 @@ public class SpelUtils { /** * Evaluates the given value against the given context as a string using the given parser. */ + @Nullable public static String evaluate(CharSequence value, EvaluationContext context, ExpressionParser parser) { return evaluate(value, context, String.class, parser); } @@ -27,6 +50,7 @@ public class SpelUtils { /** * Evaluates the given value against the given context as an object of the given class. */ + @Nullable public static T evaluate(CharSequence value, EvaluationContext context, Class clazz) { return evaluate(value, context, clazz, DEFAULT_PARSER); } @@ -34,7 +58,9 @@ public class SpelUtils { /** * Evaluates the given value against the given context as an object of the given class using the given parser. */ + @Nullable public static T evaluate(CharSequence value, EvaluationContext context, Class clazz, ExpressionParser parser) { + Expression expression = parser.parseExpression(value.toString(), ParserContext.TEMPLATE_EXPRESSION); return expression.getValue(context, clazz); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/package-info.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/package-info.java new file mode 100644 index 000000000..3da2bd2e0 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/util/package-info.java @@ -0,0 +1,7 @@ +/** + * Map and SpEL utility infrastructure. + */ +@NonNullApi +package org.springframework.data.cassandra.util; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/AbstractClusterConfigurationUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/AbstractClusterConfigurationUnitTests.java index 76a512c77..1ac93ba69 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/AbstractClusterConfigurationUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/AbstractClusterConfigurationUnitTests.java @@ -214,8 +214,8 @@ public class AbstractClusterConfigurationUnitTests { @Test // DATACASS-226 public void shouldSetKeyspaceCreations() { - final List specification = Collections - .singletonList(CreateKeyspaceSpecification.createKeyspace()); + List specification = Collections + .singletonList(CreateKeyspaceSpecification.createKeyspace("foo")); AbstractClusterConfiguration clusterConfiguration = new AbstractClusterConfiguration() { @Override protected List getKeyspaceCreations() { @@ -229,8 +229,8 @@ public class AbstractClusterConfigurationUnitTests { @Test // DATACASS-226 public void shouldSetKeyspaceDrops() { - final List specification = Collections - .singletonList(DropKeyspaceSpecification.dropKeyspace()); + List specification = Collections + .singletonList(DropKeyspaceSpecification.dropKeyspace("foo")); AbstractClusterConfiguration clusterConfiguration = new AbstractClusterConfiguration() { @Override protected List getKeyspaceDrops() { @@ -244,7 +244,7 @@ public class AbstractClusterConfigurationUnitTests { @Test // DATACASS-226 public void shouldSetStartupScripts() { - final List scripts = Collections.singletonList("USE BLUE_METH; CREATE TABLE..."); + List scripts = Collections.singletonList("USE BLUE_METH; CREATE TABLE..."); AbstractClusterConfiguration clusterConfiguration = new AbstractClusterConfiguration() { @Override protected List getStartupScripts() { @@ -258,7 +258,7 @@ public class AbstractClusterConfigurationUnitTests { @Test // DATACASS-226 public void shouldSetShutdownScripts() { - final List scripts = Collections.singletonList("USE BLUE_METH; DROP TABLE..."); + List scripts = Collections.singletonList("USE BLUE_METH; DROP TABLE..."); AbstractClusterConfiguration clusterConfiguration = new AbstractClusterConfiguration() { @Override protected List getShutdownScripts() { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/KeyspaceCreatingJavaConfigIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/KeyspaceCreatingJavaConfigIntegrationTests.java index 0902cb933..93af5bc89 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/KeyspaceCreatingJavaConfigIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/config/KeyspaceCreatingJavaConfigIntegrationTests.java @@ -72,7 +72,7 @@ public class KeyspaceCreatingJavaConfigIntegrationTests extends AbstractKeyspace protected List getKeyspaceCreations() { ArrayList list = new ArrayList<>(); - CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace().name(getKeyspaceName()); + CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace(getKeyspaceName()); specification.with(KeyspaceOption.REPLICATION, KeyspaceAttributes.newSimpleReplication(1L)); list.add(specification); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/InsertOptionsUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/InsertOptionsUnitTests.java index 736909e85..fb06f5b36 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/InsertOptionsUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/InsertOptionsUnitTests.java @@ -17,6 +17,8 @@ package org.springframework.data.cassandra.core; import static org.assertj.core.api.Assertions.*; +import java.time.Duration; + import org.junit.Test; /** @@ -34,7 +36,7 @@ public class InsertOptionsUnitTests { .withIfNotExists() // .build(); - assertThat(insertOptions.getTtl()).isEqualTo(10); + assertThat(insertOptions.getTtl()).isEqualTo(Duration.ofSeconds(10)); assertThat(insertOptions.isIfNotExists()).isTrue(); } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/UpdateOptionsUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/UpdateOptionsUnitTests.java index 0f99ca4ed..9a0a9a1d1 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/UpdateOptionsUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/UpdateOptionsUnitTests.java @@ -15,7 +15,9 @@ */ package org.springframework.data.cassandra.core; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; + +import java.time.Duration; import org.junit.Test; @@ -34,7 +36,7 @@ public class UpdateOptionsUnitTests { .withIfExists() // .build(); - assertThat(updateOptions.getTtl()).isEqualTo(10); + assertThat(updateOptions.getTtl()).isEqualTo(Duration.ofSeconds(10)); assertThat(updateOptions.isIfExists()).isTrue(); } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/ColumnReaderUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/ColumnReaderUnitTests.java index f39138132..8b04eaf4e 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/ColumnReaderUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/ColumnReaderUnitTests.java @@ -53,7 +53,7 @@ public class ColumnReaderUnitTests { } @Test - public void throwsIllegalArgumentExceptionIfColumnDoesNotExistByName() throws Exception { + public void throwsIllegalArgumentExceptionIfColumnDoesNotExistByName() { when(columnDefinitions.getIndexOf(NON_EXISTENT_COLUMN)).thenReturn(-1); @@ -66,12 +66,12 @@ public class ColumnReaderUnitTests { } @Test - public void throwsIllegalArgumentExceptionIfColumnDoesNotExistByCqlIdentifier() throws Exception { + public void throwsIllegalArgumentExceptionIfColumnDoesNotExistByCqlIdentifier() { when(columnDefinitions.getIndexOf(NON_EXISTENT_COLUMN)).thenReturn(-1); try { - underTest.get(new CqlIdentifier(NON_EXISTENT_COLUMN)); + underTest.get(CqlIdentifier.cqlId(NON_EXISTENT_COLUMN)); fail("Expected illegal argument exception"); } catch (IllegalArgumentException e) { assertThat(e.getMessage()).isEqualTo("Column does not exist in Cassandra table: " + NON_EXISTENT_COLUMN); @@ -79,12 +79,12 @@ public class ColumnReaderUnitTests { } @Test - public void throwsIllegalArgumentExceptionIfColumnDoesNotExistByCqlIdentifierAndType() throws Exception { + public void throwsIllegalArgumentExceptionIfColumnDoesNotExistByCqlIdentifierAndType() { when(columnDefinitions.getIndexOf(NON_EXISTENT_COLUMN)).thenReturn(-1); try { - underTest.get(new CqlIdentifier(NON_EXISTENT_COLUMN), String.class); + underTest.get(CqlIdentifier.cqlId(NON_EXISTENT_COLUMN), String.class); fail("Expected illegal argument exception"); } catch (IllegalArgumentException e) { assertThat(e.getMessage()).isEqualTo("Column does not exist in Cassandra table: " + NON_EXISTENT_COLUMN); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/CassandraAccessorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/CassandraAccessorUnitTests.java index fe1db4b74..e3495b3d6 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/CassandraAccessorUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/CassandraAccessorUnitTests.java @@ -39,10 +39,9 @@ public class CassandraAccessorUnitTests { private CassandraAccessor cassandraAccessor; - @Mock private CassandraExceptionTranslator mockExceptionTranslator; - @Rule public ExpectedException exception = ExpectedException.none(); + @Mock private CassandraExceptionTranslator mockExceptionTranslator; @Mock private Session mockSession; @Before @@ -79,11 +78,6 @@ public class CassandraAccessorUnitTests { } } - @Test // DATACASS-286 - public void getUninitializedExceptionTranslatorReturnsDefault() { - assertThat(cassandraAccessor.getExceptionTranslator()).isEqualTo(cassandraAccessor.exceptionTranslator); - } - @Test // DATACASS-286 public void setAndGetSession() { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/QueryOptionsUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/QueryOptionsUnitTests.java index 9f9730c00..6da5bf81d 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/QueryOptionsUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/QueryOptionsUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.cassandra.core.cql; import static org.assertj.core.api.Assertions.*; +import java.time.Duration; import java.util.concurrent.TimeUnit; import org.junit.Test; @@ -48,7 +49,7 @@ public class QueryOptionsUnitTests { assertThat(queryOptions.getClass()).isEqualTo(QueryOptions.class); assertThat(queryOptions.getRetryPolicy()).isEqualTo(FallthroughRetryPolicy.INSTANCE); assertThat(queryOptions.getConsistencyLevel()).isEqualTo(ConsistencyLevel.ANY); - assertThat(queryOptions.getReadTimeout()).isEqualTo(1000); + assertThat(queryOptions.getReadTimeout()).isEqualTo(Duration.ofSeconds(1)); assertThat(queryOptions.getFetchSize()).isEqualTo(10); assertThat(queryOptions.getTracing()).isTrue(); } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtilUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtilUnitTests.java index 145ab4ac7..b2ea6f19a 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtilUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtilUnitTests.java @@ -71,9 +71,8 @@ public class QueryOptionsUtilUnitTests { @Test // DATACASS-202 public void addPreparedStatementOptionsShouldAddOurQueryOptions() { - QueryOptions queryOptions = QueryOptions.builder().retryPolicy(FallthroughRetryPolicy.INSTANCE).build(); - - queryOptions.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM); + QueryOptions queryOptions = QueryOptions.builder().retryPolicy(FallthroughRetryPolicy.INSTANCE) + .consistencyLevel(ConsistencyLevel.LOCAL_QUORUM).build(); QueryOptionsUtil.addPreparedStatementOptions(mockPreparedStatement, queryOptions); @@ -97,9 +96,8 @@ public class QueryOptionsUtilUnitTests { @Test // DATACASS-202 public void addStatementQueryOptionsShouldAddOurQueryOptions() { - QueryOptions queryOptions = QueryOptions.builder().retryPolicy(FallthroughRetryPolicy.INSTANCE).build(); - - queryOptions.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM); + QueryOptions queryOptions = QueryOptions.builder().retryPolicy(FallthroughRetryPolicy.INSTANCE) + .consistencyLevel(ConsistencyLevel.LOCAL_QUORUM).build(); QueryOptionsUtil.addQueryOptions(mockStatement, queryOptions); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/WriteOptionsUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/WriteOptionsUnitTests.java index a0d1c0555..aaaa5d789 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/WriteOptionsUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/WriteOptionsUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.cassandra.core.cql; import static org.assertj.core.api.Assertions.*; +import java.time.Duration; import java.util.concurrent.TimeUnit; import org.junit.Test; @@ -43,10 +44,10 @@ public class WriteOptionsUnitTests { .withTracing()// .build(); // - assertThat(writeOptions.getTtl()).isEqualTo(123); + assertThat(writeOptions.getTtl()).isEqualTo(Duration.ofSeconds(123)); assertThat(writeOptions.getRetryPolicy()).isEqualTo(FallthroughRetryPolicy.INSTANCE); assertThat(writeOptions.getConsistencyLevel()).isEqualTo(ConsistencyLevel.ANY); - assertThat(writeOptions.getReadTimeout()).isEqualTo(1); + assertThat(writeOptions.getReadTimeout()).isEqualTo(Duration.ofMillis(1)); assertThat(writeOptions.getFetchSize()).isEqualTo(10); assertThat(writeOptions.getTracing()).isTrue(); } @@ -56,7 +57,7 @@ public class WriteOptionsUnitTests { WriteOptions writeOptions = WriteOptions.builder().readTimeout(1, TimeUnit.MINUTES).build(); - assertThat(writeOptions.getReadTimeout()).isEqualTo(60L * 1000L); + assertThat(writeOptions.getReadTimeout()).isEqualTo(Duration.ofSeconds(60)); assertThat(writeOptions.getFetchSize()).isNull(); assertThat(writeOptions.getTracing()).isNull(); } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AbstractTableOperationCqlGeneratorTest.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AbstractTableOperationCqlGeneratorTest.java index c6e98cf74..9645df816 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AbstractTableOperationCqlGeneratorTest.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AbstractTableOperationCqlGeneratorTest.java @@ -26,7 +26,7 @@ import org.springframework.data.cassandra.core.cql.keyspace.TableNameSpecificati * @param The type of the {@link TableNameSpecification} * @param The type of the {@link TableNameCqlGenerator} */ -public abstract class AbstractTableOperationCqlGeneratorTest, G extends TableNameCqlGenerator> { +public abstract class AbstractTableOperationCqlGeneratorTest> { public abstract S specification(); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterKeyspaceCqlGeneratorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterKeyspaceCqlGeneratorUnitTests.java index 46503f8a3..9a67c4415 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterKeyspaceCqlGeneratorUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterKeyspaceCqlGeneratorUnitTests.java @@ -74,7 +74,7 @@ public class AlterKeyspaceCqlGeneratorUnitTests { replicationMap.put(new DefaultOption("dc1", Long.class, false, false, true), 2); replicationMap.put(new DefaultOption("dc2", Long.class, false, false, true), 3); - return AlterKeyspaceSpecification.alterKeyspace().name(name).with(KeyspaceOption.REPLICATION, replicationMap) + return AlterKeyspaceSpecification.alterKeyspace(name).with(KeyspaceOption.REPLICATION, replicationMap) .with(KeyspaceOption.DURABLE_WRITES, durableWrites); } @@ -107,7 +107,7 @@ public class AlterKeyspaceCqlGeneratorUnitTests { replicationMap.put(new DefaultOption("dc1", Long.class, false, false, true), 2); replicationMap.put(new DefaultOption("dc2", Long.class, false, false, true), 3); - return AlterKeyspaceSpecification.alterKeyspace().name(name).with(KeyspaceOption.REPLICATION, replicationMap); + return AlterKeyspaceSpecification.alterKeyspace(name).with(KeyspaceOption.REPLICATION, replicationMap); } @Override diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterUserTypeCqlGeneratorIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterUserTypeCqlGeneratorIntegrationTests.java index 507c03c3d..fcb054628 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterUserTypeCqlGeneratorIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterUserTypeCqlGeneratorIntegrationTests.java @@ -88,11 +88,11 @@ public class AlterUserTypeCqlGeneratorIntegrationTests extends AbstractKeyspaceC @Test(expected = IllegalArgumentException.class) // DATACASS-172 public void generationFailsIfNameIsNotSet() { - toCql(AlterUserTypeSpecification.alterType()); + toCql(AlterUserTypeSpecification.alterType(null)); } @Test(expected = IllegalArgumentException.class) // DATACASS-172 public void generationFailsWithoutFields() { - toCql(AlterUserTypeSpecification.alterType().name("hello")); + toCql(AlterUserTypeSpecification.alterType("hello")); } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterUserTypeCqlGeneratorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterUserTypeCqlGeneratorUnitTests.java index c97053fe4..dba1013ee 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterUserTypeCqlGeneratorUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/AlterUserTypeCqlGeneratorUnitTests.java @@ -67,13 +67,9 @@ public class AlterUserTypeCqlGeneratorUnitTests { assertThat(toCql(spec)).isEqualTo("ALTER TYPE address RENAME zip TO zap AND city TO county;"); } - @Test(expected = IllegalArgumentException.class) // DATACASS-172 - public void generationFailsIfNameIsNotSet() { - toCql(AlterUserTypeSpecification.alterType()); - } @Test(expected = IllegalArgumentException.class) // DATACASS-172 public void generationFailsWithoutFields() { - toCql(AlterUserTypeSpecification.alterType().name("hello")); + toCql(AlterUserTypeSpecification.alterType("hello")); } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateKeyspaceCqlGeneratorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateKeyspaceCqlGeneratorUnitTests.java index 9ce4b4bad..5dc6f83fa 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateKeyspaceCqlGeneratorUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateKeyspaceCqlGeneratorUnitTests.java @@ -81,7 +81,7 @@ public class CreateKeyspaceCqlGeneratorUnitTests { public CreateKeyspaceSpecification specification() { keyspace = name; - return CreateKeyspaceSpecification.createKeyspace().name(keyspace) + return CreateKeyspaceSpecification.createKeyspace(keyspace) .with(KeyspaceOption.REPLICATION, replicationMap).with(KeyspaceOption.DURABLE_WRITES, durableWrites); } @@ -106,7 +106,7 @@ public class CreateKeyspaceCqlGeneratorUnitTests { public CreateKeyspaceSpecification specification() { keyspace = name; - return CreateKeyspaceSpecification.createKeyspace().name(keyspace); + return CreateKeyspaceSpecification.createKeyspace(keyspace); } @Test @@ -134,7 +134,7 @@ public class CreateKeyspaceCqlGeneratorUnitTests { replicationMap.put(new DefaultOption("dc1", Long.class, false, false, true), 2); replicationMap.put(new DefaultOption("dc2", Long.class, false, false, true), 3); - return CreateKeyspaceSpecification.createKeyspace().name(keyspace) + return CreateKeyspaceSpecification.createKeyspace(keyspace) .with(KeyspaceOption.REPLICATION, replicationMap).with(KeyspaceOption.DURABLE_WRITES, durableWrites); } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateTableCqlGeneratorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateTableCqlGeneratorUnitTests.java index e085471f0..4e19635d0 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateTableCqlGeneratorUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateTableCqlGeneratorUnitTests.java @@ -128,7 +128,7 @@ public class CreateTableCqlGeneratorUnitTests { @Override public CreateTableSpecification specification() { - return CreateTableSpecification.createTable().name(name).partitionKeyColumn(partitionKey0, partitionKeyType0) + return CreateTableSpecification.createTable(name).partitionKeyColumn(partitionKey0, partitionKeyType0) .column(column1, columnType1); } @@ -154,7 +154,7 @@ public class CreateTableCqlGeneratorUnitTests { @Override public CreateTableSpecification specification() { - return CreateTableSpecification.createTable().name(name).partitionKeyColumn(partKey0, partKeyType0) + return CreateTableSpecification.createTable(name).partitionKeyColumn(partKey0, partKeyType0) .partitionKeyColumn(partKey1, partKeyType1).column(column0, columnType0); } @@ -188,7 +188,7 @@ public class CreateTableCqlGeneratorUnitTests { @Override public CreateTableSpecification specification() { - return CreateTableSpecification.createTable().name(name).partitionKeyColumn(partitionKey0, partitionKeyType0) + return CreateTableSpecification.createTable(name).partitionKeyColumn(partitionKey0, partitionKeyType0) .partitionKeyColumn(partitionKey1, partitionKeyType1).column(column1, columnType1) .with(TableOption.READ_REPAIR_CHANCE, readRepairChance); } @@ -243,7 +243,7 @@ public class CreateTableCqlGeneratorUnitTests { cachingMap.put(CachingOption.KEYS, KeyCachingOption.ALL); cachingMap.put(CachingOption.ROWS_PER_PARTITION, "NONE"); - return CreateTableSpecification.createTable().name(name).partitionKeyColumn(partitionKey0, partitionKeyType0) + return CreateTableSpecification.createTable(name).partitionKeyColumn(partitionKey0, partitionKeyType0) .partitionKeyColumn(partitionKey1, partitionKeyType1).column(column1, columnType1) .with(TableOption.COMPACT_STORAGE).with(TableOption.READ_REPAIR_CHANCE, readRepairChance) .with(TableOption.COMPACTION, compactionMap).with(TableOption.COMPRESSION, compressionMap) @@ -306,7 +306,7 @@ public class CreateTableCqlGeneratorUnitTests { @Override public CreateTableSpecification specification() { - return CreateTableSpecification.createTable().name(tableName).partitionKeyColumn(cqlId("pk"), DataType.text()); + return CreateTableSpecification.createTable(tableName).partitionKeyColumn(cqlId("pk"), DataType.text()); } /** diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateUserTypeCqlGeneratorIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateUserTypeCqlGeneratorIntegrationTests.java index 5c438de37..9a054365a 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateUserTypeCqlGeneratorIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateUserTypeCqlGeneratorIntegrationTests.java @@ -60,8 +60,7 @@ public class CreateUserTypeCqlGeneratorIntegrationTests extends AbstractKeyspace public void createUserTypeIfNotExists() { CreateUserTypeSpecification spec = CreateUserTypeSpecification // - .createType() // - .name("address").ifNotExists().field("zip", DataType.ascii()) // + .createType("address").ifNotExists().field("zip", DataType.ascii()) // .field("city", DataType.varchar()); session.execute(toCql(spec)); @@ -75,8 +74,7 @@ public class CreateUserTypeCqlGeneratorIntegrationTests extends AbstractKeyspace public void createNestedUserType() { CreateUserTypeSpecification addressSpec = CreateUserTypeSpecification // - .createType() // - .name("address").ifNotExists().field("zip", DataType.ascii()) // + .createType("address").ifNotExists().field("zip", DataType.ascii()) // .field("city", DataType.varchar()); session.execute(toCql(addressSpec)); @@ -85,8 +83,7 @@ public class CreateUserTypeCqlGeneratorIntegrationTests extends AbstractKeyspace UserType address = keyspace.getUserType("address"); CreateUserTypeSpecification personSpec = CreateUserTypeSpecification // - .createType() // - .name("person").ifNotExists().field("address", address.copy(true)) // + .createType("person").ifNotExists().field("address", address.copy(true)) // .field("city", DataType.varchar()); session.execute(toCql(personSpec)); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateUserTypeCqlGeneratorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateUserTypeCqlGeneratorUnitTests.java index 54284b628..8e98387ae 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateUserTypeCqlGeneratorUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/CreateUserTypeCqlGeneratorUnitTests.java @@ -55,20 +55,14 @@ public class CreateUserTypeCqlGeneratorUnitTests { public void createUserTypeIfNotExists() { CreateUserTypeSpecification spec = CreateUserTypeSpecification // - .createType() // - .name("address").ifNotExists().field("zip", DataType.ascii()) // + .createType("address").ifNotExists().field("zip", DataType.ascii()) // .field("city", DataType.varchar()); assertThat(toCql(spec)).isEqualTo("CREATE TYPE IF NOT EXISTS address (zip ascii, city varchar);"); } - @Test(expected = IllegalArgumentException.class) // DATACASS-172 - public void generationFailsIfNameIsNotSet() { - toCql(CreateUserTypeSpecification.createType()); - } - @Test(expected = IllegalArgumentException.class) // DATACASS-172 public void generationFailsWithoutFields() { - toCql(CreateUserTypeSpecification.createType().name("hello")); + toCql(CreateUserTypeSpecification.createType("hello")); } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropIndexCqlGeneratorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropIndexCqlGeneratorUnitTests.java index 6a211d028..491b9fdb6 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropIndexCqlGeneratorUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropIndexCqlGeneratorUnitTests.java @@ -46,7 +46,7 @@ public class DropIndexCqlGeneratorUnitTests { public String name = "myindex"; public DropIndexSpecification specification() { - return DropIndexSpecification.dropIndex().name(name); + return DropIndexSpecification.dropIndex(name); } public DropIndexCqlGenerator generator() { @@ -67,7 +67,7 @@ public class DropIndexCqlGeneratorUnitTests { public String name = "myindex"; public DropIndexSpecification specification() { - return DropIndexSpecification.dropIndex().name(name) + return DropIndexSpecification.dropIndex(name) // .ifExists() ; } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropKeyspaceCqlGeneratorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropKeyspaceCqlGeneratorUnitTests.java index b95e720ca..bc0a3be84 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropKeyspaceCqlGeneratorUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropKeyspaceCqlGeneratorUnitTests.java @@ -49,7 +49,7 @@ public class DropKeyspaceCqlGeneratorUnitTests { @Override public DropKeyspaceSpecification specification() { - return DropKeyspaceSpecification.dropKeyspace().name(name); + return DropKeyspaceSpecification.dropKeyspace(name); } @Override diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropTableCqlGeneratorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropTableCqlGeneratorUnitTests.java index 2bcd54b48..6dcbd536c 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropTableCqlGeneratorUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/DropTableCqlGeneratorUnitTests.java @@ -46,7 +46,7 @@ public class DropTableCqlGeneratorUnitTests { public String name = "mytable"; public DropTableSpecification specification() { - return DropTableSpecification.dropTable().name(name); + return DropTableSpecification.dropTable(name); } public DropTableCqlGenerator generator() { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/TableLifecycleIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/TableLifecycleIntegrationTests.java index 54bc61d68..3d9b26577 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/TableLifecycleIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/generator/TableLifecycleIntegrationTests.java @@ -65,7 +65,7 @@ public class TableLifecycleIntegrationTests extends AbstractKeyspaceCreatingInte @Override public DropTableSpecification specification() { - return DropTableSpecification.dropTable().name(createTableTest.specification.getName()); + return DropTableSpecification.dropTable(createTableTest.specification.getName()); } @Override diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/keyspace/OptionUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/keyspace/OptionUnitTests.java index 2fb605191..690f076a0 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/keyspace/OptionUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/keyspace/OptionUnitTests.java @@ -41,19 +41,19 @@ public class OptionUnitTests { @Test public void testOptionWithNullType() { - new DefaultOption("opt", null, true, true, true); - new DefaultOption("opt", null, false, true, true); + new DefaultOption("opt", Void.class, true, true, true); + new DefaultOption("opt", Void.class, false, true, true); } @Test public void testOptionWithNullTypeIsCoerceable() { - Option op = new DefaultOption("opt", null, true, true, true); + Option op = new DefaultOption("opt", Void.class, true, true, true); assertThat(op.isCoerceable("")).isTrue(); - assertThat(op.isCoerceable(null)).isTrue(); } @Test public void testOptionValueCoercion() { + String name = "my_option"; Class type = String.class; boolean requires = true; @@ -77,7 +77,6 @@ public class OptionUnitTests { assertThat(op.toString(value)).isEqualTo(expected); } assertThat(op.isCoerceable("x")).isFalse(); - assertThat(op.isCoerceable(null)).isTrue(); type = Long.class; escapes = false; @@ -90,28 +89,25 @@ public class OptionUnitTests { assertThat(op.toString(value)).isEqualTo(expected); } assertThat(op.isCoerceable("x")).isFalse(); - assertThat(op.isCoerceable(null)).isTrue(); type = Double.class; escapes = false; quotes = false; op = new DefaultOption(name, type, requires, escapes, quotes); - String[] expecteds = new String[] { "1", "1.0", "1.0", "1", "1.0", null }; - Object[] values = new Object[] { 1, 1.0F, 1.0D, "1", "1.0", null }; + String[] expecteds = new String[] { "1", "1.0", "1.0", "1", "1.0" }; + Object[] values = new Object[] { 1, 1.0F, 1.0D, "1", "1.0" }; for (int i = 0; i < values.length; i++) { assertThat(op.isCoerceable(values[i])).isTrue(); assertThat(op.toString(values[i])).isEqualTo(expecteds[i]); } assertThat(op.isCoerceable("x")).isFalse(); - assertThat(op.isCoerceable(null)).isTrue(); type = RetentionPolicy.class; escapes = false; quotes = false; op = new DefaultOption(name, type, requires, escapes, quotes); - assertThat(op.isCoerceable(null)).isTrue(); assertThat(op.isCoerceable(RetentionPolicy.CLASS)).isTrue(); assertThat(op.isCoerceable("CLASS")).isTrue(); assertThat(op.isCoerceable("x")).isFalse(); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/QueryDerivationIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/QueryDerivationIntegrationTests.java index e9cc7a174..5da36b8e8 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/QueryDerivationIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/QueryDerivationIntegrationTests.java @@ -15,8 +15,8 @@ */ package org.springframework.data.cassandra.repository; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assume.assumeTrue; +import static org.assertj.core.api.Assertions.*; +import static org.junit.Assume.*; import java.time.LocalDate; import java.util.Arrays; @@ -25,10 +25,10 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; @@ -51,8 +51,6 @@ import org.springframework.data.util.Version; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.assertj.core.api.Assertions; - import com.datastax.driver.core.Session; /** @@ -121,7 +119,7 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC @Test // DATACASS-7 public void shouldFindByLastnameAndDynamicSort() { - List result = personRepository.findByLastname("White", new Sort("firstname")); + List result = personRepository.findByLastname("White", Sort.by("firstname")); assertThat(result).contains(flynn, skyler, walter); } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/cdi/CassandraOperationsProducer.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/cdi/CassandraOperationsProducer.java index bd78e1a06..483afffc9 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/cdi/CassandraOperationsProducer.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/cdi/CassandraOperationsProducer.java @@ -72,7 +72,7 @@ class CassandraOperationsProducer { CassandraAdminTemplate cassandraTemplate = new CassandraAdminTemplate(cluster.connect(), cassandraConverter); - CreateKeyspaceSpecification createKeyspaceSpecification = new CreateKeyspaceSpecification(KEYSPACE_NAME) + CreateKeyspaceSpecification createKeyspaceSpecification = CreateKeyspaceSpecification.createKeyspace(KEYSPACE_NAME) .ifNotExists(); cassandraTemplate.getCqlOperations().execute(CreateKeyspaceCqlGenerator.toCql(createKeyspaceSpecification)); cassandraTemplate.getCqlOperations().execute("USE " + KEYSPACE_NAME); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/conversion/ParameterConversionTestSupport.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/conversion/ParameterConversionTestSupport.java index 0d6f16f5b..8f24e241b 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/conversion/ParameterConversionTestSupport.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/conversion/ParameterConversionTestSupport.java @@ -65,7 +65,7 @@ abstract class ParameterConversionTestSupport extends AbstractSpringDataEmbedded } @Override - public CassandraSessionFactoryBean session() throws ClassNotFoundException { + public CassandraSessionFactoryBean session() { Cluster cluster = cluster().getObject(); Session session = cluster.connect(getKeyspaceName()); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/forcequote/compositeprimarykey/CompositeKeyCrudIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/forcequote/compositeprimarykey/CompositeKeyCrudIntegrationTests.java index c6e7fa8a1..0314bbf18 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/forcequote/compositeprimarykey/CompositeKeyCrudIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/forcequote/compositeprimarykey/CompositeKeyCrudIntegrationTests.java @@ -78,8 +78,7 @@ public class CompositeKeyCrudIntegrationTests extends AbstractKeyspaceCreatingIn assertThat(correlationEntities).hasSize(2); - QueryOptions queryOptions = new QueryOptions(); - queryOptions.setConsistencyLevel(ConsistencyLevel.ONE); + QueryOptions queryOptions = QueryOptions.builder().consistencyLevel(ConsistencyLevel.ONE).build(); operations.delete(correlationEntity1, queryOptions); operations.delete(correlationEntity2, queryOptions); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/ConvertingParameterAccessorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/ConvertingParameterAccessorUnitTests.java index 8cff5135e..3ef469d6f 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/ConvertingParameterAccessorUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/ConvertingParameterAccessorUnitTests.java @@ -118,16 +118,6 @@ public class ConvertingParameterAccessorUnitTests { assertThat(list.get(0)).isInstanceOf(com.datastax.driver.core.LocalDate.class); } - @Test // DATACASS-7 - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void shouldProvideTypeBasedOnValue() { - - when(mockParameterAccessor.getDataType(0)).thenReturn(null); - when(mockParameterAccessor.getParameterType(0)).thenReturn((Class) LocalDate.class); - - assertThat(convertingParameterAccessor.getDataType(0)).isEqualTo(DataType.date()); - } - @Test // DATACASS-7 @SuppressWarnings({ "rawtypes", "unchecked" }) public void shouldProvideTypeBasedOnPropertyType() { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/IntegrationTestConfig.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/IntegrationTestConfig.java index 09a88c3ff..109d85545 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/IntegrationTestConfig.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/IntegrationTestConfig.java @@ -77,11 +77,11 @@ public class IntegrationTestConfig extends AbstractReactiveCassandraConfiguratio @Override protected List getKeyspaceCreations() { - return Collections.singletonList(createKeyspace().name(getKeyspaceName()).withSimpleReplication()); + return Collections.singletonList(createKeyspace(getKeyspaceName()).withSimpleReplication()); } @Override protected List getKeyspaceDrops() { - return Collections.singletonList(DropKeyspaceSpecification.dropKeyspace().name(getKeyspaceName())); + return Collections.singletonList(DropKeyspaceSpecification.dropKeyspace(getKeyspaceName())); } }