Support AOT processing of Cassandra repositories.

We now ship runtime hints for AOT processing of the Spring Data infrastructure.

Closes #1280
This commit is contained in:
Mark Paluch
2022-07-05 07:49:51 +02:00
parent f416c2c386
commit 5866f002f0
18 changed files with 1259 additions and 68 deletions

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra;
import java.util.Arrays;
import java.util.function.Consumer;
import org.springframework.data.domain.ManagedTypes;
/**
* Cassandra-specific extension to {@link ManagedTypes}.
*
* @author Mark Paluch
* @since 4.0
*/
public final class CassandraManagedTypes implements ManagedTypes {
private final ManagedTypes delegate;
private CassandraManagedTypes(ManagedTypes types) {
this.delegate = types;
}
/**
* Wraps an existing {@link ManagedTypes} object with {@link CassandraManagedTypes}.
*
* @param managedTypes
* @return
*/
public static CassandraManagedTypes from(ManagedTypes managedTypes) {
return new CassandraManagedTypes(managedTypes);
}
/**
* Factory method used to construct {@link CassandraManagedTypes} from the given array of {@link Class types}.
*
* @param types array of {@link Class types} used to initialize the {@link ManagedTypes}; must not be {@literal null}.
* @return new instance of {@link CassandraManagedTypes} initialized from {@link Class types}.
*/
public static CassandraManagedTypes from(Class<?>... types) {
return fromIterable(Arrays.asList(types));
}
/**
* Factory method used to construct {@link CassandraManagedTypes} from the given, required {@link Iterable} of
* {@link Class types}.
*
* @param types {@link Iterable} of {@link Class types} used to initialize the {@link ManagedTypes}; must not be
* {@literal null}.
* @return new instance of {@link CassandraManagedTypes} initialized the given, required {@link Iterable} of
* {@link Class types}.
*/
public static CassandraManagedTypes fromIterable(Iterable<? extends Class<?>> types) {
return from(ManagedTypes.fromIterable(types));
}
/**
* Factory method to return an empty {@link CassandraManagedTypes} object.
*
* @return an empty {@link CassandraManagedTypes} object.
*/
public static CassandraManagedTypes empty() {
return from(ManagedTypes.empty());
}
@Override
public void forEach(Consumer<Class<?>> action) {
delegate.forEach(action);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.aot;
import java.util.Arrays;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.data.cassandra.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.cassandra.core.mapping.event.BeforeSaveCallback;
import org.springframework.data.cassandra.core.mapping.event.ReactiveBeforeConvertCallback;
import org.springframework.data.cassandra.core.mapping.event.ReactiveBeforeSaveCallback;
import org.springframework.data.cassandra.repository.support.SimpleCassandraRepository;
import org.springframework.data.cassandra.repository.support.SimpleReactiveCassandraRepository;
import org.springframework.data.repository.util.ReactiveWrappers;
import org.springframework.lang.Nullable;
/**
* {@link RuntimeHintsRegistrar} for repository types and entity callbacks.
*
* @author Mark Paluch
* @since 4.0
*/
class CassandraRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
private static final boolean PROJECT_REACTOR_PRESENT = ReactiveWrappers
.isAvailable(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR);
@Override
public void registerHints(org.springframework.aot.hint.RuntimeHints hints, @Nullable ClassLoader classLoader) {
hints.reflection().registerTypes(Arrays.asList(TypeReference.of(SimpleCassandraRepository.class), //
TypeReference.of(BeforeConvertCallback.class), //
TypeReference.of(BeforeSaveCallback.class)),
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS));
if (PROJECT_REACTOR_PRESENT) {
hints.reflection().registerTypes(Arrays.asList(TypeReference.of(SimpleReactiveCassandraRepository.class), //
TypeReference.of(ReactiveBeforeConvertCallback.class), //
TypeReference.of(ReactiveBeforeSaveCallback.class)),
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS));
}
}
}

View File

@@ -0,0 +1,7 @@
/**
* Ahead of Time processing utilities for Spring Data Cassandra.
*/
@NonNullApi
package org.springframework.data.cassandra.aot;
import org.springframework.lang.NonNullApi;

View File

@@ -24,6 +24,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.data.cassandra.CassandraManagedTypes;
import org.springframework.data.cassandra.SessionFactory;
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
@@ -36,6 +37,7 @@ import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver;
import org.springframework.data.cassandra.core.mapping.Table;
import org.springframework.data.cassandra.core.mapping.UserTypeResolver;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.domain.ManagedTypes;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.lang.Nullable;
@@ -58,9 +60,8 @@ public abstract class AbstractCassandraConfiguration extends AbstractSessionConf
private @Nullable ClassLoader beanClassLoader;
/**
* Creates a {@link CassandraConverter} using the configured {@link #cassandraMapping()}.
*
* Will apply all specified {@link #customConversions()}.
* Creates a {@link CassandraConverter} using the configured {@link #cassandraMapping()}. Will apply all specified
* {@link #customConversions()}.
*
* @return {@link CassandraConverter} used to convert Java and Cassandra value types during the mapping process.
* @see #cassandraMapping()
@@ -71,11 +72,11 @@ public abstract class AbstractCassandraConfiguration extends AbstractSessionConf
CqlSession cqlSession = getRequiredSession();
UserTypeResolver userTypeResolver =
new SimpleUserTypeResolver(cqlSession, CqlIdentifier.fromCql(getKeyspaceName()));
UserTypeResolver userTypeResolver = new SimpleUserTypeResolver(cqlSession,
CqlIdentifier.fromCql(getKeyspaceName()));
MappingCassandraConverter converter =
new MappingCassandraConverter(requireBeanOfType(CassandraMappingContext.class));
MappingCassandraConverter converter = new MappingCassandraConverter(
requireBeanOfType(CassandraMappingContext.class));
converter.setCodecRegistry(cqlSession.getContext().getCodecRegistry());
converter.setUserTypeResolver(userTypeResolver);
@@ -84,23 +85,44 @@ public abstract class AbstractCassandraConfiguration extends AbstractSessionConf
return converter;
}
/**
* Returns the a {@link CassandraManagedTypes} object holding the initial entity set.
*
* @return new instance of {@link CassandraManagedTypes}.
* @throws ClassNotFoundException
* @since 4.0
*/
@Bean
public CassandraManagedTypes cassandraManagedTypes() throws ClassNotFoundException {
return CassandraManagedTypes.fromIterable(getInitialEntitySet());
}
/**
* Return the {@link MappingContext} instance to map Entities to {@link Object Java Objects}.
*
* @see org.springframework.data.cassandra.core.mapping.CassandraMappingContext
* @deprecated since 4.0, use {@link #cassandraMappingContext(ManagedTypes)} instead.
*/
@Deprecated(since = "4.0", forRemoval = true)
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
return cassandraMappingContext(cassandraManagedTypes());
}
/**
* Return the {@link MappingContext} instance to map Entities to {@link Object Java Objects}.
*
* @throws ClassNotFoundException if the Cassandra Entity class type identified by name
* cannot be found during the scan.
* @see org.springframework.data.cassandra.core.mapping.CassandraMappingContext
*/
@Bean
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
public CassandraMappingContext cassandraMappingContext(CassandraManagedTypes cassandraManagedTypes) {
CqlSession cqlSession = getRequiredSession();
UserTypeResolver userTypeResolver =
new SimpleUserTypeResolver(cqlSession, CqlIdentifier.fromCql(getKeyspaceName()));
UserTypeResolver userTypeResolver = new SimpleUserTypeResolver(cqlSession,
CqlIdentifier.fromCql(getKeyspaceName()));
CassandraMappingContext mappingContext =
new CassandraMappingContext(userTypeResolver, SimpleTupleTypeFactory.DEFAULT);
CassandraMappingContext mappingContext = new CassandraMappingContext(userTypeResolver,
SimpleTupleTypeFactory.DEFAULT);
CustomConversions customConversions = requireBeanOfType(CassandraCustomConversions.class);
@@ -108,7 +130,7 @@ public abstract class AbstractCassandraConfiguration extends AbstractSessionConf
mappingContext.setCodecRegistry(cqlSession.getContext().getCodecRegistry());
mappingContext.setCustomConversions(customConversions);
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setManagedTypes(cassandraManagedTypes);
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
return mappingContext;

View File

@@ -17,17 +17,19 @@ package org.springframework.data.cassandra.config;
import java.lang.annotation.Annotation;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
import org.springframework.data.auditing.config.AuditingConfiguration;
import org.springframework.data.cassandra.core.mapping.event.AuditingEntityCallback;
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -49,12 +51,10 @@ class CassandraAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport
}
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
protected void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration,
BeanDefinitionRegistry registry) {
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
super.registerBeanDefinitions(annotationMetadata, registry);
potentiallyRegisterCassandraPersistentEntities(builder, registry);
}
@Override
@@ -62,13 +62,8 @@ class CassandraAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport
Assert.notNull(configuration, "AuditingConfiguration must not be null");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
builder.addConstructorArgValue(definition.getBeanDefinition());
return configureDefaultAuditHandlerAttributes(configuration, builder);
return configureDefaultAuditHandlerAttributes(configuration,
BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class));
}
@Override
@@ -85,7 +80,40 @@ class CassandraAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
AuditingEntityCallback.class.getName(), registry);
}
static void potentiallyRegisterCassandraPersistentEntities(BeanDefinitionBuilder builder,
BeanDefinitionRegistry registry) {
String persistentEntitiesBeanName = detectPersistentEntitiesBeanName(registry);
if (persistentEntitiesBeanName == null) {
persistentEntitiesBeanName = BeanDefinitionReaderUtils.uniqueBeanName("cassandraPersistentEntities", registry);
// TODO: https://github.com/spring-projects/spring-framework/issues/28728
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntities.class) //
.setFactoryMethod("of") //
.addConstructorArgReference("cassandraMappingContext");
registry.registerBeanDefinition(persistentEntitiesBeanName, definition.getBeanDefinition());
}
builder.addConstructorArgReference(persistentEntitiesBeanName);
}
@Nullable
private static String detectPersistentEntitiesBeanName(BeanDefinitionRegistry registry) {
if (registry instanceof ListableBeanFactory beanFactory) {
for (String bn : beanFactory.getBeanNamesForType(PersistentEntities.class)) {
if (bn.startsWith("cassandra")) {
return bn;
}
}
}
return null;
}
}

View File

@@ -23,6 +23,6 @@ public interface DefaultBeanNames extends DefaultCqlBeanNames {
String DATA_TEMPLATE = "cassandraTemplate";
String CONVERTER = "cassandraConverter";
String CONTEXT = "cassandraMapping";
String CONTEXT = "cassandraMappingContext";
String USER_TYPE_RESOLVER = "userTypeResolver";
}

View File

@@ -18,11 +18,9 @@ package org.springframework.data.cassandra.config;
import java.lang.annotation.Annotation;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler;
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
import org.springframework.data.auditing.config.AuditingConfiguration;
@@ -49,12 +47,9 @@ class ReactiveCassandraAuditingRegistrar extends AuditingBeanDefinitionRegistrar
}
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
super.registerBeanDefinitions(annotationMetadata, registry);
protected void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration,
BeanDefinitionRegistry registry) {
CassandraAuditingRegistrar.potentiallyRegisterCassandraPersistentEntities(builder, registry);
}
@Override
@@ -62,13 +57,8 @@ class ReactiveCassandraAuditingRegistrar extends AuditingBeanDefinitionRegistrar
Assert.notNull(configuration, "AuditingConfiguration must not be null");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveIsNewAwareAuditingHandler.class);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
builder.addConstructorArgValue(definition.getBeanDefinition());
return configureDefaultAuditHandlerAttributes(configuration, builder);
return configureDefaultAuditHandlerAttributes(configuration,
BeanDefinitionBuilder.rootBeanDefinition(ReactiveIsNewAwareAuditingHandler.class));
}
@Override

View File

@@ -29,6 +29,7 @@ import java.util.List;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.lang.NonNull;
/**
* Helper class to register JodaTime specific {@link Converter} implementations in case the library is present on the
@@ -55,6 +56,7 @@ public abstract class CassandraJsr310Converters {
converters.add(LocalTimeToMillisOfDayConverter.INSTANCE);
converters.add(DateToInstantConverter.INSTANCE);
converters.add(InstantToDateConverter.INSTANCE);
converters.add(LocalDateTimeToInstantConverter.INSTANCE);
return converters;
@@ -111,6 +113,18 @@ public abstract class CassandraJsr310Converters {
}
}
@ReadingConverter
public enum InstantToDateConverter implements Converter<Instant, Date> {
INSTANCE;
@NonNull
@Override
public Date convert(Instant source) {
return Date.from(source);
}
}
/**
* Converter from {@link LocalDateTime} to {@link Instant}.
*

View File

@@ -20,6 +20,10 @@ import static org.springframework.data.cassandra.core.cql.keyspace.CqlStringUtil
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -32,6 +36,8 @@ import org.springframework.util.Assert;
*/
public class DefaultOption implements Option {
protected final Log log = LogFactory.getLog(getClass());
private final String name;
private final Class<?> type;
@@ -86,18 +92,51 @@ public class DefaultOption implements Option {
}
}
if (type == Long.class) {
return tryParse(value, Long::parseLong);
}
if (type == Integer.class) {
return tryParse(value, Integer::parseInt);
}
if (type == Double.class) {
return tryParse(value, Double::parseDouble);
}
if (type == Float.class) {
return tryParse(value, Float::parseFloat);
}
if (type == Boolean.class) {
return tryParse(value, Boolean::valueOf);
}
// check class via String constructor
try {
Constructor<?> ctor = type.getConstructor(String.class);
if (!ctor.isAccessible()) {
if (!ctor.canAccess(this)) {
ctor.setAccessible(true);
}
ctor.newInstance(value.toString());
return true;
} catch (Exception e) {}
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("Cannot parse option %s into %s".formatted(getName(), getType()), e);
}
}
return false;
}
private boolean tryParse(Object value, Consumer<String> parseFunction) {
try {
parseFunction.accept(value.toString());
return true;
} catch (RuntimeException e) {
if (log.isDebugEnabled()) {
log.debug("Cannot parse option %s into %s".formatted(getName(), getType()), e);
}
return false;
}
}
public Class<?> getType() {
return type;
}

View File

@@ -8,7 +8,8 @@ http\://www.springframework.org/schema/data/cassandra/spring-cassandra-1.5.xsd=o
http\://www.springframework.org/schema/data/cassandra/spring-cassandra-2.0.xsd=org/springframework/data/cassandra/config/spring-cassandra-2.0.xsd
http\://www.springframework.org/schema/data/cassandra/spring-cassandra-2.2.xsd=org/springframework/data/cassandra/config/spring-cassandra-2.2.xsd
http\://www.springframework.org/schema/data/cassandra/spring-cassandra-3.0.xsd=org/springframework/data/cassandra/config/spring-cassandra-3.0.xsd
http\://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd=org/springframework/data/cassandra/config/spring-cassandra-3.0.xsd
http\://www.springframework.org/schema/data/cassandra/spring-cassandra-4.0.xsd=org/springframework/data/cassandra/config/spring-cassandra-4.0.xsd
http\://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd=org/springframework/data/cassandra/config/spring-cassandra-4.0.xsd
https\://www.springframework.org/schema/cql/spring-cql-1.0.xsd=org/springframework/data/cassandra/config/spring-cql-1.0.xsd
https\://www.springframework.org/schema/cql/spring-cql-1.5.xsd=org/springframework/data/cassandra/config/spring-cql-1.5.xsd
https\://www.springframework.org/schema/cql/spring-cql-2.0.xsd=org/springframework/data/cassandra/config/spring-cql-2.0.xsd
@@ -19,4 +20,5 @@ https\://www.springframework.org/schema/data/cassandra/spring-cassandra-1.5.xsd=
https\://www.springframework.org/schema/data/cassandra/spring-cassandra-2.0.xsd=org/springframework/data/cassandra/config/spring-cassandra-2.0.xsd
https\://www.springframework.org/schema/data/cassandra/spring-cassandra-2.2.xsd=org/springframework/data/cassandra/config/spring-cassandra-2.2.xsd
https\://www.springframework.org/schema/data/cassandra/spring-cassandra-3.0.xsd=org/springframework/data/cassandra/config/spring-cassandra-3.0.xsd
https\://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd=org/springframework/data/cassandra/config/spring-cassandra-3.0.xsd
https\://www.springframework.org/schema/data/cassandra/spring-cassandra-4.0.xsd=org/springframework/data/cassandra/config/spring-cassandra-4.0.xsd
https\://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd=org/springframework/data/cassandra/config/spring-cassandra-4.0.xsd

View File

@@ -0,0 +1 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=org.springframework.data.cassandra.aot.CassandraRuntimeHintsRegistrar

View File

@@ -0,0 +1,814 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/data/cassandra"
xmlns:repository="http://www.springframework.org/schema/data/repository"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.springframework.org/schema/data/cassandra"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"
schemaLocation="https://www.springframework.org/schema/beans/spring-beans.xsd"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"
schemaLocation="https://www.springframework.org/schema/tool/spring-tool.xsd"/>
<xsd:import namespace="http://www.springframework.org/schema/context"
schemaLocation="https://www.springframework.org/schema/context/spring-context.xsd"/>
<xsd:import namespace="http://www.springframework.org/schema/data/repository"
schemaLocation="https://www.springframework.org/schema/data/repository/spring-repository.xsd"/>
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines configuration elements in the XML namespace for Spring Data for Apache Cassandra.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="auditing">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation>
<tool:exports
type="org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback"/>
<tool:exports
type="org.springframework.data.auditing.IsNewAwareAuditingHandler"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:attributeGroup ref="repository:auditing-attributes"/>
<xsd:attribute name="mapping-context-ref" type="mappingContextRef"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="converter" type="converterType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines a CassandraConverter for getting rich mapping functionality.
]]></xsd:documentation>
<xsd:appinfo>
<tool:exports
type="org.springframework.data.cassandra.core.convert.CassandraConverter"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="cql-template" type="cqlTemplateType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.cql.config.CassandraCqlTemplateFactoryBean">
<![CDATA[
Defines a CassandraTemplate.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports
type="org.springframework.data.cassandra.core.cql.CqlTemplate"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="initialize-keyspace" type="initializeKeyspaceType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.cassandra.core.cql.session.init.SessionFactoryInitializer">
<![CDATA[
Initializes a keyspace with CQL scripts provided in nested <script/> elements.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="mapping">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines a CassandraMappingContext for holding rich entity mapping information.
]]></xsd:documentation>
<xsd:appinfo>
<tool:exports
type="org.springframework.data.cassandra.core.mapping.CassandraMappingContext"/>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="entity" type="entityType" minOccurs="0"
maxOccurs="unbounded"/>
<xsd:element name="user-type-resolver" type="userTypeResolverType"
minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the mapping context; default is "cassandraMappingContext".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="entity-base-packages" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The comma-delimited base packages in which to scan for entities and their mapping information.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="user-type-resolver-ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a UserTypeResolver. UserTypeResolver is required when working with User-defined types.
]]></xsd:documentation>
<xsd:appinfo>
<tool:exports
type="org.springframework.data.cassandra.core.mapping.UserTypeResolver"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="repositories">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="repository:repositories">
<xsd:attributeGroup ref="cassandra-repository-attributes"/>
<xsd:attributeGroup ref="repository:repository-attributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="session" type="sessionType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.cassandra.config.CqlSessionFactoryBean">
<![CDATA[
Defines a Cassandra session.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.datastax.driver.core.Session"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="session-factory" type="sessionFactoryType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.cassandra.config.SessionFactoryFactoryBean">
<![CDATA[
Defines a Cassandra SessionFactory.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports
type="org.springframework.data.cassandra.SessionFactory"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="template" type="templateType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.cassandra.config.CassandraTemplateFactoryBean">
<![CDATA[
Defines a CassandraTemplate.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports
type="org.springframework.data.cassandra.CassandraTemplate"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:attributeGroup name="cassandra-repository-attributes">
<xsd:attribute name="cassandra-template-ref" type="cassandraTemplateRef">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a CassandraTemplate. Will default to 'cqlTemplate'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:simpleType name="cassandraConverterRef" final="union">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="org.springframework.data.cassandra.core.convert.CassandraConverter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<xsd:simpleType name="cassandraTemplateRef" final="union">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="org.springframework.data.cassandra.core.CassandraTemplate"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<xsd:simpleType name="cqlTemplateRef" final="union">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="org.springframework.data.cassandra.core.cql.CqlTemplate"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<xsd:simpleType name="sessionRef" final="union">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="com.datastax.driver.core.Session"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<xsd:simpleType name="sessionFactoryRef" final="union">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="org.springframework.data.cassandra.SessionFactory"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<xsd:complexType name="converterType">
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the converter ; default is "cassandraConverter".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="mapping-ref" type="mappingContextRef">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.cassandra.core.mapping.CassandraMappingContext">
<![CDATA[
The reference to a CassandraMappingContext. Will default to 'cassandraMapping'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="datacenterType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to specify replication factors by data center.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the data center.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="replication-factor" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The replication factor for the data center.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="initializeKeyspaceType">
<xsd:sequence>
<xsd:element name="script" type="scriptType" minOccurs="1"
maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
An CQL script to execute to populate, initialize, or clean up a keyspace.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="session-factory-ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to a session factory that should be initialized. Deprecated, use "session-factory".
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref"/>
<tool:expected-type
type="org.springframework.data.cassandra.SessionFactory"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="enabled" type="xsd:string" default="true">
<xsd:annotation>
<xsd:documentation>
Is this bean "enabled", meaning the scripts will be executed?
Defaults to true but can be used to switch on and off script execution
depending on the environment.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ignore-failures" default="NONE">
<xsd:annotation>
<xsd:documentation>
Should failed CQL statements be ignored during execution?
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="NONE">
<xsd:annotation>
<xsd:documentation><![CDATA[
Do not ignore failures (the default)
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="DROPS">
<xsd:annotation>
<xsd:documentation><![CDATA[
Ignore failed DROP statements
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="ALL">
<xsd:annotation>
<xsd:documentation><![CDATA[
Ignore all failures
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="separator" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The default statement separator to use (the default is to use ';' if it is present
in the script, or '\n' otherwise).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="keyspaceType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to define keyspaces.
]]></xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="replication" type="replicationType" minOccurs="0"
maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to configure the keyspace's replication settings.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="action" use="required" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The keyspace action to take at startup and possibly shutdown.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="durable-writes" type="xsd:string" use="optional"
default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether or not the keyspace supports durable writes.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of this keyspace. Required.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="replicationType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to configure the keyspace's replication settings.
]]></xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="data-center" type="datacenterType" minOccurs="0"
maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to specify replication factors by data center.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="class" type="xsd:string"
default="SimpleStrategy">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the replication class; default is "SIMPLE_STRATEGY".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="replication-factor" type="xsd:string"
default="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
The replication factor; default is 1.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="sessionType">
<xsd:sequence>
<xsd:element name="keyspace" type="keyspaceType" minOccurs="0"
maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to define a keyspace.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="keyspace-startup-cql" type="xsd:string" minOccurs="0"
maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Arbitrary CQL script to be executed against the system keyspace during bean initialization. Multiple elements will be executed in document order.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="keyspace-shutdown-cql" type="xsd:string" minOccurs="0"
maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Arbitrary CQL script to be executed against the system keyspace during bean destruction. Multiple elements will be executed in document order.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="startup-cql" type="xsd:string" minOccurs="0"
maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Arbitrary CQL script to be executed against the session's keyspace during bean initialization. Multiple elements will be executed in document order.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="shutdown-cql" type="xsd:string" minOccurs="0"
maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Arbitrary CQL script to be executed against the session's keyspace during bean destruction. Multiple elements will be executed in document order.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the session definition; default is "cassandraSession".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cassandra-converter-ref" type="cassandraConverterRef"
>
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a CassandraConverter; default is "cassandraConverter".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="contact-points" type="xsd:string" default="localhost"
>
<xsd:annotation>
<xsd:documentation><![CDATA[
The comma separated list of Cassandra servers. Default is "localhost".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="keyspace-name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of a Cassandra Keyspace. No default; for the system keyspace, use the empty string.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-datacenter" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the local data center.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="password" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
When Authentication is enabled, the password to use when connecting to the Cluster.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="port" type="xsd:string" default="9042">
<xsd:annotation>
<xsd:documentation><![CDATA[
The native CQL port to connect to. Default is 9042.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="schema-action" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The schema action to perform; default is NONE.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="username" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
When Authentication is enabled, the username to use when connecting to the Cluster.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="sessionFactoryType">
<xsd:sequence>
<xsd:element name="script" type="scriptType" minOccurs="1"
maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
An CQL script to execute to populate, initialize, or clean up a keyspace.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the session definition; default is "cassandraSessionFactory".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cassandra-converter-ref" type="cassandraConverterRef"
>
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a CassandraConverter; default is "cassandraConverter".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ignore-failures" default="NONE">
<xsd:annotation>
<xsd:documentation>
Should failed CQL statements be ignored during execution?
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="NONE">
<xsd:annotation>
<xsd:documentation><![CDATA[
Do not ignore failures (the default)
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="DROPS">
<xsd:annotation>
<xsd:documentation><![CDATA[
Ignore failed DROP statements
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="ALL">
<xsd:annotation>
<xsd:documentation><![CDATA[
Ignore all failures
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="separator" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The default statement separator to use (the default is to use ';' if it is present
in the script, or '\n' otherwise).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="schema-action" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The schema action to perform; default is NONE.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="session-ref" type="sessionRef">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra session; default is "cassandraSession".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="cqlTemplateType">
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the template; default is "cqlTemplate".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="session-ref" type="sessionRef">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra session; default is "cassandraSession".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="session-factory-ref" type="sessionFactoryRef">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra SessionFactory. This attribute has precedence over "session-ref".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="templateType">
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the template; default is "cassandraTemplate".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cassandra-converter-ref" type="cassandraConverterRef"
>
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a CassandraConverter; default is "cassandraConverter".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cql-template-ref" type="cqlTemplateRef">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a CqlTemplate; default is none. Providing a CqlTemplate reference overrides session references.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="session-ref" type="sessionRef">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra session; default is "cassandraSession". Session reference is omitted if a CqlTemplate reference is provided.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="session-factory-ref" type="sessionFactoryRef">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra SessionFactory. This attribute has precedence over "session-ref".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:simpleType name="mappingContextRef">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="org.springframework.data.cassandra.core.mapping.CassandraMappingContext"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<xsd:complexType name="entityType">
<xsd:sequence>
<xsd:element name="table" type="tableType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="property" type="propertyType" minOccurs="0"
maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="class" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Entity class name.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="propertyType">
<xsd:attribute name="column-name" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The column-name that the property should be mapped to.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="force-quote" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether the column name should be force-quoted.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the property. Required.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="scriptType">
<xsd:attribute name="location" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The resource location of an CQL script to execute. Can be a single script location
or a pattern (e.g. classpath:/com/foo/cql/*-data.cql).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="encoding" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The encoding for CQL scripts, if different from the platform encoding.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="separator" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The statement separator in the script (the default is to use ';' if it is present
in the script, or '\n' otherwise).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="execution">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicate the execution phase of this script. Use INIT to execute on startup (as a
bean initialization) or DESTROY to execute on shutdown (as a bean destruction callback).
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="INIT"/>
<xsd:enumeration value="DESTROY"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="tableType">
<xsd:attribute name="force-quote" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether to force-quote the table name.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="name" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Table name override.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="userTypeResolverType">
<xsd:attribute name="session-ref" type="sessionRef">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra Session; default is "cassandraSession".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.aot;
import static org.assertj.core.api.Assertions.*;
import java.util.stream.Stream;
import org.assertj.core.api.AbstractAssert;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.JdkProxyHint;
import org.springframework.aot.hint.RuntimeHintsPredicates;
/**
* AssertJ {@link AbstractAssert Assertion} for code contributions originating from Spring Data Repository
* infrastructure AOT processing.
*
* @author Christoph Strobl
* @author John Blum
* @since 4.0
*/
@SuppressWarnings("UnusedReturnValue")
public class CodeContributionAssert extends AbstractAssert<CodeContributionAssert, GenerationContext> {
public CodeContributionAssert(GenerationContext contribution) {
super(contribution, CodeContributionAssert.class);
}
public CodeContributionAssert contributesReflectionFor(Class<?>... types) {
for (Class<?> type : types) {
assertThat(this.actual.getRuntimeHints()).describedAs("No reflection entry found for [%s]", type)
.matches(RuntimeHintsPredicates.reflection().onType(type));
}
return this;
}
public CodeContributionAssert doesNotContributeReflectionFor(Class<?>... types) {
for (Class<?> type : types) {
assertThat(this.actual.getRuntimeHints()).describedAs("Reflection entry found for [%s]", type)
.matches(RuntimeHintsPredicates.reflection().onType(type).negate());
}
return this;
}
public CodeContributionAssert contributesJdkProxyFor(Class<?> entryPoint) {
assertThat(jdkProxiesFor(entryPoint).findFirst()).describedAs("No JDK proxy found for [%s]", entryPoint)
.isPresent();
return this;
}
public CodeContributionAssert doesNotContributeJdkProxyFor(Class<?> entryPoint) {
assertThat(jdkProxiesFor(entryPoint).findFirst())
.describedAs("Found JDK proxy matching [%s] though it should not be present", entryPoint).isNotPresent();
return this;
}
private Stream<JdkProxyHint> jdkProxiesFor(Class<?> entryPoint) {
return this.actual.getRuntimeHints().proxies().jdkProxies().filter(jdkProxyHint -> jdkProxyHint
.getProxiedInterfaces().get(0).getCanonicalName().equals(entryPoint.getCanonicalName()));
}
}

View File

@@ -25,6 +25,7 @@ import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
/**
* Integration tests for registering both, imperative and reactive auditing handlers.
@@ -53,8 +54,13 @@ class CassandraAuditingRegistrarIntegrationTests {
static class MyConfiguration {
@Bean
CassandraConverter cassandraConverter() {
return new MappingCassandraConverter();
CassandraConverter cassandraConverter(CassandraMappingContext context) {
return new MappingCassandraConverter(context);
}
@Bean
CassandraMappingContext cassandraMappingContext() {
return new CassandraMappingContext();
}
}

View File

@@ -21,7 +21,6 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import org.junit.jupiter.api.Test;
@@ -30,9 +29,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.CassandraManagedTypes;
import org.springframework.data.cassandra.repository.support.AbstractSpringDataEmbeddedCassandraIntegrationTest;
import org.springframework.data.cassandra.repository.support.IntegrationTestConfig;
import org.springframework.data.convert.CustomConversions;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.datastax.oss.driver.api.core.CqlIdentifier;
@@ -50,20 +49,10 @@ public class CreateUserTypeIntegrationTests extends AbstractSpringDataEmbeddedCa
@Configuration
public static class Config extends IntegrationTestConfig {
@Override
@Bean
public CassandraMappingContext cassandraMapping() {
CassandraMappingContext mappingContext = new CassandraMappingContext();
mappingContext.setInitialEntitySet(new HashSet<>(Arrays.asList(Car.class, Engine.class, Manufacturer.class)));
CustomConversions customConversions = customConversions();
mappingContext.setCustomConversions(customConversions);
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(getRequiredSession()));
return mappingContext;
public CassandraManagedTypes cassandraManagedTypes() {
return CassandraManagedTypes.fromIterable(Arrays.asList(Car.class, Engine.class, Manufacturer.class));
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.aot;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generate.ClassNameGenerator;
import org.springframework.aot.generate.DefaultGenerationContext;
import org.springframework.aot.generate.InMemoryGeneratedFiles;
import org.springframework.data.cassandra.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.cassandra.core.mapping.event.BeforeSaveCallback;
import org.springframework.data.cassandra.core.mapping.event.ReactiveBeforeConvertCallback;
import org.springframework.data.cassandra.core.mapping.event.ReactiveBeforeSaveCallback;
import org.springframework.data.cassandra.repository.support.SimpleCassandraRepository;
import org.springframework.data.cassandra.repository.support.SimpleReactiveCassandraRepository;
/**
* Unit tests for {@link CassandraRuntimeHintsRegistrar}.
*
* @author Mark Paluch
*/
class CassandraRuntimeHintsRegistrarUnitTests {
@Test // GH-1280
void shouldRegisterCassandraHints() {
CassandraRuntimeHintsRegistrar registrar = new CassandraRuntimeHintsRegistrar();
DefaultGenerationContext context = new DefaultGenerationContext(new ClassNameGenerator(Object.class),
new InMemoryGeneratedFiles());
registrar.registerHints(context.getRuntimeHints(), null);
new CodeContributionAssert(context).contributesReflectionFor(SimpleCassandraRepository.class,
SimpleReactiveCassandraRepository.class);
new CodeContributionAssert(context).contributesReflectionFor(BeforeConvertCallback.class, BeforeSaveCallback.class,
ReactiveBeforeConvertCallback.class, ReactiveBeforeSaveCallback.class);
}
}

View File

@@ -17,7 +17,7 @@
<context:property-placeholder
location="classpath:/config/cassandra-connection.properties"/>
<cassandra:converter mapping-ref="cassandraMapping"/>
<cassandra:converter mapping-ref="cassandraMappingContext"/>
<cassandra:template cassandra-converter-ref="cassandraConverter"/>

View File

@@ -13,7 +13,7 @@
<cassandra:mapping/>
<bean class="org.springframework.data.cassandra.core.convert.MappingCassandraConverter">
<constructor-arg index="0" ref="cassandraMapping"/>
<constructor-arg index="0" ref="cassandraMappingContext"/>
</bean>
</beans>