DATACASS-296 - Use CustomConversions for JSR-310, Joda and ThreeTen Backport types.

We now allow registering CustomConversions to register custom converters (read and write targets). CustomConversions registers by default JSR-310, Joda Time and ThreeTen backport type converters.
These types (LocalDate, LocalDateTime) can be used in domain classes and with query method arguments. Date-only types map to Cassandra's date type, Date and time types map to Cassandra's timestamp type. Type mapping works for singular and collection types (List and Set).

```
@Table
public Person

    @Id String id;

    java.time.LocalDate date;

    @CassandraType(type = Name.TIMESTAMP)
    java.time.LocalDate timestamp;

    List<org.threeten.bp.Instant> timestamps;
}
```

Original pull request: #68.
This commit is contained in:
Mark Paluch
2016-06-17 15:36:26 +02:00
committed by John Blum
parent fae1ba8591
commit 66a69c0926
45 changed files with 3544 additions and 450 deletions

21
pom.xml
View File

@@ -124,6 +124,20 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.threeten</groupId>
<artifactId>threetenbp</artifactId>
<version>${threetenbp}</version>
<optional>true</optional>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>org.apache.cassandra</groupId>
@@ -177,13 +191,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>

View File

@@ -5,6 +5,12 @@ Bundle-ManifestVersion: 2
Import-Package:
sun.reflect;version="0";resolution:=optional
Import-Template:
com.datastax.driver.core.*;resolution:="optional";version="[0.1.0, 1.0.0)",
com.google.common.*;resolution:="optional";version="[11.0.0, 20.0.0)",
javax.xml.transform.*;resolution:="optional";version="0",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.codehaus.jackson.*;resolution:="optional";version="[1.6, 2.0.0)",
org.springframework.beans.*;version="[3.1.0, 4.0.0)",
org.springframework.cache.*;version="[3.1.0, 4.0.0)",
org.springframework.context.*;version="[3.1.0, 4.0.0)",
@@ -16,15 +22,6 @@ Import-Template:
org.springframework.transaction.support.*;version="[3.1.0, 4.0.0)",
org.springframework.data.*;version="[1.5.0, 2.0.0)",
org.springframework.expression.*;version="[3.1.0, 4.0.0)",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.w3c.dom.*;version="0",
javax.xml.transform.*;resolution:="optional";version="0",
com.datastax.driver.core.*;resolution:="optional";version="[0.1.0, 1.0.0)",
org.apache.cassandra.db.marshal.*;version="[1.2.0, 1.3.0)",
org.springframework.cassandra.*;version="[1.0.0,2.0.0)",
org.slf4j.*;version="[1.5.0, 1.8.0)",
org.idevlab.rjc.*;resolution:="optional";version="[0.6.4, 0.6.4]",
org.apache.commons.pool.impl.*;resolution:="optional";version="[1.0.0, 3.0.0)",
org.codehaus.jackson.*;resolution:="optional";version="[1.6, 2.0.0)",
org.apache.commons.beanutils.*;resolution:="optional";version=1.8.5,
com.google.common.*;resolution:="optional";version="[11.0.0, 20.0.0)"
org.w3c.dom.*;version="0"

View File

@@ -59,6 +59,18 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.threeten</groupId>
<artifactId>threetenbp</artifactId>
<optional>true</optional>
</dependency>
<!-- CDI -->
<dependency>
<groupId>javax.enterprise</groupId>
@@ -110,12 +122,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors
* Copyright 2013-2016 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.
@@ -15,14 +15,18 @@
*/
package org.springframework.data.cassandra.config.java;
import java.util.Collections;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.cassandra.config.java.AbstractClusterConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.cassandra.config.CassandraEntityClassScanner;
import org.springframework.data.cassandra.config.CassandraSessionFactoryBean;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.CustomConversions;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraAdminOperations;
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
@@ -37,6 +41,7 @@ import org.springframework.data.mapping.context.MappingContext;
* @author Alex Shvid
* @author Matthew T. Adams
* @author John Blum
* @author Mark Paluch
*/
@Configuration
public abstract class AbstractCassandraConfiguration extends AbstractClusterConfiguration
@@ -72,15 +77,44 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf
mappingContext.setBeanClassLoader(beanClassLoader);
mappingContext.setInitialEntitySet(CassandraEntityClassScanner.scan(getEntityBasePackages()));
CustomConversions customConversions = customConversions();
mappingContext.setCustomConversions(customConversions);
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
return mappingContext;
}
/**
* Return the {@link CassandraConverter} instance to convert Rows to Objects, Objects to BuiltStatements
* Register custom {@link Converter}s in a {@link CustomConversions} object if required. These
* {@link CustomConversions} will be registered with the {@link #cassandraConverter()} and {@link #cassandraMapping()}
* . Returns an empty {@link CustomConversions} instance by default.
*
* @return must not be {@literal null}.
* @since 1.5
*/
@Bean
public CustomConversions customConversions() {
return new CustomConversions(Collections.emptyList());
}
/**
* Creates a {@link CassandraConverter} using the configured {@link #cassandraMapping()}. Will get
* {@link #customConversions()} applied.
*
* @see #customConversions()
* @see #cassandraMapping()
* @return
* @throws Exception
*/
@Bean
public CassandraConverter cassandraConverter() throws Exception {
return new MappingCassandraConverter(cassandraMapping());
MappingCassandraConverter mappingCassandraConverter = new MappingCassandraConverter(cassandraMapping());
mappingCassandraConverter.setCustomConversions(customConversions());
return mappingCassandraConverter;
}
/**
@@ -99,19 +133,22 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf
}
/**
* Base packages to scan for entities annotated with {@link Table} annotations. By default, returns the package
* name of {@literal this} (<code>this.getClass().getPackage().getName()</code>).
*
* This method must never return null.
* Base packages to scan for entities annotated with {@link Table} annotations. By default, returns the package name
* of {@literal this} (<code>this.getClass().getPackage().getName()</code>). This method must never return null.
*/
public String[] getEntityBasePackages() {
return new String[] { getClass().getPackage().getName() };
}
/**
* Return the name of the keyspace to connect to.
*
* @return must not be {@literal null}.
*/
protected abstract String getKeyspaceName();
/**
* The {@link SchemaAction} to perform at startup. Defaults to {@link SchemaAction#NONE}.
* The {@link SchemaAction} to perform at startup. Defaults to {@link SchemaAction#NONE}.
*/
public SchemaAction getSchemaAction() {
return SchemaAction.NONE;

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2016 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.convert;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.joda.time.DateMidnight;
import org.joda.time.LocalDate;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.ClassUtils;
/**
* Helper class to register JSR-310 specific {@link Converter} implementations to convert between Cassandra types in
* case the library is present on the classpath.
*
* @author Mark Paluch
* @since 1.5
*/
@SuppressWarnings("deprecation")
public abstract class CassandraJodaTimeConverters {
private static final boolean JODA_TIME_IS_PRESENT = ClassUtils.isPresent("org.joda.time.LocalDate", null);
private CassandraJodaTimeConverters() {}
/**
* Returns the converters to be registered. Will only return converters in case JodaTime is present on the class path.
*
* @return
*/
public static Collection<Converter<?, ?>> getConvertersToRegister() {
if (!JODA_TIME_IS_PRESENT) {
return Collections.emptySet();
}
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(CassandraLocalDateToLocalDateConverter.INSTANCE);
converters.add(LocalDateToCassandraLocalDateConverter.INSTANCE);
converters.add(CassandraLocalDateToDateMidnightConverter.INSTANCE);
converters.add(DateMidnightToCassandraLocalDateConverter.INSTANCE);
return converters;
}
/**
* Simple singleton to convert {@link com.datastax.driver.core.LocalDate}s to their {@link LocalDate} representation.
*
* @author Mark Paluch
*/
public static enum CassandraLocalDateToLocalDateConverter
implements Converter<com.datastax.driver.core.LocalDate, LocalDate> {
INSTANCE;
@Override
public LocalDate convert(com.datastax.driver.core.LocalDate source) {
return new LocalDate(source.getYear(), source.getMonth(), source.getDay());
}
}
/**
* Simple singleton to convert {@link LocalDate}s to their {@link com.datastax.driver.core.LocalDate} representation.
*
* @author Mark Paluch
*/
public static enum LocalDateToCassandraLocalDateConverter
implements Converter<LocalDate, com.datastax.driver.core.LocalDate> {
INSTANCE;
@Override
public com.datastax.driver.core.LocalDate convert(LocalDate source) {
return com.datastax.driver.core.LocalDate.fromYearMonthDay(source.getYear(), source.getMonthOfYear(),
source.getDayOfMonth());
}
}
/**
* Simple singleton to convert {@link com.datastax.driver.core.LocalDate}s to their {@link DateMidnight}
* representation.
*
* @author Mark Paluch
*/
public static enum CassandraLocalDateToDateMidnightConverter
implements Converter<com.datastax.driver.core.LocalDate, DateMidnight> {
INSTANCE;
@Override
public DateMidnight convert(com.datastax.driver.core.LocalDate source) {
return new DateMidnight(source.getYear(), source.getMonth(), source.getDay());
}
}
/**
* Simple singleton to convert {@link DateMidnight}s to their {@link com.datastax.driver.core.LocalDate}
* representation.
*
* @author Mark Paluch
*/
public static enum DateMidnightToCassandraLocalDateConverter
implements Converter<DateMidnight, com.datastax.driver.core.LocalDate> {
INSTANCE;
@Override
public com.datastax.driver.core.LocalDate convert(DateMidnight source) {
return com.datastax.driver.core.LocalDate.fromYearMonthDay(source.getYear(), source.getMonthOfYear(),
source.getDayOfMonth());
}
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2016 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.convert;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.Jsr310Converters;
import org.springframework.util.ClassUtils;
/**
* Helper class to register JodaTime specific {@link Converter} implementations in case the library is present on the
* classpath.
*
* @author Mark Paluch
* @since 1.5
*/
@SuppressWarnings("Since15")
public abstract class CassandraJsr310Converters {
private static final boolean JAVA_8_IS_PRESENT = ClassUtils.isPresent("java.time.LocalDateTime",
Jsr310Converters.class.getClassLoader());
private CassandraJsr310Converters() {}
/**
* Returns the converters to be registered. Will only return converters in case we're running on Java 8.
*
* @return
*/
public static Collection<Converter<?, ?>> getConvertersToRegister() {
if (!JAVA_8_IS_PRESENT) {
return Collections.emptySet();
}
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(CassandraLocalDateToLocalDateConverter.INSTANCE);
converters.add(LocalDateToCassandraLocalDateConverter.INSTANCE);
return converters;
}
/**
* Simple singleton to convert {@link com.datastax.driver.core.LocalDate}s to their {@link LocalDate} representation.
*
* @author Mark Paluch
*/
public static enum CassandraLocalDateToLocalDateConverter
implements Converter<com.datastax.driver.core.LocalDate, LocalDate> {
INSTANCE;
@Override
public LocalDate convert(com.datastax.driver.core.LocalDate source) {
return LocalDate.of(source.getYear(), source.getMonth(), source.getDay());
}
}
/**
* Simple singleton to convert {@link LocalDate}s to their {@link com.datastax.driver.core.LocalDate} representation.
*
* @author Mark Paluch
*/
public static enum LocalDateToCassandraLocalDateConverter
implements Converter<LocalDate, com.datastax.driver.core.LocalDate> {
INSTANCE;
@Override
public com.datastax.driver.core.LocalDate convert(LocalDate source) {
return com.datastax.driver.core.LocalDate.fromYearMonthDay(source.getYear(), source.getMonthValue(),
source.getDayOfMonth());
}
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2016 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.convert;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ThreeTenBackPortConverters;
import org.springframework.util.ClassUtils;
import org.threeten.bp.Instant;
import org.threeten.bp.LocalDate;
import org.threeten.bp.LocalDateTime;
import org.threeten.bp.ZoneId;
/**
* Helper class to register {@link Converter} implementations for the ThreeTen Backport project in case it's present on
* the classpath.
*
* @author Mark Paluch
* @see http://www.threeten.org/threetenbp
* @since 1.5
*/
public abstract class CassandraThreeTenBackPortConverters {
private static final boolean THREE_TEN_BACK_PORT_IS_PRESENT = ClassUtils.isPresent("org.threeten.bp.LocalDateTime",
ThreeTenBackPortConverters.class.getClassLoader());
private CassandraThreeTenBackPortConverters() {}
/**
* Returns the converters to be registered. Will only return converters in case ThreeTen Backport is on the class
* path.
*
* @return
*/
public static Collection<Converter<?, ?>> getConvertersToRegister() {
if (!THREE_TEN_BACK_PORT_IS_PRESENT) {
return Collections.emptySet();
}
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(CassandraLocalDateToLocalDateConverter.INSTANCE);
converters.add(LocalDateToCassandraLocalDateConverter.INSTANCE);
return converters;
}
/**
* Simple singleton to convert {@link com.datastax.driver.core.LocalDate}s to their {@link LocalDate} representation.
*
* @author Mark Paluch
*/
public static enum CassandraLocalDateToLocalDateConverter
implements Converter<com.datastax.driver.core.LocalDate, LocalDate> {
INSTANCE;
@Override
public LocalDate convert(com.datastax.driver.core.LocalDate source) {
return LocalDate.of(source.getYear(), source.getMonth(), source.getDay());
}
}
/**
* Simple singleton to convert {@link LocalDate}s to their {@link com.datastax.driver.core.LocalDate} representation.
*
* @author Mark Paluch
*/
public static enum LocalDateToCassandraLocalDateConverter
implements Converter<LocalDate, com.datastax.driver.core.LocalDate> {
INSTANCE;
@Override
public com.datastax.driver.core.LocalDate convert(LocalDate source) {
return com.datastax.driver.core.LocalDate.fromYearMonthDay(source.getYear(), source.getMonthValue(),
source.getDayOfMonth());
}
}
}

View File

@@ -35,7 +35,10 @@ import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder;
import org.springframework.data.convert.JodaTimeConverters;
import org.springframework.data.convert.Jsr310Converters;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.ThreeTenBackPortConverters;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.CacheValue;
@@ -59,7 +62,7 @@ public class CustomConversions {
private final Set<ConvertiblePair> readingPairs;
private final Set<ConvertiblePair> writingPairs;
private final Set<Class<?>> customSimpleTypes;
private final SimpleTypeHolder simpleTypeHolder;
private final CassandraSimpleTypeHolder simpleTypeHolder;
private final List<Object> converters;
@@ -96,6 +99,14 @@ public class CustomConversions {
toRegister.addAll(converters);
toRegister.addAll(CassandraConverters.getConvertersToRegister());
toRegister.addAll(CassandraJodaTimeConverters.getConvertersToRegister());
toRegister.addAll(CassandraJsr310Converters.getConvertersToRegister());
toRegister.addAll(CassandraThreeTenBackPortConverters.getConvertersToRegister());
toRegister.addAll(JodaTimeConverters.getConvertersToRegister());
toRegister.addAll(Jsr310Converters.getConvertersToRegister());
toRegister.addAll(ThreeTenBackPortConverters.getConvertersToRegister());
for (Object c : toRegister) {
registerConversion(c);
}
@@ -106,6 +117,15 @@ public class CustomConversions {
this.simpleTypeHolder = new CassandraSimpleTypeHolder();
}
/**
* Returns the underlying {@link SimpleTypeHolder}.
*
* @return
*/
public SimpleTypeHolder getSimpleTypeHolder() {
return simpleTypeHolder;
}
/**
* Returns whether the given type is considered to be simple. That means it's either a general simple type or we have
* a writing {@link Converter} registered for a particular type.

View File

@@ -18,6 +18,7 @@ package org.springframework.data.cassandra.convert;
import static org.springframework.data.cassandra.repository.support.BasicMapId.*;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import org.slf4j.Logger;
@@ -26,6 +27,7 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
@@ -45,6 +47,7 @@ import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.Row;
@@ -71,15 +74,18 @@ import com.datastax.driver.core.querybuilder.Update;
* @see org.springframework.data.convert.EntityWriter
*/
public class MappingCassandraConverter extends AbstractCassandraConverter
implements CassandraConverter, ApplicationContextAware, BeanClassLoaderAware {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final CassandraMappingContext mappingContext;
implements CassandraConverter, ApplicationContextAware, BeanClassLoaderAware {
protected ApplicationContext applicationContext;
protected SpELContext spELContext;
protected final CassandraMappingContext mappingContext;
protected ClassLoader beanClassLoader;
protected final Logger log = LoggerFactory.getLogger(getClass());
protected SpELContext spELContext;
/**
* Creates a new {@link MappingCassandraConverter} with a {@link BasicCassandraMappingContext}.
*/
@@ -96,7 +102,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
super(new DefaultConversionService());
Assert.notNull(mappingContext);
Assert.notNull(mappingContext, "CassandraMappingContext must not be null");
this.mappingContext = mappingContext;
this.spELContext = new SpELContext(RowReaderPropertyAccessor.INSTANCE);
@@ -131,6 +137,9 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
return readEntityFromRow(persistentEntity, row);
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
@@ -154,20 +163,19 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
protected void readPropertiesFromRow(final CassandraPersistentEntity<?> entity,
final BasicCassandraRowValueProvider row, final PersistentPropertyAccessor accessor) {
final BasicCassandraRowValueProvider row, final PersistentPropertyAccessor propertyAccessor) {
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
MappingCassandraConverter.this.readPropertyFromRow(entity, prop, row, accessor);
public void doWithPersistentProperty(CassandraPersistentProperty property) {
MappingCassandraConverter.this.readPropertyFromRow(entity, property, row, propertyAccessor);
}
});
}
// TODO argh! too many return statements!
protected void readPropertyFromRow(final CassandraPersistentEntity<?> entity, final CassandraPersistentProperty property,
final BasicCassandraRowValueProvider row, final PersistentPropertyAccessor accessor) {
protected void readPropertyFromRow(CassandraPersistentEntity<?> entity, CassandraPersistentProperty property,
BasicCassandraRowValueProvider row, PersistentPropertyAccessor propertyAccessor) {
// if true then skip; property was set in constructor
if (entity.isConstructorArgument(property)) {
@@ -179,7 +187,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
CassandraPersistentProperty keyProperty = entity.getIdProperty();
CassandraPersistentEntity<?> keyEntity = keyProperty.getCompositePrimaryKeyEntity();
Object key = accessor.getProperty(keyProperty);
Object key = propertyAccessor.getProperty(keyProperty);
if (key == null) {
key = instantiatePrimaryKey(keyEntity, keyProperty, row);
@@ -189,7 +197,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
readPropertiesFromRow(property.getCompositePrimaryKeyEntity(), row, getConvertingAccessor(key, keyEntity));
// now that the key's properties have been populated, set the key property on the entity
accessor.setProperty(keyProperty, key);
propertyAccessor.setProperty(keyProperty, key);
return;
}
@@ -198,17 +206,22 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
return;
}
accessor.setProperty(property, row.getPropertyValue(property));
Object obj = getReadValue(property, row);
propertyAccessor.setProperty(property, obj);
}
@SuppressWarnings("unused")
protected Object instantiatePrimaryKey(CassandraPersistentEntity<?> entity, CassandraPersistentProperty keyProperty,
BasicCassandraRowValueProvider propertyProvider) {
BasicCassandraRowValueProvider propertyProvider) {
return instantiators.getInstantiatorFor(entity).createInstance(entity,
new CassandraPersistentEntityParameterValueProvider(entity, propertyProvider, null));
}
/* (non-Javadoc)
* @see org.springframework.data.convert.EntityReader#read(java.lang.Class, S)
*/
@Override
public <R> R read(Class<R> type, Object row) {
@@ -216,7 +229,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
return readRow(type, (Row) row);
}
throw new MappingException("Unknown row object " + row.getClass().getName());
throw new MappingException("Unknown row object " + ObjectUtils.nullSafeClassName(row));
}
@Override
@@ -248,15 +261,14 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
protected void writeInsertFromWrapper(final ConvertingPropertyAccessor accessor, final Insert insert,
CassandraPersistentEntity<?> entity) {
CassandraPersistentEntity<?> entity) {
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty property) {
Object value = accessor.getProperty(property, property.isCompositePrimaryKey() ? property.getType()
: CodecRegistry.DEFAULT_INSTANCE.codecFor(property.getDataType()).getJavaType().getRawType());
Object value = getWriteValue(property, accessor);
if (log.isDebugEnabled()) {
log.debug("doWithProperties Property.type {}, Property.value {}", property.getType().getName(), value);
@@ -268,7 +280,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
writeInsertFromWrapper(getConvertingAccessor(value, property.getCompositePrimaryKeyEntity()), insert,
property.getCompositePrimaryKeyEntity());
property.getCompositePrimaryKeyEntity());
return;
}
@@ -287,15 +299,14 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
protected void writeUpdateFromWrapper(final ConvertingPropertyAccessor accessor, final Update update,
final CassandraPersistentEntity<?> entity) {
final CassandraPersistentEntity<?> entity) {
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty property) {
Object value = accessor.getProperty(property, property.isCompositePrimaryKey() ? property.getType()
: CodecRegistry.DEFAULT_INSTANCE.codecFor(property.getDataType()).getJavaType().getRawType());
Object value = getWriteValue(property, accessor);
if (property.isCompositePrimaryKey()) {
CassandraPersistentEntity<?> keyEntity = property.getCompositePrimaryKeyEntity();
@@ -313,12 +324,12 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
protected void writeDeleteWhereFromObject(final Object object, final Where where,
CassandraPersistentEntity<?> entity) {
CassandraPersistentEntity<?> entity) {
writeDeleteWhereFromWrapper(getConvertingAccessor(object, entity), where, entity);
}
protected void writeDeleteWhereFromWrapper(final ConvertingPropertyAccessor accessor, final Where where,
CassandraPersistentEntity<?> entity) {
CassandraPersistentEntity<?> entity) {
// if the entity itself if a composite primary key, then we've recursed, so just add columns & return
if (entity.isCompositePrimaryKey()) {
@@ -326,19 +337,22 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
Object value = accessor.getProperty(prop, CodecRegistry.DEFAULT_INSTANCE.codecFor(prop.getDataType()).getJavaType().getRawType());
Object value = accessor.getProperty(prop,
CodecRegistry.DEFAULT_INSTANCE.codecFor(prop.getDataType()).getJavaType().getRawType());
where.and(QueryBuilder.eq(prop.getColumnName().toCql(), value));
}
});
return;
}
// else, wrapper is an entity with an id
Object id = getId(accessor, entity);
if (id == null) {
String msg = String.format("no id value found in object %s", accessor.getBean());
log.error(msg);
throw new IllegalArgumentException(msg);
String message = String.format("no id value found in object %s", accessor.getBean());
log.error(message);
throw new IllegalArgumentException(message);
}
if (id instanceof MapId) {
@@ -358,7 +372,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
CassandraPersistentEntity<?> idEntity = idProperty.getCompositePrimaryKeyEntity();
writeDeleteWhereFromWrapper(getConvertingAccessor(id, idEntity), where,
idProperty.getCompositePrimaryKeyEntity());
idProperty.getCompositePrimaryKeyEntity());
return;
}
@@ -372,12 +387,13 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
Assert.notNull(object);
final ConvertingPropertyAccessor wrapper = getConvertingAccessor(object, entity);
object = wrapper.getBean();
if (!entity.getType().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException(
String.format("given instance of type [%s] is not of compatible expected type [%s]",
object.getClass().getName(), entity.getType().getName()));
String.format("given instance of type [%s] is not of compatible expected type [%s]",
object.getClass().getName(), entity.getType().getName()));
}
if (object instanceof MapIdentifiable) {
@@ -385,13 +401,15 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
CassandraPersistentProperty idProperty = entity.getIdProperty();
if (idProperty != null) {
return wrapper.getProperty(entity.getIdProperty(),
idProperty.isCompositePrimaryKey() ? idProperty.getType() : CodecRegistry.DEFAULT_INSTANCE.codecFor(idProperty.getDataType()).getJavaType().getRawType());
return wrapper.getProperty(entity.getIdProperty(), idProperty.isCompositePrimaryKey() ? idProperty.getType()
: CodecRegistry.DEFAULT_INSTANCE.codecFor(idProperty.getDataType()).getJavaType().getRawType());
}
// if the class doesn't have an id property, then it's using MapId
final MapId id = id();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
@@ -436,10 +454,10 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
*/
private ConvertingPropertyAccessor getConvertingAccessor(Object source, CassandraPersistentEntity<?> entity) {
PersistentPropertyAccessor accessor = (source instanceof PersistentPropertyAccessor
? (PersistentPropertyAccessor) source : entity.getPropertyAccessor(source));
PersistentPropertyAccessor propertyAccessor = (source instanceof PersistentPropertyAccessor
? (PersistentPropertyAccessor) source : entity.getPropertyAccessor(source));
return new ConvertingPropertyAccessor(accessor, conversionService);
return new ConvertingPropertyAccessor(propertyAccessor, conversionService);
}
/**
@@ -451,4 +469,77 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
private boolean isPrimaryKeyPart(CassandraPersistentProperty property) {
return (property.isCompositePrimaryKey() || property.isPrimaryKeyColumn() || property.isIdProperty());
}
private Class<?> getTargetType(CassandraPersistentProperty property) {
return (property.isCompositePrimaryKey() ? property.getType() : CodecRegistry.DEFAULT_INSTANCE.codecFor(
mappingContext.getDataType(property)).getJavaType().getRawType());
}
/**
* Retrieve the value to write for the given {@link CassandraPersistentProperty} from
* {@link ConvertingPropertyAccessor} and perform optionally a conversion of collection element types.
*
* @param property the property.
* @param accessor the property accessor
* @return the return value, may be {@literal null}.
*/
@SuppressWarnings("unchecked")
private Object getWriteValue(CassandraPersistentProperty property, ConvertingPropertyAccessor accessor) {
Object value = accessor.getProperty(property, getTargetType(property));
if (conversions.hasCustomWriteTarget(property.getActualType()) && property.isCollectionLike()) {
Class<?> customWriteTarget = conversions.getCustomWriteTarget(property.getActualType());
if (Collection.class.isAssignableFrom(property.getType()) && value instanceof Collection) {
Collection<Object> original = (Collection<Object>) value;
Collection<Object> converted = CollectionFactory.createCollection(property.getType(), original.size());
for (Object o : original) {
converted.add(getConversionService().convert(o, customWriteTarget));
}
value = converted;
}
}
return value;
}
/**
* Retrieve the value to read for the given {@link CassandraPersistentProperty} from
* {@link BasicCassandraRowValueProvider} and perform optionally a conversion of collection element types.
*
* @param property the property.
* @param row the row.
* @return the return value, may be {@literal null}.
*/
@SuppressWarnings("unchecked")
private Object getReadValue(CassandraPersistentProperty property, BasicCassandraRowValueProvider row) {
Object obj = row.getPropertyValue(property);
if (obj == null) {
return null;
}
if (conversions.hasCustomWriteTarget(property.getActualType()) && property.isCollectionLike()) {
if (Collection.class.isAssignableFrom(property.getType()) && obj instanceof Collection) {
Collection<Object> original = (Collection<Object>) obj;
Collection<Object> converted = CollectionFactory.createCollection(property.getType(), original.size());
for (Object o : original) {
converted.add(getConversionService().convert(o, property.getActualType()));
}
return converted;
}
}
return obj;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors
* Copyright 2013-2016 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.
@@ -18,6 +18,7 @@ package org.springframework.data.cassandra.mapping;
import static org.springframework.cassandra.core.cql.CqlIdentifier.*;
import static org.springframework.cassandra.core.keyspace.CreateTableSpecification.*;
import static org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder.*;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
@@ -25,6 +26,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -33,6 +35,7 @@ import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.cassandra.convert.CustomConversions;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.context.MappingContext;
@@ -43,6 +46,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.TableMetadata;
/**
@@ -51,8 +55,10 @@ import com.datastax.driver.core.TableMetadata;
*
* @author Alex Shvid
* @author Matthew T. Adams
* @author Mark Paluch
*/
public class BasicCassandraMappingContext extends AbstractMappingContext<CassandraPersistentEntity<?>, CassandraPersistentProperty>
public class BasicCassandraMappingContext
extends AbstractMappingContext<CassandraPersistentEntity<?>, CassandraPersistentProperty>
implements CassandraMappingContext, ApplicationContextAware {
protected ApplicationContext context;
@@ -70,13 +76,30 @@ public class BasicCassandraMappingContext extends AbstractMappingContext<Cassand
protected Set<CassandraPersistentEntity<?>> nonPrimaryKeyEntities = new HashSet<CassandraPersistentEntity<?>>();
protected Set<CassandraPersistentEntity<?>> primaryKeyEntities = new HashSet<CassandraPersistentEntity<?>>();
private CustomConversions customConversions;
/**
* Creates a new {@link BasicCassandraMappingContext}.
*/
public BasicCassandraMappingContext() {
setCustomConversions(new CustomConversions(Collections.EMPTY_LIST));
setSimpleTypeHolder(CassandraSimpleTypeHolder.HOLDER);
}
/**
* Sets the {@link CustomConversions}.
*
* @param customConversions must not be {@literal null}.
* @since 1.5
*/
public void setCustomConversions(CustomConversions customConversions) {
Assert.notNull(customConversions, "CustomConversions must not be null");
this.customConversions = customConversions;
}
@Override
public void initialize() {
super.initialize();
@@ -182,21 +205,20 @@ public class BasicCassandraMappingContext extends AbstractMappingContext<Cassand
public void doWithPersistentProperty(CassandraPersistentProperty pkProp) {
if (pkProp.isPartitionKeyColumn()) {
spec.partitionKeyColumn(pkProp.getColumnName(), pkProp.getDataType());
spec.partitionKeyColumn(pkProp.getColumnName(), getDataType(pkProp));
} else { // it's a cluster column
spec.clusteredKeyColumn(pkProp.getColumnName(), pkProp.getDataType(),
pkProp.getPrimaryKeyOrdering());
spec.clusteredKeyColumn(pkProp.getColumnName(), getDataType(pkProp), pkProp.getPrimaryKeyOrdering());
}
}
});
} else {
if (prop.isIdProperty() || prop.isPartitionKeyColumn()) {
spec.partitionKeyColumn(prop.getColumnName(), prop.getDataType());
spec.partitionKeyColumn(prop.getColumnName(), getDataType(prop));
} else if (prop.isClusterKeyColumn()) {
spec.clusteredKeyColumn(prop.getColumnName(), prop.getDataType());
spec.clusteredKeyColumn(prop.getColumnName(), getDataType(prop));
} else {
spec.column(prop.getColumnName(), prop.getDataType());
spec.column(prop.getColumnName(), getDataType(prop));
}
}
}
@@ -210,6 +232,82 @@ public class BasicCassandraMappingContext extends AbstractMappingContext<Cassand
return spec;
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.context.AbstractMappingContext#shouldCreatePersistentEntityFor(org.springframework.data.util.TypeInformation)
*/
@Override
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
if (customConversions.hasCustomWriteTarget(type.getType())) {
return false;
}
return super.shouldCreatePersistentEntityFor(type);
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.context.AbstractMappingContext#addPersistentEntity(org.springframework.data.util.TypeInformation)
*/
@Override
protected CassandraPersistentEntity<?> addPersistentEntity(TypeInformation<?> typeInformation) {
// Prevent conversion types created as CassandraPersistentEntity
if (shouldCreatePersistentEntityFor(typeInformation)) {
return super.addPersistentEntity(typeInformation);
}
return null;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.mapping.CassandraMappingContext#getDataType(org.springframework.data.cassandra.mapping.CassandraPersistentProperty)
*/
@Override
public DataType getDataType(CassandraPersistentProperty property) {
if (property.isCompositePrimaryKey()) {
return property.getDataType();
}
if (property.findAnnotation(CassandraType.class) != null) {
return property.getDataType();
}
if (customConversions.hasCustomWriteTarget(property.getActualType())) {
Class<?> targetType = customConversions.getCustomWriteTarget(property.getActualType());
if (property.isCollectionLike()) {
if (Set.class.isAssignableFrom(property.getType())) {
return DataType.set(getDataTypeFor(targetType));
}
if (List.class.isAssignableFrom(property.getType())) {
return DataType.list(getDataTypeFor(targetType));
}
}
return getDataTypeFor(targetType);
}
return property.getDataType();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.mapping.CassandraMappingContext#getDataType(java.lang.Class)
*/
@Override
public DataType getDataType(Class<?> type) {
if (customConversions.hasCustomWriteTarget(type)) {
return getDataTypeFor(customConversions.getCustomWriteTarget(type));
}
return getDataTypeFor(type);
}
public void setMapping(Mapping mapping) {
Assert.notNull(mapping);
@@ -236,15 +334,13 @@ public class BasicCassandraMappingContext extends AbstractMappingContext<Cassand
try {
entityClass = ClassUtils.forName(entityClassName, beanClassLoader);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(String.format("unknown persistent entity name [%s]",
entityClassName), e);
throw new IllegalStateException(String.format("unknown persistent entity name [%s]", entityClassName), e);
}
CassandraPersistentEntity<?> entity = getPersistentEntity(entityClass);
if (entity == null) {
throw new IllegalStateException(String.format("unknown persistent entity class name [%s]",
entityClassName));
throw new IllegalStateException(String.format("unknown persistent entity class name [%s]", entityClassName));
}
String tableName = entityMapping.getTableName();
@@ -270,7 +366,7 @@ public class BasicCassandraMappingContext extends AbstractMappingContext<Cassand
if (property == null) {
throw new IllegalArgumentException(String.format("entity class [%s] has no persistent property named [%s]",
entity.getType().getName(), mapping.getPropertyName()));
entity.getType().getName(), mapping.getPropertyName()));
}
boolean forceQuote = false;

View File

@@ -142,9 +142,25 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return (primaryKeyColumn != null ? primaryKeyColumn.ordering() : null);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.mapping.CassandraPersistentProperty#getDataType()
*/
@Override
public DataType getDataType() {
DataType dataType = findDataType();
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(String.format(
"unknown type for property [%s], type [%s] in entity [%s]; only primitive types and collections or maps of primitive types are allowed",
getName(), getType(), getOwner().getName()));
}
return dataType;
}
private DataType findDataType() {
CassandraType cassandraType = findAnnotation(CassandraType.class);
if (cassandraType != null) {
@@ -173,15 +189,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
}
}
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(getType());
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(String.format(
"unknown type for property [%s], type [%s] in entity [%s]; only primitive types and collections or maps of primitive types are allowed",
getName(), getType(), getOwner().getName()));
}
return dataType;
return CassandraSimpleTypeHolder.getDataTypeFor(getType());
}
private DataType getDataTypeFor(CassandraType annotation) {
@@ -192,7 +200,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
case MAP:
ensureTypeArguments(annotation.typeArguments().length, 2);
return DataType.map(getDataTypeFor(annotation.typeArguments()[0]),
getDataTypeFor(annotation.typeArguments()[1]));
getDataTypeFor(annotation.typeArguments()[1]));
case LIST:
ensureTypeArguments(annotation.typeArguments().length, 1);
return DataType.list(getDataTypeFor(annotation.typeArguments()[0]));
@@ -235,8 +243,8 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(String.format(
"only primitive types are allowed inside collections for the property '%1$s' type is '%2$s' in the entity %3$s",
getName(), getType(), getOwner().getName()));
"only primitive types are allowed inside collections for the property '%1$s' type is '%2$s' in the entity %3$s",
getName(), getType(), getOwner().getName()));
}
return dataType;
@@ -248,7 +256,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(String.format(
"only primitive types are allowed inside collections for the property '%1$s' type is '%2$s' in the entity %3$s",
getName(), getType(), getOwner().getName()));
getName(), getType(), getOwner().getName()));
}
return dataType;
@@ -256,8 +264,8 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
protected void ensureTypeArguments(int args, int expected) {
if (args != expected) {
throw new InvalidDataAccessApiUsageException(String.format(
"expected %1$s of typed arguments for the property '%2$s' type is '%3$s' in the entity %4$s",
throw new InvalidDataAccessApiUsageException(
String.format("expected %1$s of typed arguments for the property '%2$s' type is '%3$s' in the entity %4$s",
expected, getName(), getType(), getOwner().getName()));
}
}
@@ -278,8 +286,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
if (isCompositePrimaryKey()) { // then the id type has @PrimaryKeyClass
addCompositePrimaryKeyColumnNames(getCompositePrimaryKeyEntity(), columnNames);
}
else { // else we're dealing with a single-column field
} else { // else we're dealing with a single-column field
String defaultName = getName(); // TODO: replace with naming strategy class
String overriddenName;
boolean forceQuote;
@@ -355,8 +362,8 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
columnNames.size()));
}
this.columnNames = this.explicitColumnNames = Collections.unmodifiableList(
new ArrayList<CqlIdentifier>(columnNames));
this.columnNames = this.explicitColumnNames = Collections
.unmodifiableList(new ArrayList<CqlIdentifier>(columnNames));
}
@Override
@@ -368,8 +375,8 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
this.forceQuote = forceQuote;
}
List<CqlIdentifier> columnNames = new ArrayList<CqlIdentifier>(this.columnNames == null ? 0
: this.columnNames.size());
List<CqlIdentifier> columnNames = new ArrayList<CqlIdentifier>(
this.columnNames == null ? 0 : this.columnNames.size());
for (CqlIdentifier columnName : getColumnNames()) {
columnNames.add(cqlId(columnName.getUnquoted(), forceQuote));
@@ -382,8 +389,8 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
public List<CassandraPersistentProperty> getCompositePrimaryKeyProperties() {
if (!isCompositePrimaryKey()) {
throw new IllegalStateException(String.format(
"[%s] does not represent a composite primary key property", getName()));
throw new IllegalStateException(
String.format("[%s] does not represent a composite primary key property", getName()));
}
return getCompositePrimaryKeyEntity().getCompositePrimaryKeyProperties();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors
* Copyright 2013-2016 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.
@@ -18,17 +18,20 @@ package org.springframework.data.cassandra.mapping;
import java.util.Collection;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.data.cassandra.convert.CustomConversions;
import org.springframework.data.mapping.context.MappingContext;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.TableMetadata;
/**
* A {@link MappingContext} for Cassandra.
*
* @author Matthew T. Adams
* @author Mark Paluch
*/
public interface CassandraMappingContext extends
MappingContext<CassandraPersistentEntity<?>, CassandraPersistentProperty> {
public interface CassandraMappingContext
extends MappingContext<CassandraPersistentEntity<?>, CassandraPersistentProperty> {
/**
* Returns only those entities that don't represent primary key types.
@@ -36,7 +39,7 @@ public interface CassandraMappingContext extends
* @see #getPersistentEntities(boolean)
*/
@Override
public Collection<CassandraPersistentEntity<?>> getPersistentEntities();
Collection<CassandraPersistentEntity<?>> getPersistentEntities();
/**
* Returns all persistent entities or only non-primary-key entities.
@@ -44,7 +47,7 @@ public interface CassandraMappingContext extends
* @param includePrimaryKeyTypes If <code>true</code>, returns all entities, including entities that represent primary
* key types. If <code>false</code>, returns only entities that don't represent primary key types.
*/
public Collection<CassandraPersistentEntity<?>> getPersistentEntities(boolean includePrimaryKeyTypes);
Collection<CassandraPersistentEntity<?>> getPersistentEntities(boolean includePrimaryKeyTypes);
/**
* Returns only those entities representing primary key types.
@@ -91,4 +94,28 @@ public interface CassandraMappingContext extends
* Sets a verifier other than the {@link BasicCassandraPersistentEntityMetadataVerifier}
*/
void setVerifier(CassandraPersistentEntityMetadataVerifier verifier);
/**
* Retrieve the data type of the property. Cassandra {@link DataType types} are determined using simple types and
* configured {@link CustomConversions}.
*
* @param property must not be {@literal null}.
* @return the Cassandra {@link DataType type}.
* @see CustomConversions
* @see CassandraSimpleTypeHolder
* @since 1.5
*/
DataType getDataType(CassandraPersistentProperty property);
/**
* Retrieve the data type based on the given {@code type}. Cassandra {@link DataType types} are determined using simple types and
* configured {@link CustomConversions}.
*
* @param type must not be {@literal null}.
* @return the Cassandra {@link DataType type}.
* @see CustomConversions
* @see CassandraSimpleTypeHolder
* @since 1.5
*/
DataType getDataType(Class<?> type);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors
* Copyright 2013-2016 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.
@@ -21,6 +21,7 @@ import org.springframework.cassandra.core.Ordering;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.TypeInformation;
@@ -32,9 +33,10 @@ import com.datastax.driver.core.DataType;
* @author Alex Shvid
* @author Matthew T. Adams
* @author David T. Webb
* @author Mark Paluch
*/
public interface CassandraPersistentProperty extends PersistentProperty<CassandraPersistentProperty>,
ApplicationContextAware {
public interface CassandraPersistentProperty
extends PersistentProperty<CassandraPersistentProperty>, ApplicationContextAware {
/**
* Whether the property is a composite primary key.
@@ -78,7 +80,11 @@ public interface CassandraPersistentProperty extends PersistentProperty<Cassandr
Ordering getPrimaryKeyOrdering();
/**
* The column's data type. Not valid for a composite primary key, in which case this method returns null.
* The column's data type. Not valid for a composite primary key.
*
* @return the Cassandra {@link DataType}
* @throws InvalidDataAccessApiUsageException if the {@link DataType} cannot be resolved
* @see CassandraType
*/
DataType getDataType();

View File

@@ -29,6 +29,7 @@ import org.springframework.data.util.TypeInformation;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.DataType.Name;
import com.datastax.driver.core.Row;
/**
* Simple constant holder for a {@link SimpleTypeHolder} enriched with Cassandra specific simple types.
@@ -61,6 +62,7 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder {
Set<Class<?>> simpleTypes = getCassandraPrimitiveTypes(codecRegistry);
simpleTypes.add(Number.class);
simpleTypes.add(Row.class);
classToDataType = Collections.unmodifiableMap(classToDataType(primitiveWrappers, codecRegistry));
nameToDataType = Collections.unmodifiableMap(nameToDataType());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors
* Copyright 2013-2016 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.
@@ -22,10 +22,11 @@ import java.lang.annotation.RetentionPolicy;
import com.datastax.driver.core.DataType;
/**
* Specifies the Cassandra type of the annotated property.
* Specifies the Cassandra type of the annotated property or parameter if used in query methods.
*
* @author Alex Shvid
* @author Matthew T. Adams
* @author Mark Paluch
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -80,7 +80,7 @@ public abstract class AbstractCassandraQuery implements RepositoryQuery {
@Override
public Object execute(Object[] parameters) {
CassandraParameterAccessor accessor = new CassandraParametersParameterAccessor(method, parameters);
CassandraParameterAccessor accessor = new ConvertingParameterAccessor(template.getConverter(), new CassandraParametersParameterAccessor(method, parameters));
String query = createQuery(accessor);
ResultProcessor processor = method.getResultProcessor().withDynamicProjection(accessor);

View File

@@ -1,5 +1,51 @@
/*
* Copyright 2016 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.repository.query;
import org.springframework.data.repository.query.ParameterAccessor;
public interface CassandraParameterAccessor extends ParameterAccessor {}
import com.datastax.driver.core.DataType;
/**
* Cassandra-specific {@link ParameterAccessor} exposing a Cassandra {@link DataType types} that are supported by the
* driver and parameter type.
*
* @author Matthew Adams
* @author Mark Paluch
*/
public interface CassandraParameterAccessor extends ParameterAccessor {
/**
* Returns the Cassandra {@link DataType} for the declared parameter if the type is a
* {@link org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder simple type}. Parameter types may be
* specified using {@link org.springframework.data.cassandra.mapping.CassandraType}.
*
* @param index the parameter index
* @return the Cassandra {@link DataType} or {@literal null} if the parameter type cannot be determined from
* {@link org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder}
* @see org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder
* @see org.springframework.data.cassandra.mapping.CassandraType
*/
DataType getDataType(int index);
/**
* The actual parameter type (after unwrapping).
*
* @param index the parameter index
* @return the parameter type, never {@literal null}.
*/
Class<?> getParameterType(int index);
}

View File

@@ -1,37 +1,108 @@
/*
* Copyright 2016 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.repository.query;
import java.lang.reflect.Method;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder;
import org.springframework.data.cassandra.mapping.CassandraType;
import org.springframework.data.cassandra.repository.query.CassandraParameters.CassandraParameter;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
public class CassandraParameters extends Parameters<CassandraParameters, Parameter> {
import com.datastax.driver.core.DataType;
public CassandraParameters(List<Parameter> originals) {
super(originals);
}
/**
* Custom extension of {@link Parameters} discovering additional properties of query method parameters.
*
* @author Matthew Adams
* @author Mark Paluch
*/
public class CassandraParameters extends Parameters<CassandraParameters, CassandraParameter> {
/**
* Creates a new {@link CassandraParameters} instance from the given {@link Method}
*
* @param method must not be {@literal null}.
*/
public CassandraParameters(Method method) {
super(method);
}
private CassandraParameters(List<CassandraParameter> originals) {
super(originals);
}
/* (non-Javadoc)
* @see org.springframework.data.repository.query.Parameters#createParameter(org.springframework.core.MethodParameter)
*/
@Override
protected CassandraParameter createParameter(MethodParameter parameter) {
return new CassandraParameter(parameter);
}
/* (non-Javadoc)
* @see org.springframework.data.repository.query.Parameters#createFrom(java.util.List)
*/
@Override
protected CassandraParameters createFrom(List<Parameter> parameters) {
protected CassandraParameters createFrom(List<CassandraParameter> parameters) {
return new CassandraParameters(parameters);
}
/**
* Custom {@link Parameter} implementation adding {@link CassandraType} support.
*
* @author Mark Paluch
*/
class CassandraParameter extends Parameter {
private final DataType dataType;
protected CassandraParameter(MethodParameter parameter) {
super(parameter);
if (parameter.hasParameterAnnotation(CassandraType.class)) {
CassandraType cassandraType = parameter.getParameterAnnotation(CassandraType.class);
if (cassandraType.type() == null) {
throw new IllegalArgumentException(
String.format("You must specify the type() when annotating method parameters with @%s",
CassandraType.class.getSimpleName()));
}
this.dataType = CassandraSimpleTypeHolder.getDataTypeFor(cassandraType.type());
} else {
this.dataType = CassandraSimpleTypeHolder.getDataTypeFor(getType());
}
}
/**
* Returns the Cassandra {@link DataType} for the declared parameter if the type is a
* {@link org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder simple type}. Parameter types may be
* specified using {@link org.springframework.data.cassandra.mapping.CassandraType}.
*
* @return the Cassandra {@link DataType} or {@literal null} if the parameter type cannot be determined from
* {@link org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder}
*/
public DataType getCassandraType() {
return dataType;
}
}
}

View File

@@ -1,17 +1,71 @@
/*
* Copyright 2016 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.repository.query;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.ParametersParameterAccessor;
public class CassandraParametersParameterAccessor extends ParametersParameterAccessor implements
CassandraParameterAccessor {
import com.datastax.driver.core.DataType;
/**
* Cassandra-specific {@link ParameterAccessor} exposing a Cassandra {@link DataType types} that are supported by the
* driver and parameter type.
*
* @author Mark Paluch
*/
public class CassandraParametersParameterAccessor extends ParametersParameterAccessor
implements CassandraParameterAccessor {
/**
* Creates a new {@link CassandraParametersParameterAccessor}.
*
* @param method must not be {@literal null}.
* @param values must not be {@@iteral null}.
* @param values must not be {@literal null}.
*/
public CassandraParametersParameterAccessor(CassandraQueryMethod method, Object... values) {
super(method.getParameters(), values);
}
/**
* Returns the Cassandra {@link DataType} for the declared parameter if the type is a
* {@link org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder simple type}. Parameter types may be
* specified using {@link org.springframework.data.cassandra.mapping.CassandraType}.
*
* @param index parameter index
* @return the Cassandra {@link DataType} or {@literal null} if the parameter type cannot be determined from
* {@link org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder}
*/
public DataType getDataType(int index) {
return getParameters().getParameter(index).getCassandraType();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.repository.query.CassandraParameterAccessor#getParameterType(int)
*/
@Override
public Class<?> getParameterType(int index) {
return getParameters().getParameter(index).getType();
}
/* (non-Javadoc)
* @see org.springframework.data.repository.query.ParametersParameterAccessor#getParameters()
*/
@Override
public CassandraParameters getParameters() {
return (CassandraParameters) super.getParameters();
}
}

View File

@@ -1,18 +1,23 @@
/*
* Copyright 2016 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.repository.query;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
@@ -23,54 +28,43 @@ import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.ResultSet;
/**
* Cassandra specific implementation of {@link QueryMethod}.
*
* @author Matthew Adams
* @author Oliver Gierke
* @author Mark Paluch
*/
public class CassandraQueryMethod extends QueryMethod {
// TODO: double-check this list
public static final List<Class<?>> ALLOWED_PARAMETER_TYPES = Collections.unmodifiableList(Arrays
.asList(new Class<?>[] { String.class, CharSequence.class, char.class, Character.class, char[].class, long.class,
Long.class, boolean.class, Boolean.class, BigDecimal.class, BigInteger.class, double.class, Double.class,
float.class, Float.class, InetAddress.class, Date.class, UUID.class, int.class, Integer.class }));
private final Method method;
private final CassandraMappingContext mappingContext;
private Query query;
private String queryString;
private boolean queryCached = false;
public static final List<Class<?>> STRING_LIKE_PARAMETER_TYPES = Collections.unmodifiableList(Arrays
.asList(new Class<?>[] { CharSequence.class, char.class, Character.class, char[].class }));
public static final List<Class<?>> DATE_PARAMETER_TYPES = Collections.unmodifiableList(Arrays
.asList(new Class<?>[] { Date.class }));
public static boolean isMapOfCharSequenceToObject(TypeInformation<?> type) {
if (!type.isMap()) {
return false;
}
TypeInformation<?> keyType = type.getComponentType();
TypeInformation<?> valueType = type.getMapValueType();
return ClassUtils.isAssignable(CharSequence.class, keyType.getType()) && Object.class.equals(valueType.getType());
}
protected Method method;
protected CassandraMappingContext mappingContext;
protected Query query;
protected String queryString;
protected boolean queryCached = false;
protected Set<Integer> stringLikeParameterIndexes = new HashSet<Integer>();
protected Set<Integer> dateParameterIndexes = new HashSet<Integer>();
public CassandraQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory, CassandraMappingContext mappingContext) {
/**
* Creates a new {@link CassandraQueryMethod} from the given {@link Method}.
*
* @param method must not be {@literal null}.
* @param metadata must not be {@literal null}.
* @param projectionFactory must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
*/
public CassandraQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
CassandraMappingContext mappingContext) {
super(method, metadata, factory);
Assert.notNull(mappingContext, "MappingContext must not be null!");
verify(method, metadata);
this.method = method;
Assert.notNull(mappingContext, "MappingContext must not be null!");
this.mappingContext = mappingContext;
}
@@ -78,36 +72,13 @@ public class CassandraQueryMethod extends QueryMethod {
// TODO: support Page & Slice queries
if (isSliceQuery() || isPageQuery()) {
throw new InvalidDataAccessApiUsageException("neither slice nor page queries are supported yet");
}
Set<Class<?>> offendingTypes = new HashSet<Class<?>>();
int i = 0;
for (Class<?> type : method.getParameterTypes()) {
if (!ALLOWED_PARAMETER_TYPES.contains(type)) {
offendingTypes.add(type);
}
for (Class<?> quotedType : STRING_LIKE_PARAMETER_TYPES) {
if (quotedType.isAssignableFrom(type)) {
stringLikeParameterIndexes.add(i);
}
}
for (Class<?> quotedType : DATE_PARAMETER_TYPES) {
if (quotedType.isAssignableFrom(type)) {
dateParameterIndexes.add(i);
}
}
i++;
}
if (offendingTypes.size() > 0) {
throw new IllegalArgumentException(String.format(
"encountered unsupported query parameter type%s [%s] in method %s", offendingTypes.size() == 1 ? "" : "s",
StringUtils.arrayToCommaDelimitedString(new ArrayList<Class<?>>(offendingTypes).toArray()), method));
throw new InvalidDataAccessApiUsageException("Slice and Page queries are not supported.");
}
}
/* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryMethod#createParameters(java.lang.reflect.Method)
*/
@Override
protected CassandraParameters createParameters(Method method) {
return new CassandraParameters(method);
@@ -118,7 +89,7 @@ public class CassandraQueryMethod extends QueryMethod {
*/
Query getQueryAnnotation() {
if (query == null) {
query = method.getAnnotation(Query.class);
query = AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
queryCached = true;
}
return query;
@@ -145,42 +116,17 @@ public class CassandraQueryMethod extends QueryMethod {
return queryString;
}
/**
* @return the return type for this {@link QueryMethod}.
*/
public TypeInformation<?> getReturnType() {
return ClassTypeInformation.fromReturnTypeOf(method);
}
/**
* @return true is the method returns a {@link ResultSet}.
*/
public boolean isResultSetQuery() {
return ResultSet.class.isAssignableFrom(method.getReturnType());
}
public boolean isSingleEntityQuery() {
return ClassUtils.isAssignable(getDomainClass(), method.getReturnType());
}
public boolean isCollectionOfEntityQuery() {
return isQueryForEntity() && isCollectionQuery();
}
public boolean isMapOfCharSequenceToObjectQuery() {
return isMapOfCharSequenceToObject(getReturnType());
}
public boolean isListOfMapOfCharSequenceToObject() {
TypeInformation<?> type = getReturnType();
if (!ClassUtils.isAssignable(List.class, type.getType())) {
return false;
}
return isMapOfCharSequenceToObject(type.getComponentType());
}
public boolean isStringLikeParameter(int parameterIndex) {
return stringLikeParameterIndexes.contains(parameterIndex);
}
public boolean isDateParameter(int parameterIndex) {
return dateParameterIndexes.contains(parameterIndex);
return ResultSet.class.isAssignableFrom(getReturnType().getActualType().getType());
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2016 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.repository.query;
import java.util.Iterator;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.TypeCodec;
/**
* Custom {@link org.springframework.data.repository.query.ParameterAccessor} that uses a {@link CassandraConverter} to
* convert parameters.
*
* @author Mark Paluch
* @since 1.5
*/
class ConvertingParameterAccessor implements CassandraParameterAccessor {
private final CassandraConverter cassandraConverter;
private final CassandraParameterAccessor delegate;
public ConvertingParameterAccessor(CassandraConverter cassandraConverter, CassandraParameterAccessor delegate) {
this.cassandraConverter = cassandraConverter;
this.delegate = delegate;
}
/* (non-Javadoc)
* @see org.springframework.data.repository.query.ParameterAccessor#getPageable()
*/
@Override
public Pageable getPageable() {
return delegate.getPageable();
}
/* (non-Javadoc)
* @see org.springframework.data.repository.query.ParameterAccessor#getSort()
*/
@Override
public Sort getSort() {
return delegate.getSort();
}
/* (non-Javadoc)
* @see org.springframework.data.repository.query.ParameterAccessor#getDynamicProjection()
*/
@Override
public Class<?> getDynamicProjection() {
return delegate.getDynamicProjection();
}
/* (non-Javadoc)
* @see org.springframework.data.repository.query.ParameterAccessor#getBindableValue(int)
*/
@Override
public Object getBindableValue(int index) {
return potentiallyConvert(index, delegate.getBindableValue(index));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.repository.query.CassandraParameterAccessor#getDataType(int)
*/
@Override
public DataType getDataType(int index) {
return delegate.getDataType(index);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.repository.query.CassandraParameterAccessor#getParameterType(int)
*/
@Override
public Class<?> getParameterType(int index) {
return delegate.getParameterType(index);
}
/* (non-Javadoc)
* @see org.springframework.data.repository.query.ParameterAccessor#hasBindableNullValue()
*/
@Override
public boolean hasBindableNullValue() {
return delegate.hasBindableNullValue();
}
/*
* (non-Javadoc)
*
* @see java.lang.Iterable#iterator()
*/
/* (non-Javadoc)
* @see org.springframework.data.repository.query.ParameterAccessor#iterator()
*/
public Iterator<Object> iterator() {
return new ConvertingIterator(delegate.iterator());
}
private Object potentiallyConvert(int index, Object bindableValue) {
if (bindableValue == null) {
return null;
}
DataType parameterType = getDataType(index);
if (parameterType == null) {
parameterType = cassandraConverter.getMappingContext().getDataType(getParameterType(index));
}
TypeCodec<?> cassandraType = CodecRegistry.DEFAULT_INSTANCE.codecFor(parameterType);
if (cassandraType.getJavaType().getRawType().isAssignableFrom(bindableValue.getClass())) {
return bindableValue;
}
return cassandraConverter.getConversionService().convert(bindableValue, cassandraType.getJavaType().getRawType());
}
/**
* Custom {@link Iterator} to convert items before returning them.
*
* @author Mark Paluch
*/
private class ConvertingIterator implements Iterator<Object> {
private final Iterator<Object> delegate;
private int index = 0;
/**
* Creates a new {@link ConvertingIterator} for the given delegate.
*
* @param delegate
*/
public ConvertingIterator(Iterator<Object> delegate) {
this.delegate = delegate;
}
/*
* (non-Javadoc)
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return delegate.hasNext();
}
/*
* (non-Javadoc)
* @see java.util.Iterator#next()
*/
public Object next() {
return potentiallyConvert(index++, next());
}
/*
* (non-Javadoc)
* @see java.util.Iterator#remove()
*/
public void remove() {
delegate.remove();
}
}
}

View File

@@ -1,20 +1,47 @@
/*
* Copyright 2016 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.repository.query;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.core.cql.CqlStringUtils;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.util.ClassUtils;
import com.datastax.driver.core.LocalDate;
/**
* Query to use a plain String to create the {@link Query} to actually execute.
*
* @author Matthew Adams
* @author Mark Paluch
*/
public class StringBasedCassandraQuery extends AbstractCassandraQuery {
private static final Pattern PLACEHOLDER = Pattern.compile("\\?(\\d+)");
private static final Logger LOG = LoggerFactory.getLogger(StringBasedCassandraQuery.class);
@SuppressWarnings("unchecked") private static final Set<Class<?>> STRING_LIKE_PARAMETER_TYPES = new HashSet<Class<?>>(
Arrays.asList(CharSequence.class, char.class, Character.class, char[].class));
protected String query;
private static final Pattern PLACEHOLDER = Pattern.compile("\\?(\\d+)");
protected final String query;
public StringBasedCassandraQuery(String query, CassandraQueryMethod queryMethod, CassandraOperations operations) {
@@ -27,6 +54,9 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery {
this(queryMethod.getAnnotatedQuery(), queryMethod, operations);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.repository.query.AbstractCassandraQuery#createQuery(org.springframework.data.cassandra.repository.query.CassandraParameterAccessor)
*/
@Override
public String createQuery(CassandraParameterAccessor accessor) {
return replacePlaceholders(query, accessor);
@@ -41,13 +71,14 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery {
String group = matcher.group();
int index = Integer.parseInt(matcher.group(1));
Object value = getParameterWithIndex(accessor, index);
String stringValue = null;
CassandraQueryMethod queryMethod = getQueryMethod();
String stringValue;
if (queryMethod.isStringLikeParameter(index)) {
stringValue = "'" + CqlStringUtils.escapeSingle(value) + "'";
} else if (queryMethod.isDateParameter(index)) {
stringValue = "" + ((Date) value).getTime();
if (isStringLike(value)) {
stringValue = String.format("'%s'", CqlStringUtils.escapeSingle(value));
} else if (isTimestampParameter(value)) {
stringValue = String.format("%d", ((Date) value).getTime());
} else if (isDateParameter(value)) {
stringValue = String.format("'%s'", value);
} else {
stringValue = value.toString();
}
@@ -58,6 +89,30 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery {
return result;
}
private boolean isTimestampParameter(Object value) {
return value instanceof Date;
}
private boolean isDateParameter(Object value) {
return value instanceof LocalDate;
}
private boolean isStringLike(Object value) {
if (value == null) {
return false;
}
for (Class<?> type : STRING_LIKE_PARAMETER_TYPES) {
if (ClassUtils.isAssignableValue(type, value)) {
return true;
}
}
return false;
}
private Object getParameterWithIndex(CassandraParameterAccessor accessor, int index) {
return accessor.getBindableValue(index);
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright 2016 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;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.util.Assert;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Row;
/**
* Utility to mock a Cassandra {@link Row}.
*
* @author Mark Paluch
*/
public class RowMockUtil {
/**
* Creates a new {@link Row} mock using the given {@code columns}. Each column carries a name, value and data type so
* users of {@link Row} can use most of the methods.
*
* @param columns
* @return
*/
public static Row newRowMock(final Column... columns) {
Assert.notNull(columns, "Columns must not be null");
Row rowMock = mock(Row.class);
ColumnDefinitions columnDefinitionsMock = mock(ColumnDefinitions.class);
when(rowMock.getColumnDefinitions()).thenReturn(columnDefinitionsMock);
when(columnDefinitionsMock.contains(anyString())).thenAnswer(new Answer<Boolean>() {
@Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
for (Column column : columns) {
if (column.name.equalsIgnoreCase((String) invocation.getArguments()[0])) {
return true;
}
}
return false;
}
});
when(columnDefinitionsMock.getIndexOf(anyString())).thenAnswer(new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocation) throws Throwable {
int counter = 0;
for (Column column : columns) {
if (column.name.equalsIgnoreCase((String) invocation.getArguments()[0])) {
return counter;
}
counter++;
}
return -1;
}
});
when(columnDefinitionsMock.getType(anyString())).thenAnswer(new Answer<DataType>() {
@Override
public DataType answer(InvocationOnMock invocation) throws Throwable {
for (Column column : columns) {
if (column.name.equalsIgnoreCase((String) invocation.getArguments()[0])) {
return column.type;
}
}
return null;
}
});
when(columnDefinitionsMock.getType(anyInt())).thenAnswer(new Answer<DataType>() {
@Override
public DataType answer(InvocationOnMock invocation) throws Throwable {
return columns[(Integer) invocation.getArguments()[0]].type;
}
});
when(rowMock.getObject(anyInt())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return columns[(Integer) invocation.getArguments()[0]].value;
}
});
when(rowMock.getObject(anyString())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
for (Column column : columns) {
if (column.name.equalsIgnoreCase((String) invocation.getArguments()[0])) {
return column.value;
}
}
return null;
}
});
return rowMock;
}
/**
* Creates a new {@link Column} to be used with {@link RowMockUtil#newRowMock(Column...)}.
*
* @param name must not be empty or {@link null}.
* @param value can be {@literal null}.
* @param type must not be {@literal null}.
* @return
*/
public static Column column(String name, Object value, DataType type) {
Assert.hasText(name, "Name must not be empty");
Assert.notNull(type, "DataType must not be null");
return new Column(name, value, type);
}
public static class Column {
private final String name;
private final Object value;
private final DataType type;
Column(String name, Object value, DataType type) {
this.name = name;
this.value = value;
this.type = type;
}
}
}

View File

@@ -20,6 +20,7 @@ import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.cassandra.domain.Person;
/**
* Unit tests for {@link ConverterRegistration}.
@@ -72,8 +73,4 @@ public class ConverterRegistrationUnitTests {
assertThat(context.isWriting(), is(true));
assertThat(context.isReading(), is(true));
}
public static class Person {
}
}

View File

@@ -37,6 +37,9 @@ import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.WritingConverter;
import org.threeten.bp.LocalDateTime;
import com.datastax.driver.core.Row;
/**
* Unit tests for {@link CustomConversions}.
@@ -140,6 +143,16 @@ public class CustomConversionsUnitTests {
assertThat(conversions.isSimpleType(InetAddress.class), is(true));
}
/**
* @see DATACASS-280
*/
@Test
public void considersRowASimpleType() {
CustomConversions conversions = new CustomConversions();
assertThat(conversions.isSimpleType(Row.class), is(true));
}
/**
* @see DATACASS-280
*/
@@ -197,6 +210,39 @@ public class CustomConversionsUnitTests {
assertThat(customConversions.getCustomWriteTarget(String.class, SimpleDateFormat.class), notNullValue());
}
/**
* @see DATACASS-296
*/
@Test
public void registersConvertersForJsr310() {
CustomConversions customConversions = new CustomConversions();
assertThat(customConversions.hasCustomWriteTarget(java.time.LocalDateTime.class), is(true));
}
/**
* @see DATACASS-296
*/
@Test
public void registersConvertersForThreeTenBackPort() {
CustomConversions customConversions = new CustomConversions();
assertThat(customConversions.hasCustomWriteTarget(org.threeten.bp.LocalDateTime.class), is(true));
}
/**
* @see DATACASS-296
*/
@Test
public void registersConvertersForJoda() {
CustomConversions customConversions = new CustomConversions();
assertThat(customConversions.hasCustomWriteTarget(org.joda.time.LocalDate.class), is(true));
}
private static Class<?> createProxyTypeFor(Class<?> type) {
ProxyFactory factory = new ProxyFactory();

View File

@@ -22,15 +22,23 @@ import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assume.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.cassandra.RowMockUtil.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.junit.Before;
@@ -43,6 +51,7 @@ import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.core.SpringVersion;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.data.cassandra.RowMockUtil;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraType;
@@ -53,6 +62,7 @@ import org.springframework.data.cassandra.mapping.Table;
import org.springframework.test.util.ReflectionTestUtils;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.DataType.Name;
import com.datastax.driver.core.LocalDate;
import com.datastax.driver.core.Row;
@@ -71,6 +81,7 @@ import com.datastax.driver.core.querybuilder.Update.Assignments;
* @author Mark Paluch
* @soundtrack Outlandich - Dont Leave Me Feat Cyt (Sun Kidz Electrocore Mix)
*/
@SuppressWarnings("Since15")
@RunWith(MockitoJUnitRunner.class)
public class MappingCassandraConverterUnitTests {
@@ -88,6 +99,7 @@ public class MappingCassandraConverterUnitTests {
mappingCassandraConverter = new MappingCassandraConverter(mappingContext);
mappingCassandraConverter.afterPropertiesSet();
}
/**
@@ -373,7 +385,7 @@ public class MappingCassandraConverterUnitTests {
* @see DATACASS-280
*/
@Test
public void shouldReadInetAddressCorrectly() throws UnknownHostException {
public void shouldReadInetAddressCorrectly() throws UnknownHostException{
InetAddress localHost = InetAddress.getLocalHost();
when(rowMock.getInet(0)).thenReturn(localHost);
@@ -388,7 +400,7 @@ public class MappingCassandraConverterUnitTests {
* @see DATACASS-271
*/
@Test
public void shouldReadTimestampCorrectly() throws UnknownHostException {
public void shouldReadTimestampCorrectly() {
Date date = new Date(1);
when(rowMock.getTimestamp(0)).thenReturn(date);
@@ -402,7 +414,7 @@ public class MappingCassandraConverterUnitTests {
* @see DATACASS-271
*/
@Test
public void shouldReadDateCorrectly() throws UnknownHostException {
public void shouldReadDateCorrectly() {
LocalDate date = LocalDate.fromDaysSinceEpoch(1234);
when(rowMock.getDate(0)).thenReturn(date);
@@ -416,7 +428,7 @@ public class MappingCassandraConverterUnitTests {
* @see DATACASS-280
*/
@Test
public void shouldReadBooleanCorrectly() throws UnknownHostException {
public void shouldReadBooleanCorrectly() {
when(rowMock.getBool(0)).thenReturn(true);
@@ -425,6 +437,307 @@ public class MappingCassandraConverterUnitTests {
assertThat(result, is(equalTo(true)));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadLocalDateCorrectly() {
LocalDateTime now = LocalDateTime.now();
Instant instant = now.toInstant(ZoneOffset.UTC);
Row rowMock = RowMockUtil.newRowMock(column("id", "my-id", DataType.ascii()),
column("localdate", Date.from(instant), DataType.timestamp()));
TypeWithLocalDate result = mappingCassandraConverter.readRow(TypeWithLocalDate.class, rowMock);
assertThat(result.localDate, is(notNullValue()));
assertThat(result.localDate.getYear(), is(now.getYear()));
assertThat(result.localDate.getMonthValue(), is(now.getMonthValue()));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreateInsertWithLocalDateCorrectly() {
java.time.LocalDate now = java.time.LocalDate.now();
TypeWithLocalDate typeWithLocalDate = new TypeWithLocalDate();
typeWithLocalDate.localDate = now;
Insert insert = QueryBuilder.insertInto("table");
mappingCassandraConverter.write(typeWithLocalDate, insert);
assertThat(getValues(insert),
contains((Object) LocalDate.fromYearMonthDay(now.getYear(), now.getMonthValue(), now.getDayOfMonth())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreateUpdateWithLocalDateCorrectly() {
java.time.LocalDate now = java.time.LocalDate.now();
TypeWithLocalDate typeWithLocalDate = new TypeWithLocalDate();
typeWithLocalDate.localDate = now;
Update update = QueryBuilder.update("table");
mappingCassandraConverter.write(typeWithLocalDate, update);
assertThat(getAssignmentValues(update),
contains((Object) LocalDate.fromYearMonthDay(now.getYear(), now.getMonthValue(), now.getDayOfMonth())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreateInsertWithLocalDateListUsingCassandraDate() {
java.time.LocalDate now = java.time.LocalDate.now();
TypeWithLocalDate typeWithLocalDate = new TypeWithLocalDate();
java.time.LocalDate localDate = java.time.LocalDate.of(2010, 7, 4);
typeWithLocalDate.list = Arrays.asList(now, localDate);
Insert insert = QueryBuilder.insertInto("table");
mappingCassandraConverter.write(typeWithLocalDate, insert);
List<Object> values = getValues(insert);
assertThat(values.get(0), is(instanceOf(List.class)));
List<LocalDate> dates = (List) values.get(0);
assertThat(dates, hasItem(LocalDate.fromYearMonthDay(now.getYear(), now.getMonthValue(), now.getDayOfMonth())));
assertThat(dates, hasItem(LocalDate.fromYearMonthDay(2010, 7, 4)));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreateInsertWithLocalDateSetUsingCassandraDate() {
java.time.LocalDate now = java.time.LocalDate.now();
TypeWithLocalDate typeWithLocalDate = new TypeWithLocalDate();
java.time.LocalDate localDate = java.time.LocalDate.of(2010, 7, 4);
typeWithLocalDate.set = new HashSet<java.time.LocalDate>(Arrays.asList(now, localDate));
Insert insert = QueryBuilder.insertInto("table");
mappingCassandraConverter.write(typeWithLocalDate, insert);
List<Object> values = getValues(insert);
assertThat(values.get(0), is(instanceOf(Set.class)));
Set<LocalDate> dates = (Set) values.get(0);
assertThat(dates, hasItem(LocalDate.fromYearMonthDay(now.getYear(), now.getMonthValue(), now.getDayOfMonth())));
assertThat(dates, hasItem(LocalDate.fromYearMonthDay(2010, 7, 4)));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadLocalDateTimeUsingCassandraDateCorrectly() {
Row rowMock = RowMockUtil.newRowMock(column("id", "my-id", DataType.ascii()),
column("localDate", LocalDate.fromYearMonthDay(2010, 7, 4), DataType.date()));
TypeWithLocalDateMappedToDate result = mappingCassandraConverter.readRow(TypeWithLocalDateMappedToDate.class,
rowMock);
assertThat(result.localDate, is(notNullValue()));
assertThat(result.localDate.getYear(), is(2010));
assertThat(result.localDate.getMonthValue(), is(7));
assertThat(result.localDate.getDayOfMonth(), is(4));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreateInsertWithLocalDateUsingCassandraDateCorrectly() {
TypeWithLocalDateMappedToDate typeWithLocalDate = new TypeWithLocalDateMappedToDate();
typeWithLocalDate.localDate = java.time.LocalDate.of(2010, 7, 4);
Insert insert = QueryBuilder.insertInto("table");
mappingCassandraConverter.write(typeWithLocalDate, insert);
assertThat(getValues(insert), contains((Object) LocalDate.fromYearMonthDay(2010, 7, 4)));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreateUpdateWithLocalDateUsingCassandraDateCorrectly() {
TypeWithLocalDateMappedToDate typeWithLocalDate = new TypeWithLocalDateMappedToDate();
typeWithLocalDate.localDate = java.time.LocalDate.of(2010, 7, 4);
Update update = QueryBuilder.update("table");
mappingCassandraConverter.write(typeWithLocalDate, update);
assertThat(getAssignmentValues(update), contains((Object) LocalDate.fromYearMonthDay(2010, 7, 4)));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadLocalDateTimeCorrectly() {
LocalDateTime now = LocalDateTime.now();
Instant instant = now.toInstant(ZoneOffset.UTC);
Row rowMock = RowMockUtil.newRowMock(column("id", "my-id", DataType.ascii()),
column("localDateTime", Date.from(instant), DataType.timestamp()));
TypeWithLocalDate result = mappingCassandraConverter.readRow(TypeWithLocalDate.class, rowMock);
assertThat(result.localDateTime, is(notNullValue()));
assertThat(result.localDateTime.getYear(), is(now.getYear()));
assertThat(result.localDateTime.getMinute(), is(now.getMinute()));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadInstantCorrectly() {
LocalDateTime now = LocalDateTime.now();
Instant instant = now.toInstant(ZoneOffset.UTC);
Row rowMock = RowMockUtil.newRowMock(column("id", "my-id", DataType.ascii()),
column("instant", Date.from(instant), DataType.timestamp()));
TypeWithInstant result = mappingCassandraConverter.readRow(TypeWithInstant.class, rowMock);
assertThat(result.instant, is(notNullValue()));
assertThat(result.instant.getEpochSecond(), is(instant.getEpochSecond()));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadZoneIdCorrectly() {
Row rowMock = RowMockUtil.newRowMock(column("id", "my-id", DataType.ascii()),
column("zoneId", "Europe/Paris", DataType.varchar()));
TypeWithZoneId result = mappingCassandraConverter.readRow(TypeWithZoneId.class, rowMock);
assertThat(result.zoneId, is(notNullValue()));
assertThat(result.zoneId.getId(), is(equalTo("Europe/Paris")));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadJodaLocalDateTimeUsingCassandraDateCorrectly() {
Row rowMock = RowMockUtil.newRowMock(column("id", "my-id", DataType.ascii()),
column("localDate", LocalDate.fromYearMonthDay(2010, 7, 4), DataType.date()));
TypeWithJodaLocalDateMappedToDate result = mappingCassandraConverter
.readRow(TypeWithJodaLocalDateMappedToDate.class, rowMock);
assertThat(result.localDate, is(notNullValue()));
assertThat(result.localDate.getYear(), is(2010));
assertThat(result.localDate.getMonthOfYear(), is(7));
assertThat(result.localDate.getDayOfMonth(), is(4));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreateInsertWithJodaLocalDateUsingCassandraDateCorrectly() {
TypeWithJodaLocalDateMappedToDate typeWithLocalDate = new TypeWithJodaLocalDateMappedToDate();
typeWithLocalDate.localDate = new org.joda.time.LocalDate(2010, 7, 4);
Insert insert = QueryBuilder.insertInto("table");
mappingCassandraConverter.write(typeWithLocalDate, insert);
assertThat(getValues(insert), contains((Object) LocalDate.fromYearMonthDay(2010, 7, 4)));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreateUpdateWithJodaLocalDateUsingCassandraDateCorrectly() {
TypeWithJodaLocalDateMappedToDate typeWithLocalDate = new TypeWithJodaLocalDateMappedToDate();
typeWithLocalDate.localDate = new org.joda.time.LocalDate(2010, 7, 4);
Update update = QueryBuilder.update("table");
mappingCassandraConverter.write(typeWithLocalDate, update);
assertThat(getAssignmentValues(update), contains((Object) LocalDate.fromYearMonthDay(2010, 7, 4)));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadThreeTenBpLocalDateTimeUsingCassandraDateCorrectly() {
Row rowMock = RowMockUtil.newRowMock(column("id", "my-id", DataType.ascii()),
column("localDate", LocalDate.fromYearMonthDay(2010, 7, 4), DataType.date()));
TypeWithThreeTenBpLocalDateMappedToDate result = mappingCassandraConverter
.readRow(TypeWithThreeTenBpLocalDateMappedToDate.class, rowMock);
assertThat(result.localDate, is(notNullValue()));
assertThat(result.localDate.getYear(), is(2010));
assertThat(result.localDate.getMonthValue(), is(7));
assertThat(result.localDate.getDayOfMonth(), is(4));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreateInsertWithThreeTenBpLocalDateUsingCassandraDateCorrectly() {
TypeWithThreeTenBpLocalDateMappedToDate typeWithLocalDate = new TypeWithThreeTenBpLocalDateMappedToDate();
typeWithLocalDate.localDate = org.threeten.bp.LocalDate.of(2010, 7, 4);
Insert insert = QueryBuilder.insertInto("table");
mappingCassandraConverter.write(typeWithLocalDate, insert);
assertThat(getValues(insert), contains((Object) LocalDate.fromYearMonthDay(2010, 7, 4)));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreateUpdateWithThreeTenBpLocalDateUsingCassandraDateCorrectly() {
TypeWithThreeTenBpLocalDateMappedToDate typeWithLocalDate = new TypeWithThreeTenBpLocalDateMappedToDate();
typeWithLocalDate.localDate = org.threeten.bp.LocalDate.of(2010, 7, 4);
Update update = QueryBuilder.update("table");
mappingCassandraConverter.write(typeWithLocalDate, update);
assertThat(getAssignmentValues(update), contains((Object) LocalDate.fromYearMonthDay(2010, 7, 4)));
}
@SuppressWarnings("unchecked")
private List<Object> getValues(Insert statement) {
return (List<Object>) ReflectionTestUtils.getField(statement, "values");
@@ -565,4 +878,65 @@ public class MappingCassandraConverterUnitTests {
public static enum Condition {
MINT, USED;
}
@Table
public static class TypeWithLocalDate {
@PrimaryKey private String id;
java.time.LocalDate localDate;
java.time.LocalDateTime localDateTime;
List<java.time.LocalDate> list;
Set<java.time.LocalDate> set;
}
/**
* Uses Cassandra's {@link Name#DATE} which maps by default to {@link LocalDate}
*/
@Table
public static class TypeWithLocalDateMappedToDate {
@PrimaryKey private String id;
@CassandraType(type = Name.DATE) java.time.LocalDate localDate;
}
/**
* Uses Cassandra's {@link Name#DATE} which maps by default to Joda {@link LocalDate}
*/
@Table
public static class TypeWithJodaLocalDateMappedToDate {
@PrimaryKey private String id;
@CassandraType(type = Name.DATE) org.joda.time.LocalDate localDate;
}
/**
* Uses Cassandra's {@link Name#DATE} which maps by default to Joda {@link LocalDate}
*/
@Table
public static class TypeWithThreeTenBpLocalDateMappedToDate {
@PrimaryKey private String id;
@CassandraType(type = Name.DATE) org.threeten.bp.LocalDate localDate;
}
@Table
public static class TypeWithInstant {
@PrimaryKey private String id;
Instant instant;
}
@Table
public static class TypeWithZoneId {
@PrimaryKey private String id;
ZoneId zoneId;
}
}

View File

@@ -29,8 +29,10 @@ import org.springframework.data.cassandra.mapping.CassandraType;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.data.cassandra.test.integration.mapping.types.CassandraTypeMappingIntegrationTest.Condition;
import org.threeten.bp.LocalDateTime;
import com.datastax.driver.core.DataType.Name;
import com.datastax.driver.core.LocalDate;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -88,4 +90,23 @@ public class AllPossibleTypes {
private Map<String, String> mapOfString;
private Condition anEnum;
// supported by conversion
java.time.LocalDate localDate;
java.time.LocalDateTime localDateTime;
java.time.LocalTime localTime;
java.time.Instant instant;
java.time.ZoneId zoneId;
org.joda.time.LocalDate jodaLocalDate;
org.joda.time.LocalDateTime jodaLocalDateTime;
org.joda.time.DateTime jodaDateTime;
org.joda.time.DateMidnight jodaDateMidnight;
org.threeten.bp.LocalDate bpLocalDate;
org.threeten.bp.LocalDateTime bpLocalDateTime;
org.threeten.bp.LocalTime bpLocalTime;
org.threeten.bp.Instant bpInstant;
org.threeten.bp.ZoneId bpZoneId;
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2016 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.domain;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.mapping.Table;
import lombok.Data;
/**
* @author Mark Paluch
*/
@Table
@Data
public class Person {
@Id String id;
String firstname;
}

View File

@@ -0,0 +1,385 @@
/*
* Copyright 2016 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.mapping;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.keyspace.ColumnSpecification;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.convert.CustomConversions;
import org.springframework.data.cassandra.domain.AllPossibleTypes;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.DataType.CollectionType;
import com.datastax.driver.core.DataType.Name;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Unit tests for {@link BasicCassandraMappingContext} targeted on {@link CreateTableSpecification}.
*
* @author Mark Paluch
* @soundtrack Black Rose - Volbeat
*/
public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
BasicCassandraMappingContext ctx = new BasicCassandraMappingContext();
@Before
public void setUp() throws Exception {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(new PersonReadConverter());
converters.add(new PersonWriteConverter());
CustomConversions customConversions = new CustomConversions(converters);
ctx.setCustomConversions(customConversions);
}
/**
* @see DATACASS-296
*/
@Test
public void customConversionTestShouldCreateCorrectTableDefinition() {
CassandraPersistentEntity<?> persistentEntity = ctx.getPersistentEntity(Employee.class);
CreateTableSpecification specification = ctx.getCreateTableSpecificationFor(persistentEntity);
assertThat(getColumn("human", specification).getType(), is(DataType.varchar()));
ColumnSpecification friends = getColumn("friends", specification);
assertThat(friends.getType().isCollection(), is(true));
CollectionType friendsCollection = (CollectionType) friends.getType();
assertThat(friendsCollection.getName(), is(Name.LIST));
assertThat(friendsCollection.getTypeArguments().size(), is(1));
assertThat(friendsCollection.getTypeArguments().get(0), is(DataType.varchar()));
ColumnSpecification people = getColumn("people", specification);
assertThat(people.getType().isCollection(), is(true));
CollectionType peopleCollection = (CollectionType) people.getType();
assertThat(peopleCollection.getName(), is(Name.SET));
assertThat(peopleCollection.getTypeArguments().size(), is(1));
assertThat(peopleCollection.getTypeArguments().get(0), is(DataType.varchar()));
}
/**
* @see DATACASS-296
*/
@Test
public void customConversionTestShouldHonorTypeAnnotationAndCreateCorrectTableDefinition() {
CassandraPersistentEntity<?> persistentEntity = ctx.getPersistentEntity(Employee.class);
CreateTableSpecification specification = ctx.getCreateTableSpecificationFor(persistentEntity);
assertThat(getColumn("floater", specification).getType(), is(DataType.cfloat()));
ColumnSpecification enemies = getColumn("enemies", specification);
assertThat(enemies.getType().isCollection(), is(true));
CollectionType enemiesCollection = (CollectionType) enemies.getType();
assertThat(enemiesCollection.getName(), is(Name.SET));
assertThat(enemiesCollection.getTypeArguments().size(), is(1));
assertThat(enemiesCollection.getTypeArguments().get(0), is(DataType.bigint()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToVarchar() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("id", specification).getType(), is(DataType.varchar()));
assertThat(getColumn("zoneId", specification).getType(), is(DataType.varchar()));
assertThat(getColumn("bpZoneId", specification).getType(), is(DataType.varchar()));
assertThat(getColumn("anEnum", specification).getType(), is(DataType.varchar()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToTinyInt() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("boxedByte", specification).getType(), is(DataType.tinyint()));
assertThat(getColumn("primitiveByte", specification).getType(), is(DataType.tinyint()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToSmallInt() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("boxedShort", specification).getType(), is(DataType.smallint()));
assertThat(getColumn("primitiveShort", specification).getType(), is(DataType.smallint()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToBigInt() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("boxedLong", specification).getType(), is(DataType.bigint()));
assertThat(getColumn("primitiveLong", specification).getType(), is(DataType.bigint()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToVarInt() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("bigInteger", specification).getType(), is(DataType.varint()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToDecimal() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("bigDecimal", specification).getType(), is(DataType.decimal()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToInt() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("boxedInteger", specification).getType(), is(DataType.cint()));
assertThat(getColumn("primitiveInteger", specification).getType(), is(DataType.cint()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToFloat() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("boxedFloat", specification).getType(), is(DataType.cfloat()));
assertThat(getColumn("primitiveFloat", specification).getType(), is(DataType.cfloat()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToDouble() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("boxedDouble", specification).getType(), is(DataType.cdouble()));
assertThat(getColumn("primitiveDouble", specification).getType(), is(DataType.cdouble()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToBoolean() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("boxedBoolean", specification).getType(), is(DataType.cboolean()));
assertThat(getColumn("primitiveBoolean", specification).getType(), is(DataType.cboolean()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToDate() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("date", specification).getType(), is(DataType.date()));
assertThat(getColumn("localDate", specification).getType(), is(DataType.date()));
assertThat(getColumn("jodaLocalDate", specification).getType(), is(DataType.date()));
assertThat(getColumn("jodaDateMidnight", specification).getType(), is(DataType.date()));
assertThat(getColumn("bpLocalDate", specification).getType(), is(DataType.date()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToTimestamp() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("timestamp", specification).getType(), is(DataType.timestamp()));
assertThat(getColumn("localDateTime", specification).getType(), is(DataType.timestamp()));
assertThat(getColumn("instant", specification).getType(), is(DataType.timestamp()));
assertThat(getColumn("jodaLocalDateTime", specification).getType(), is(DataType.timestamp()));
assertThat(getColumn("jodaDateTime", specification).getType(), is(DataType.timestamp()));
assertThat(getColumn("bpLocalDateTime", specification).getType(), is(DataType.timestamp()));
assertThat(getColumn("bpInstant", specification).getType(), is(DataType.timestamp()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToTimestampUsingOverrides() {
CreateTableSpecification specification = getCreateTableSpecificationFor(TypeWithOverrides.class);
assertThat(getColumn("localDate", specification).getType(), is(DataType.timestamp()));
assertThat(getColumn("jodaLocalDate", specification).getType(), is(DataType.timestamp()));
}
/**
* @see DATACASS-296
*/
@Test
public void columnsShouldMapToBlob() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumn("blob", specification).getType(), is(DataType.blob()));
}
public CreateTableSpecification getCreateTableSpecificationFor(Class<?> persistentEntityClass) {
CustomConversions customConversions = new CustomConversions(Collections.EMPTY_LIST);
ctx.setCustomConversions(customConversions);
CassandraPersistentEntity<?> persistentEntity = ctx.getPersistentEntity(persistentEntityClass);
return ctx.getCreateTableSpecificationFor(persistentEntity);
}
private ColumnSpecification getColumn(String columnName, CreateTableSpecification specification) {
for (ColumnSpecification columnSpecification : specification.getColumns()) {
if (columnSpecification.getName().equals(CqlIdentifier.cqlId(columnName))) {
return columnSpecification;
}
}
throw new IllegalArgumentException(
String.format("Cannot find column '%s' amongst '%s'", columnName, specification.getColumns()));
}
/**
* @author Mark Paluch
*/
@Data
@Table
public static class Employee {
@Id String id;
Human human;
List<Human> friends;
Set<Human> people;
@CassandraType(type = Name.FLOAT) Human floater;
@CassandraType(type = Name.SET, typeArguments = Name.BIGINT) List<Human> enemies;
}
/**
* @author Mark Paluch
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Human {
String firstname;
String lastname;
}
/**
* @author Mark Paluch
*/
@Data
@Table
static class TypeWithOverrides {
@Id String id;
@CassandraType(type = Name.TIMESTAMP) java.time.LocalDate localDate;
@CassandraType(type = Name.TIMESTAMP) org.joda.time.LocalDate jodaLocalDate;
}
static class PersonReadConverter implements Converter<String, Human> {
public Human convert(String source) {
if (StringUtils.hasText(source)) {
try {
return new ObjectMapper().readValue(source, Human.class);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
return null;
}
}
static class PersonWriteConverter implements Converter<Human, String> {
public String convert(Human source) {
try {
return new ObjectMapper().writeValueAsString(source);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
}

View File

@@ -19,12 +19,17 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.cassandra.convert.CustomConversions;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.util.ClassTypeInformation;
/**
* Unit tests for {@link BasicCassandraMappingContext}.
@@ -34,107 +39,15 @@ import org.springframework.data.mapping.model.MappingException;
*/
public class MappingContextUnitTests {
public static class Transient {}
@Table
public static class X {
@PrimaryKey String key;
}
@Table
public static class Y {
@PrimaryKey String key;
}
@Table
public static class PrimaryKeyOnProperty {
String key;
@PrimaryKey(value = "foo")
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
@Table
public static class PrimaryKeyColumnsOnProperty {
String firstname;
String lastname;
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED)
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@PrimaryKeyColumn(name = "mylastname", ordinal = 2, type = PrimaryKeyType.CLUSTERED)
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
@Table
public static class PrimaryKeyOnPropertyWithPrimaryKeyClass {
CompositePrimaryKeyClassWithProperties key;
@PrimaryKey
public CompositePrimaryKeyClassWithProperties getKey() {
return key;
}
public void setKey(CompositePrimaryKeyClassWithProperties key) {
this.key = key;
}
}
@PrimaryKeyClass
public static class CompositePrimaryKeyClassWithProperties implements Serializable{
String firstname;
String lastname;
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED)
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@PrimaryKeyColumn(name = "mylastname", ordinal = 2, type = PrimaryKeyType.CLUSTERED)
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
BasicCassandraMappingContext ctx = new BasicCassandraMappingContext();
@Test(expected = MappingException.class)
public void testGetPersistentEntityOfTransientType() {
CassandraPersistentEntity<?> entity = ctx.getPersistentEntity(Transient.class);
ctx.getPersistentEntity(Transient.class);
}
private static class Transient {}
@Test
public void testGetExistingPersistentEntityHappyPath() {
@@ -145,6 +58,16 @@ public class MappingContextUnitTests {
assertFalse(ctx.contains(Y.class));
}
@Table
private static class X {
@PrimaryKey String key;
}
@Table
private static class Y {
@PrimaryKey String key;
}
/**
* @see DATACASS-248
*/
@@ -161,6 +84,21 @@ public class MappingContextUnitTests {
assertThat(columnNames.get(0).toCql(), is(equalTo("foo")));
}
@Table
private static class PrimaryKeyOnProperty {
String key;
@PrimaryKey(value = "foo")
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
/**
* @see DATACASS-248
*/
@@ -182,32 +120,133 @@ public class MappingContextUnitTests {
assertThat(lastname.isClusterKeyColumn(), is(true));
assertThat(lastname.getColumnName().toCql(), is(equalTo("mylastname")));
}
@Table
private static class PrimaryKeyColumnsOnProperty {
String firstname;
String lastname;
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED)
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@PrimaryKeyColumn(name = "mylastname", ordinal = 2, type = PrimaryKeyType.CLUSTERED)
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
/**
* @see DATACASS-248
*/
@Test
public void primaryKeyClassWithprimaryKeyColumnsOnPropertyShouldWork() {
CassandraPersistentEntity<?> persistentEntity = ctx.getPersistentEntity(PrimaryKeyOnPropertyWithPrimaryKeyClass.class);
CassandraPersistentEntity<?> primaryKeyClass = ctx.getPersistentEntity(CompositePrimaryKeyClassWithProperties.class);
CassandraPersistentEntity<?> persistentEntity = ctx
.getPersistentEntity(PrimaryKeyOnPropertyWithPrimaryKeyClass.class);
CassandraPersistentEntity<?> primaryKeyClass = ctx
.getPersistentEntity(CompositePrimaryKeyClassWithProperties.class);
assertThat(persistentEntity.isCompositePrimaryKey(), is(false));
assertThat(persistentEntity.getPersistentProperty("key").isCompositePrimaryKey(), is(true));
assertThat(primaryKeyClass.isCompositePrimaryKey(), is(true));
assertThat(primaryKeyClass.getCompositePrimaryKeyProperties(), hasSize(2));
CassandraPersistentProperty firstname = primaryKeyClass.getPersistentProperty("firstname");
assertThat(firstname.isPrimaryKeyColumn(), is(true));
assertThat(firstname.isPartitionKeyColumn(), is(true));
assertThat(firstname.isClusterKeyColumn(), is(false));
assertThat(firstname.getColumnName().toCql(), is(equalTo("firstname")));
CassandraPersistentProperty lastname = primaryKeyClass.getPersistentProperty("lastname");
assertThat(lastname.isPrimaryKeyColumn(), is(true));
assertThat(lastname.isPartitionKeyColumn(), is(false));
assertThat(lastname.isClusterKeyColumn(), is(true));
assertThat(lastname.getColumnName().toCql(), is(equalTo("mylastname")));
}
@Table
private static class PrimaryKeyOnPropertyWithPrimaryKeyClass {
CompositePrimaryKeyClassWithProperties key;
@PrimaryKey
public CompositePrimaryKeyClassWithProperties getKey() {
return key;
}
public void setKey(CompositePrimaryKeyClassWithProperties key) {
this.key = key;
}
}
@PrimaryKeyClass
private static class CompositePrimaryKeyClassWithProperties implements Serializable {
String firstname;
String lastname;
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED)
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@PrimaryKeyColumn(name = "mylastname", ordinal = 2, type = PrimaryKeyType.CLUSTERED)
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
/**
* @see DATACASS-296
*/
@Test
public void shouldCreatePersistentEntityIfNoConversionRegistered() {
ctx.setCustomConversions(new CustomConversions(Collections.EMPTY_LIST));
assertThat(ctx.shouldCreatePersistentEntityFor(ClassTypeInformation.from(Human.class)), is(true));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldNotCreateEntitiesForCustomConvertedTypes() {
List<?> converters = Arrays.asList(new HumanToStringConverter());
ctx.setCustomConversions(new CustomConversions(converters));
assertThat(ctx.shouldCreatePersistentEntityFor(ClassTypeInformation.from(Human.class)), is(false));
}
private static class Human {
}
private static class HumanToStringConverter implements Converter<Human, String> {
@Override
public String convert(Human source) {
return "hello";
}
}
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright 2016 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.repository.isolated;
import static java.time.Instant.*;
import static java.time.ZoneId.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;
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.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.convert.CustomConversions;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.domain.AllPossibleTypes;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraType;
import org.springframework.data.cassandra.repository.Query;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest;
import org.springframework.data.cassandra.test.integration.support.IntegrationTestConfig;
import org.springframework.data.repository.CrudRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.datastax.driver.core.DataType.Name;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.InvalidQueryException;
/**
* Integration tests for various query method parameter types.
*
* @author Mark Paluch
* @see DATACASS-296
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SuppressWarnings("Since15")
public class RepositoryQueryMethodParameterTypesIntegrationTests
extends AbstractSpringDataEmbeddedCassandraIntegrationTest {
@Configuration
@EnableCassandraRepositories(basePackageClasses = RepositoryQueryMethodParameterTypesIntegrationTests.class,
considerNestedRepositories = true)
public static class Config extends IntegrationTestConfig {
@Override
public String[] getEntityBasePackages() {
return new String[] { AllPossibleTypes.class.getPackage().getName() };
}
@Override
public SchemaAction getSchemaAction() {
return SchemaAction.RECREATE_DROP_UNUSED;
}
}
@Autowired AllPossibleTypesRepository allPossibleTypesRepository;
@Autowired Session session;
@Autowired BasicCassandraMappingContext mappingContext;
@Autowired MappingCassandraConverter converter;
@Before
public void setUp() throws Exception {
allPossibleTypesRepository.deleteAll();
}
/**
* @see DATACASS-296
*/
@Test
public void shouldFindByLocalDate() {
session.execute("CREATE INDEX IF NOT EXISTS allpossibletypes_localdate ON allpossibletypes ( localdate )");
AllPossibleTypes allPossibleTypes = new AllPossibleTypes();
allPossibleTypes.setId("id");
allPossibleTypes.setLocalDate(LocalDate.now());
allPossibleTypesRepository.save(allPossibleTypes);
List<AllPossibleTypes> result = allPossibleTypesRepository.findWithCreatedDate(allPossibleTypes.getLocalDate());
assertThat(result, hasSize(1));
assertThat(result, contains(allPossibleTypes));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldFindByAnnotatedDateParameter() {
CustomConversions customConversions = new CustomConversions(Arrays.asList(new DateToLocalDateConverter()));
mappingContext.setCustomConversions(customConversions);
converter.setCustomConversions(customConversions);
converter.afterPropertiesSet();
session.execute("CREATE INDEX IF NOT EXISTS allpossibletypes_date ON allpossibletypes ( date )");
AllPossibleTypes allPossibleTypes = new AllPossibleTypes();
LocalDate localDate = LocalDate.now();
Instant instant = localDate.atStartOfDay().toInstant(ZoneOffset.UTC);
allPossibleTypes.setId("id");
allPossibleTypes.setDate(com.datastax.driver.core.LocalDate.fromYearMonthDay(localDate.getYear(),
localDate.getMonthValue(), localDate.getDayOfMonth()));
allPossibleTypesRepository.save(allPossibleTypes);
List<AllPossibleTypes> result = allPossibleTypesRepository.findWithAnnotatedDateParameter(Date.from(instant));
assertThat(result, hasSize(1));
assertThat(result, contains(allPossibleTypes));
}
/**
* @see DATACASS-296
*/
@Test(expected = InvalidQueryException.class)
public void shouldThrowExceptionUsingWrongMethodParameter() {
// NOTE: InvalidQueryException is a driver exception. This should get fixed with DATACASS-304
session.execute("CREATE INDEX IF NOT EXISTS allpossibletypes_date ON allpossibletypes ( date )");
allPossibleTypesRepository.findWithDateParameter(Date.from(Instant.ofEpochSecond(44234123421L)));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldFindByZoneId() {
ZoneId zoneId = ZoneId.of("Europe/Paris");
session.execute("CREATE INDEX IF NOT EXISTS allpossibletypes_zoneid ON allpossibletypes ( zoneid )");
AllPossibleTypes allPossibleTypes = new AllPossibleTypes();
allPossibleTypes.setId("id");
allPossibleTypes.setZoneId(zoneId);
allPossibleTypesRepository.save(allPossibleTypes);
List<AllPossibleTypes> result = allPossibleTypesRepository.findWithZoneId(zoneId);
assertThat(result, hasSize(1));
assertThat(result, contains(allPossibleTypes));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldFindByOptionalOfZoneId() {
ZoneId zoneId = ZoneId.of("Europe/Paris");
session.execute("CREATE INDEX IF NOT EXISTS allpossibletypes_zoneid ON allpossibletypes ( zoneid )");
AllPossibleTypes allPossibleTypes = new AllPossibleTypes();
allPossibleTypes.setId("id");
allPossibleTypes.setZoneId(zoneId);
allPossibleTypesRepository.save(allPossibleTypes);
List<AllPossibleTypes> result = allPossibleTypesRepository.findWithZoneId(Optional.of(zoneId));
assertThat(result, hasSize(1));
assertThat(result, contains(allPossibleTypes));
}
private interface AllPossibleTypesRepository extends CrudRepository<AllPossibleTypes, String> {
@Query("select * from allpossibletypes where localdate = ?0")
List<AllPossibleTypes> findWithCreatedDate(java.time.LocalDate createdDate);
@Query("select * from allpossibletypes where zoneid = ?0")
List<AllPossibleTypes> findWithZoneId(ZoneId zoneId);
@Query("select * from allpossibletypes where date = ?0")
List<AllPossibleTypes> findWithAnnotatedDateParameter(@CassandraType(type = Name.DATE) Date timestamp);
@Query("select * from allpossibletypes where date = ?0")
List<AllPossibleTypes> findWithDateParameter(Date timestamp);
@Query("select * from allpossibletypes where zoneid = ?0")
List<AllPossibleTypes> findWithZoneId(Optional<ZoneId> zoneId);
}
private static class DateToLocalDateConverter implements Converter<Date, com.datastax.driver.core.LocalDate> {
@Override
public com.datastax.driver.core.LocalDate convert(Date source) {
LocalDate localDate = LocalDateTime.ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalDate();
return com.datastax.driver.core.LocalDate.fromYearMonthDay(localDate.getYear(), localDate.getMonthValue(),
localDate.getDayOfMonth());
}
}
}

View File

@@ -315,7 +315,7 @@ public class RepositoryReturnTypesIntegrationTests extends AbstractSpringDataEmb
allPossibleTypesRepository.save(entity);
Map<String, Object> result = allPossibleTypesRepository.findEntityAsMapById(entity.getId());
assertThat(result.size(), is(27));
assertThat(result.size(), is(41));
assertThat(result.get("primitiveinteger"), is(equalTo((Object) Integer.valueOf(123))));
assertThat(result.get("biginteger"), is(equalTo((Object) BigInteger.ONE)));
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright 2016 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.repository.query;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.cassandra.domain.AllPossibleTypes;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraType;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.threeten.bp.LocalDateTime;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.DataType.Name;
/**
* Unit tests for {@link CassandraParametersParameterAccessor}.
*
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class CassandraParametersParameterAccessorUnitTests {
@Mock ProjectionFactory projectionFactory;
RepositoryMetadata metadata = new DefaultRepositoryMetadata(PossibleRepository.class);
CassandraMappingContext context = new BasicCassandraMappingContext();
/**
* @see DATACASS-296
*/
@Test
public void returnsCassandraSimpleType() throws Exception {
Method method = PossibleRepository.class.getMethod("findByFirstname", String.class);
CassandraParameterAccessor accessor = new CassandraParametersParameterAccessor(getCassandraQueryMethod(method),
new Object[] { "firstname" });
assertThat(accessor.getDataType(0), is(equalTo(DataType.varchar())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReturnNoTypeForComplexTypes() throws Exception {
Method method = PossibleRepository.class.getMethod("findByBpLocalDateTime", LocalDateTime.class);
CassandraParameterAccessor accessor = new CassandraParametersParameterAccessor(getCassandraQueryMethod(method),
new Object[] { LocalDateTime.of(2000, 10, 11, 12, 13, 14) });
assertThat(accessor.getDataType(0), is(nullValue()));
}
/**
* @see DATACASS-296
*/
@Test
public void returnTypeForAnnotatedParameter() throws Exception {
Method method = PossibleRepository.class.getMethod("findByAnnotatedBpLocalDateTime", LocalDateTime.class);
CassandraParameterAccessor accessor = new CassandraParametersParameterAccessor(getCassandraQueryMethod(method),
new Object[] { LocalDateTime.of(2000, 10, 11, 12, 13, 14) });
assertThat(accessor.getDataType(0), is(DataType.date()));
}
/**
* @see DATACASS-296
*/
@Test
public void returnTypeForAnnotatedParameterWhenUsingStringValue() throws Exception {
Method method = PossibleRepository.class.getMethod("findByAnnotatedObject", Object.class);
CassandraParameterAccessor accessor = new CassandraParametersParameterAccessor(getCassandraQueryMethod(method),
new Object[] { "" });
assertThat(accessor.getDataType(0), is(DataType.date()));
}
/**
* @see DATACASS-296
*/
@Test
public void returnTypeForAnnotatedParameterWhenUsingNullValue() throws Exception {
Method method = PossibleRepository.class.getMethod("findByAnnotatedObject", Object.class);
CassandraParameterAccessor accessor = new CassandraParametersParameterAccessor(getCassandraQueryMethod(method),
new Object[] { "" });
assertThat(accessor.getDataType(0), is(DataType.date()));
}
private CassandraQueryMethod getCassandraQueryMethod(Method method) {
return new CassandraQueryMethod(method, metadata, projectionFactory, context);
}
interface PossibleRepository extends Repository<AllPossibleTypes, Long> {
List<AllPossibleTypes> findByFirstname(String firstname);
List<AllPossibleTypes> findByBpLocalDateTime(LocalDateTime dateTime);
List<AllPossibleTypes> findByAnnotatedBpLocalDateTime(@CassandraType(type = Name.DATE) LocalDateTime dateTime);
List<AllPossibleTypes> findByAnnotatedObject(@CassandraType(type = Name.DATE) Object dateTime);
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2016 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.repository.query;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.cassandra.domain.Person;
import org.springframework.data.cassandra.mapping.CassandraType;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.DataType.Name;
/**
* Unit tests for {@link CassandraParameters}.
*
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class CassandraParametersUnitTests {
@Mock CassandraQueryMethod queryMethod;
/**
* @see DATACASS-296
*/
@Test
public void shouldReturnDataTypeForSimpleType() throws Exception {
Method method = PersonRepository.class.getMethod("findByFirstname", String.class);
CassandraParameters cassandraParameters = new CassandraParameters(method);
assertThat(cassandraParameters.getParameter(0).getCassandraType(), is(DataType.varchar()));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReturnDataTypeForAnnotatedSimpleType() throws Exception {
Method method = PersonRepository.class.getMethod("findByFirstTime", String.class);
CassandraParameters cassandraParameters = new CassandraParameters(method);
assertThat(cassandraParameters.getParameter(0).getCassandraType(), is(DataType.time()));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReturnNoTypeForComplexType() throws Exception {
Method method = PersonRepository.class.getMethod("findByObject", Object.class);
CassandraParameters cassandraParameters = new CassandraParameters(method);
assertThat(cassandraParameters.getParameter(0).getCassandraType(), is(nullValue()));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReturnTypeForAnnotatedType() throws Exception {
Method method = PersonRepository.class.getMethod("findByAnnotatedObject", Object.class);
CassandraParameters cassandraParameters = new CassandraParameters(method);
assertThat(cassandraParameters.getParameter(0).getCassandraType(), is(DataType.time()));
}
interface PersonRepository {
Person findByFirstname(String firstname);
Person findByFirstTime(@CassandraType(type = Name.TIME) String firstname);
Person findByObject(Object firstname);
Person findByAnnotatedObject(@CassandraType(type = Name.TIME) Object firstname);
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2016 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.repository.query;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.time.LocalDate;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import com.datastax.driver.core.DataType;
/**
* Unit tests for {@link ConvertingParameterAccessor}.
*
* @author Mark Paluch
*/
@SuppressWarnings("Since15")
@RunWith(MockitoJUnitRunner.class)
public class ConvertingParameterAccessorUnitTests {
@Mock CassandraParameterAccessor delegateMock;
MappingCassandraConverter converter;
@Before
public void setUp() {
this.converter = new MappingCassandraConverter(new BasicCassandraMappingContext());
this.converter.afterPropertiesSet();
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReturnNullBindableValue() {
ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, delegateMock);
assertThat(accessor.getBindableValue(0), is(nullValue()));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReturnNativeBindableValue() {
ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, delegateMock);
when(delegateMock.getBindableValue(0)).thenReturn("hello");
when(delegateMock.getDataType(0)).thenReturn(DataType.varchar());
assertThat(accessor.getBindableValue(0), is(equalTo((Object) "hello")));
}
/**
* @see DATACASS-296
*/
@Test
@SuppressWarnings("rawtypes")
public void shouldReturnConvertedBindableValue() {
ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, delegateMock);
LocalDate localDate = LocalDate.of(2010, 7, 4);
when(delegateMock.getBindableValue(0)).thenReturn(localDate);
when(delegateMock.getParameterType(0)).thenReturn((Class) LocalDate.class);
assertThat(accessor.getBindableValue(0),
is(equalTo((Object) com.datastax.driver.core.LocalDate.fromYearMonthDay(2010, 7, 4))));
}
}

View File

@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.test.integration.repository.querymethods.declared;
package org.springframework.data.cassandra.repository.query;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
import java.time.LocalDate;
import org.junit.Before;
import org.junit.Test;
@@ -31,9 +32,7 @@ import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.repository.Query;
import org.springframework.data.cassandra.repository.query.CassandraParametersParameterAccessor;
import org.springframework.data.cassandra.repository.query.CassandraQueryMethod;
import org.springframework.data.cassandra.repository.query.StringBasedCassandraQuery;
import org.springframework.data.cassandra.test.integration.repository.querymethods.declared.Person;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.Repository;
@@ -49,14 +48,15 @@ import com.datastax.driver.core.querybuilder.Select;
*
* @author Matthew T. Adams
* @author Oliver Gierke
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class StringBasedCassandraQueryIntegrationTests {
public class StringBasedCassandraQueryIntegrationUnitTests {
@Mock CassandraOperations operations;
RepositoryMetadata metadata;
CassandraConverter converter;
MappingCassandraConverter converter;
ProjectionFactory factory;
@Before
@@ -67,6 +67,8 @@ public class StringBasedCassandraQueryIntegrationTests {
this.metadata = AbstractRepositoryMetadata.getMetadata(SampleRepository.class);
this.converter = new MappingCassandraConverter(new BasicCassandraMappingContext());
this.factory = new SpelAwareProxyProjectionFactory();
this.converter.afterPropertiesSet();
}
@Test
@@ -96,10 +98,10 @@ public class StringBasedCassandraQueryIntegrationTests {
CassandraQueryMethod queryMethod = new CassandraQueryMethod(method, metadata, factory,
converter.getMappingContext());
StringBasedCassandraQuery cassandraQuery = new StringBasedCassandraQuery(queryMethod, operations);
CassandraParametersParameterAccessor accesor = new CassandraParametersParameterAccessor(queryMethod, "Matthews",
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(queryMethod, "Matthews",
"John");
String stringQuery = cassandraQuery.createQuery(accesor);
String stringQuery = cassandraQuery.createQuery(accessor);
SimpleStatement actual = new SimpleStatement(stringQuery);
String table = Person.class.getSimpleName().toLowerCase();
@@ -110,6 +112,30 @@ public class StringBasedCassandraQueryIntegrationTests {
assertThat(actual.getQueryString(), is(expected.getQueryString()));
}
/**
* @see DATACASS-296
*/
@Test
public void bindsConvertedPropertyCorrectly() throws Exception {
Method method = SampleRepository.class.getMethod("findByCreatedDate", LocalDate.class);
CassandraQueryMethod queryMethod = new CassandraQueryMethod(method, metadata, factory,
converter.getMappingContext());
StringBasedCassandraQuery cassandraQuery = new StringBasedCassandraQuery(queryMethod, operations);
CassandraParameterAccessor accessor = new ConvertingParameterAccessor(converter, new CassandraParametersParameterAccessor(queryMethod,
LocalDate.of(2010, 7, 4)));
String stringQuery = cassandraQuery.createQuery(accessor);
SimpleStatement actual = new SimpleStatement(stringQuery);
String table = Person.class.getSimpleName().toLowerCase();
Select expected = QueryBuilder.select().all().from(table);
expected.setForceNoValues(true);
expected.where(QueryBuilder.eq("createdDate", com.datastax.driver.core.LocalDate.fromYearMonthDay(2010, 7, 4)));
assertThat(actual.getQueryString(), is(expected.getQueryString()));
}
private interface SampleRepository extends Repository<Person, String> {
@Query("SELECT * FROM person WHERE lastname=?0;")
@@ -117,5 +143,8 @@ public class StringBasedCassandraQueryIntegrationTests {
@Query("SELECT * FROM person WHERE lastname=?0 AND firstname=?1;")
Person findByLastnameAndFirstname(String lastname, String firstname);
@Query("SELECT * FROM person WHERE createdDate=?0;")
Person findByCreatedDate(LocalDate createdDate);
}
}

View File

@@ -46,38 +46,25 @@ import com.datastax.driver.core.Session;
import com.datastax.driver.core.TableMetadata;
/**
* The SchemaActionIntegrationTests class is a test suite of test cases testing the contract and behavior
* of various {@link SchemaAction}s on startup of a Spring configured, Cassandra application client.
* The SchemaActionIntegrationTests class is a test suite of test cases testing the contract and behavior of various
* {@link SchemaAction}s on startup of a Spring configured, Cassandra application client.
*
* @author John Blum
* @see org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest
* @see org.springframework.cassandra.test.integration.KeyspaceRule
* @see org.springframework.data.cassandra.config.CassandraSessionFactoryBean
* @see org.springframework.data.cassandra.config.SchemaAction
* @see <a href="https://jira.spring.io/browse/DATACASS-219>DATACASS-219</a>
* @since 1.5.0
*/
public class SchemaActionIntegrationTests extends AbstractEmbeddedCassandraIntegrationTest {
protected static final String KEYSPACE_NAME = SchemaActionIntegrationTests.class.getSimpleName().toLowerCase();
protected static final String PERSON_TABLE_DEFINITION_CQL =
String.format("CREATE TABLE %s.person (id int, firstName text, lastName text, PRIMARY KEY(id));",
KEYSPACE_NAME);
protected static final String PERSON_TABLE_DEFINITION_CQL = String
.format("CREATE TABLE %s.person (id int, firstName text, lastName text, PRIMARY KEY(id));", KEYSPACE_NAME);
@Rule
public ExpectedException exception = ExpectedException.none();
@Rule public ExpectedException exception = ExpectedException.none();
@Rule
public KeyspaceRule KEYSPACE_RULE = new KeyspaceRule(cassandraEnvironment, KEYSPACE_NAME);
static <T> T[] asArray(T... array) {
return array;
}
@Rule public KeyspaceRule KEYSPACE_RULE = new KeyspaceRule(cassandraEnvironment, KEYSPACE_NAME);
protected ConfigurableApplicationContext newApplicationContext(Class<?>... annotatedClasses) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(annotatedClasses);
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(annotatedClasses);
applicationContext.registerShutdownHook();
@@ -91,13 +78,9 @@ public class SchemaActionIntegrationTests extends AbstractEmbeddedCassandraInteg
applicationContext = newApplicationContext(annotatedClass);
return sessionCallback.doInSession(applicationContext.getBean(Session.class));
} finally {
close(applicationContext);
}
}
protected void close(ConfigurableApplicationContext applicationContext) {
if (applicationContext != null) {
applicationContext.close();
if (applicationContext != null) {
applicationContext.close();
}
}
}
@@ -114,22 +97,21 @@ public class SchemaActionIntegrationTests extends AbstractEmbeddedCassandraInteg
assertThat(tableMetadata.getColumns().size(), is(equalTo(columns.length)));
for (String columnName : columns) {
assertThat(String.format("Column [%s] dos not exist!", columnName),
tableMetadata.getColumn(columnName), is(notNullValue()));
assertThat(String.format("Column [%s] dos not exist!", columnName), tableMetadata.getColumn(columnName),
is(notNullValue()));
}
}
@Test
public void createWithNoExistingTableCreatesTableFromEntity() {
doInSessionWithConfiguration(CreateWithNoExistingTableConfiguration.class,
new SessionCallback<Void>() {
@Override public Void doInSession(Session session) throws DataAccessException {
assertHasTableWithColumns(session, "person", "firstName", "lastName", "nickname",
"birthDate", "numberOfChildren", "cool");
return null;
}
doInSessionWithConfiguration(CreateWithNoExistingTableConfiguration.class, new SessionCallback<Void>() {
@Override
public Void doInSession(Session session) throws DataAccessException {
assertHasTableWithColumns(session, "person", "firstName", "lastName", "nickname", "birthDate",
"numberOfChildren", "cool", "createdDate", "zoneId");
return null;
}
);
});
}
@Test
@@ -137,53 +119,48 @@ public class SchemaActionIntegrationTests extends AbstractEmbeddedCassandraInteg
exception.expect(BeanCreationException.class);
exception.expectMessage(containsString(String.format("Table %s.person already exists", KEYSPACE_NAME)));
doInSessionWithConfiguration(CreateWithExistingTableConfiguration.class,
new SessionCallback<Object>() {
@Override public Object doInSession(Session s) throws DataAccessException {
fail(String.format("%s should have failed!",
CreateWithExistingTableConfiguration.class.getSimpleName()));
return null;
}
doInSessionWithConfiguration(CreateWithExistingTableConfiguration.class, new SessionCallback<Object>() {
@Override
public Object doInSession(Session s) throws DataAccessException {
fail(String.format("%s should have failed!", CreateWithExistingTableConfiguration.class.getSimpleName()));
return null;
}
);
});
}
@Test
public void createIfNotExistsWithNoExistingTableCreatesTableFromEntity() {
doInSessionWithConfiguration(CreateIfNotExistsWithNoExistingTableConfiguration.class,
new SessionCallback<Void>() {
@Override public Void doInSession(Session session) throws DataAccessException {
assertHasTableWithColumns(session, "person", "firstName", "lastName", "nickname",
"birthDate", "numberOfChildren", "cool");
return null;
}
doInSessionWithConfiguration(CreateIfNotExistsWithNoExistingTableConfiguration.class, new SessionCallback<Void>() {
@Override
public Void doInSession(Session session) throws DataAccessException {
assertHasTableWithColumns(session, "person", "firstName", "lastName", "nickname", "birthDate",
"numberOfChildren", "cool", "createdDate", "zoneId");
return null;
}
);
});
}
@Test
public void createIfNotExistsWithExistingTableUsesExistingTable() {
doInSessionWithConfiguration(CreateIfNotExistsWithExistingTableConfiguration.class,
new SessionCallback<Void>() {
@Override public Void doInSession(Session session) throws DataAccessException {
assertHasTableWithColumns(session, "person", "id", "firstName", "lastName");
return null;
}
doInSessionWithConfiguration(CreateIfNotExistsWithExistingTableConfiguration.class, new SessionCallback<Void>() {
@Override
public Void doInSession(Session session) throws DataAccessException {
assertHasTableWithColumns(session, "person", "id", "firstName", "lastName");
return null;
}
);
});
}
@Test
public void recreateTableFromEntityDropsExistingTable() {
doInSessionWithConfiguration(RecreateSchemaActionWithExistingTableConfiguration.class,
new SessionCallback<Void>() {
@Override public Void doInSession(Session session) throws DataAccessException {
assertHasTableWithColumns(session, "person", "firstName", "lastName", "nickname",
"birthDate", "numberOfChildren", "cool");
return null;
}
doInSessionWithConfiguration(RecreateSchemaActionWithExistingTableConfiguration.class, new SessionCallback<Void>() {
@Override
public Void doInSession(Session session) throws DataAccessException {
assertHasTableWithColumns(session, "person", "firstName", "lastName", "nickname", "birthDate",
"numberOfChildren", "cool", "createdDate", "zoneId");
return null;
}
);
});
}
@Configuration
@@ -253,11 +230,13 @@ public class SchemaActionIntegrationTests extends AbstractEmbeddedCassandraInteg
@Override
public CassandraCqlClusterFactoryBean cluster() {
return new CassandraCqlClusterFactoryBean() {
@Override public void afterPropertiesSet() throws Exception {
@Override
public void afterPropertiesSet() throws Exception {
// avoid Cassandra Cluster creation; use embedded
}
@Override public Cluster getObject() {
@Override
public Cluster getObject() {
return cassandraEnvironment.getCluster();
}
};
@@ -265,7 +244,7 @@ public class SchemaActionIntegrationTests extends AbstractEmbeddedCassandraInteg
@Override
public String[] getEntityBasePackages() {
return asArray(Person.class.getPackage().getName());
return new String[] { Person.class.getPackage().getName() };
}
@Override

View File

@@ -0,0 +1,290 @@
/*
* Copyright 2016 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.test.integration.mapping.customconversion;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.codehaus.jackson.map.ObjectMapper;
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.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.convert.CustomConversions;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
import org.springframework.data.cassandra.test.integration.repository.querymethods.datekey.DateThingRepo;
import org.springframework.data.cassandra.test.integration.support.IntegrationTestConfig;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Test suite for applying {@link CustomConversions} to
* {@link org.springframework.data.cassandra.mapping.BasicCassandraMappingContext}.
*
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CustomConversionTests {
@Configuration
@EnableCassandraRepositories(basePackageClasses = DateThingRepo.class)
public static class Config extends IntegrationTestConfig {
@Override
public String[] getEntityBasePackages() {
return new String[] { Employee.class.getPackage().getName() };
}
@Override
public SchemaAction getSchemaAction() {
return SchemaAction.RECREATE_DROP_UNUSED;
}
@Override
public CustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(new PersonReadConverter());
converters.add(new PersonWriteConverter());
return new CustomConversions(converters);
}
}
@Autowired CassandraOperations cassandraOperations;
@Before
public void setUp() {
cassandraOperations.deleteAll(Employee.class);
}
/**
* @see DATACASS-296
*/
@Test
public void shouldInsertCustomConvertedObject() {
Employee employee = new Employee();
employee.setId("employee-id");
employee.setPerson(new Person("Homer", "Simpson"));
cassandraOperations.insert(employee);
Row row = cassandraOperations.selectOne(QueryBuilder.select("id", "person").from("employee"), Row.class);
assertThat(row.getString("id"), is(equalTo("employee-id")));
assertThat(row.getString("person"), containsString("\"firstname\":\"Homer\""));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldUpdateCustomConvertedObject() {
Employee employee = new Employee();
employee.setId("employee-id");
cassandraOperations.insert(employee);
employee.setPerson(new Person("Homer", "Simpson"));
cassandraOperations.update(employee);
Row row = cassandraOperations.selectOne(QueryBuilder.select("id", "person").from("employee"), Row.class);
assertThat(row.getString("id"), is(equalTo("employee-id")));
assertThat(row.getString("person"), containsString("\"firstname\":\"Homer\""));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldInsertCustomConvertedObjectWithCollections() {
Employee employee = new Employee();
employee.setId("employee-id");
cassandraOperations.insert(employee);
employee.setFriends(Arrays.asList(new Person("Carl", "Carlson"), new Person("Lenny", "Leonard")));
employee.setPeople(Collections.singleton(new Person("Apu", "Nahasapeemapetilon")));
cassandraOperations.update(employee);
Row row = cassandraOperations.selectOne(QueryBuilder.select("id", "person", "friends", "people").from("employee"),
Row.class);
assertThat(row.getObject("friends"), is(instanceOf(List.class)));
assertThat(row.getList("friends", String.class), hasSize(2));
assertThat(row.getObject("people"), is(instanceOf(Set.class)));
assertThat(row.getSet("people", String.class), hasSize(1));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldUpdateCustomConvertedObjectWithCollections() {
Employee employee = new Employee();
employee.setId("employee-id");
employee.setFriends(Arrays.asList(new Person("Carl", "Carlson"), new Person("Lenny", "Leonard")));
employee.setPeople(Collections.singleton(new Person("Apu", "Nahasapeemapetilon")));
cassandraOperations.insert(employee);
Row row = cassandraOperations.selectOne(QueryBuilder.select("id", "person", "friends", "people").from("employee"),
Row.class);
assertThat(row.getObject("friends"), is(instanceOf(List.class)));
assertThat(row.getList("friends", String.class), hasSize(2));
assertThat(row.getObject("people"), is(instanceOf(Set.class)));
assertThat(row.getSet("people", String.class), hasSize(1));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldLoadCustomConvertedObject() {
cassandraOperations.execute(QueryBuilder.insertInto("employee").value("id", "employee-id").value("person",
"{\"firstname\":\"Homer\",\"lastname\":\"Simpson\"}"));
Employee employee = cassandraOperations.selectOne(QueryBuilder.select("id", "person").from("employee"),
Employee.class);
assertThat(employee.getId(), is(equalTo("employee-id")));
assertThat(employee.getPerson(), is(notNullValue()));
assertThat(employee.getPerson().getFirstname(), is(equalTo("Homer")));
assertThat(employee.getPerson().getLastname(), is(equalTo("Simpson")));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldLoadCustomConvertedWithCollectionsObject() {
cassandraOperations.execute(QueryBuilder.insertInto("employee").value("id", "employee-id").value("people",
Collections.singleton("{\"firstname\":\"Apu\",\"lastname\":\"Nahasapeemapetilon\"}")));
Employee employee = cassandraOperations.selectOne(QueryBuilder.select("id", "people").from("employee"),
Employee.class);
assertThat(employee.getId(), is(equalTo("employee-id")));
assertThat(employee.getPeople(), is(notNullValue()));
Person apu = employee.getPeople().iterator().next();
assertThat(apu.getFirstname(), is(equalTo("Apu")));
}
/**
* @see DATACASS-296
*/
@Test
public void dummy() {
cassandraOperations.execute(QueryBuilder.insertInto("employee").value("id", "employee-id"));
cassandraOperations
.execute(QueryBuilder.update("employee").where(QueryBuilder.eq("id", "employee-id")).with(QueryBuilder
.set("people", Collections.singleton("{\"firstname\":\"Apu\",\"lastname\":\"Nahasapeemapetilon\"}"))));
}
/**
* @author Mark Paluch
*/
@Data
@Table
static class Employee {
@Id String id;
Person person;
List<Person> friends;
Set<Person> people;
}
/**
* @author Mark Paluch
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Person {
String firstname;
String lastname;
}
/**
* @author Mark Paluch
*/
static class PersonReadConverter implements Converter<String, Person> {
public Person convert(String source) {
if (StringUtils.hasText(source)) {
try {
return new ObjectMapper().readValue(source, Person.class);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
return null;
}
}
/**
* @author Mark Paluch
*/
static class PersonWriteConverter implements Converter<Person, String> {
public String convert(Person source) {
try {
return new ObjectMapper().writeValueAsString(source);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
}

View File

@@ -56,6 +56,7 @@ import com.datastax.driver.core.querybuilder.QueryBuilder;
* @author Mark Paluch
* @soundtrack DJ THT meets Scarlet - Live 2 Dance (Extended Mix) (Zgin Remix)
*/
@SuppressWarnings("Since15")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CassandraTypeMappingIntegrationTest extends AbstractSpringDataEmbeddedCassandraIntegrationTest {
@@ -521,6 +522,201 @@ public class CassandraTypeMappingIntegrationTest extends AbstractSpringDataEmbed
assertThat(loaded.getTime(), is(equalTo(time)));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteLocalDate() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setLocalDate(java.time.LocalDate.of(2010, 7, 4));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getLocalDate(), is(equalTo(entity.getLocalDate())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteLocalDateTime() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setLocalDateTime(java.time.LocalDateTime.of(2010, 7, 4, 1, 2, 3));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getLocalDateTime(), is(equalTo(entity.getLocalDateTime())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteLocalTime() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setLocalTime(java.time.LocalTime.of(1, 2, 3));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getLocalTime(), is(equalTo(entity.getLocalTime())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteInstant() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setInstant(java.time.Instant.now());
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getInstant(), is(equalTo(entity.getInstant())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteZoneId() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setZoneId(java.time.ZoneId.of("Europe/Paris"));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getZoneId(), is(equalTo(entity.getZoneId())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteJodaLocalDate() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setJodaLocalDate(new org.joda.time.LocalDate(2010, 7, 4));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getJodaLocalDate(), is(equalTo(entity.getJodaLocalDate())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteJodaDateMidnight() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setJodaDateMidnight(new org.joda.time.DateMidnight(2010, 7, 4));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getJodaDateMidnight(), is(equalTo(entity.getJodaDateMidnight())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteJodaDateTime() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setJodaDateTime(new org.joda.time.DateTime(2010, 7, 4, 1, 2, 3));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getJodaDateTime(), is(equalTo(entity.getJodaDateTime())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteBpLocalDate() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setBpLocalDate(org.threeten.bp.LocalDate.of(2010, 7, 4));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getBpLocalDate(), is(equalTo(entity.getBpLocalDate())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteBpLocalDateTime() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setBpLocalDateTime(org.threeten.bp.LocalDateTime.of(2010, 7, 4, 1, 2, 3));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getBpLocalDateTime(), is(equalTo(entity.getBpLocalDateTime())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteBpLocalTime() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setBpLocalTime(org.threeten.bp.LocalTime.of(1, 2, 3));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getBpLocalTime(), is(equalTo(entity.getBpLocalTime())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteBpInstant() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setBpInstant(org.threeten.bp.Instant.now());
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getBpZoneId(), is(equalTo(entity.getBpZoneId())));
}
/**
* @see DATACASS-296
*/
@Test
public void shouldReadAndWriteBpZoneId() throws Exception {
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setBpZoneId(org.threeten.bp.ZoneId.of("Europe/Paris"));
cassandraOperations.insert(entity);
AllPossibleTypes loaded = cassandraOperations.selectOneById(AllPossibleTypes.class, entity.getId());
assertThat(loaded.getBpZoneId(), is(equalTo(entity.getBpZoneId())));
}
/**
* @see DATACASS-271
*/

View File

@@ -15,14 +15,17 @@
*/
package org.springframework.data.cassandra.test.integration.repository.querymethods.declared;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import lombok.Data;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.cassandra.mapping.Indexed;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;
import lombok.Data;
/**
* Sample domain class.
*/
@@ -38,4 +41,7 @@ public class Person {
private Date birthDate;
private int numberOfChildren;
private boolean cool;
private LocalDate createdDate;
private ZoneId zoneId;
}

View File

@@ -18,6 +18,8 @@ package org.springframework.data.cassandra.test.integration.repository.querymeth
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@@ -36,9 +38,14 @@ import org.springframework.data.cassandra.test.integration.support.AbstractSprin
import org.springframework.data.cassandra.test.integration.support.IntegrationTestConfig;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.datastax.driver.core.Session;
/**
* Integration tests for use with {@link PersonRepository}.
*
* @author Matthew T. Adams
* @author Mark Paluch
* @soundtrack Mary Jane Kelly - Volbeat
*/
@RunWith(SpringJUnit4ClassRunner.class)
public abstract class QueryIntegrationTests extends AbstractSpringDataEmbeddedCassandraIntegrationTest {
@@ -57,6 +64,7 @@ public abstract class QueryIntegrationTests extends AbstractSpringDataEmbeddedCa
}
@Autowired PersonRepository personRepository;
@Autowired Session session;
@Before
public void before() {
@@ -286,8 +294,8 @@ public abstract class QueryIntegrationTests extends AbstractSpringDataEmbeddedCa
personToSave = personRepository.save(personToSave);
Optional<Person> savedPerson = personRepository.findOptionalWithLastnameAndFirstname(
personToSave.getLastname(), personToSave.getFirstname());
Optional<Person> savedPerson = personRepository.findOptionalWithLastnameAndFirstname(personToSave.getLastname(),
personToSave.getFirstname());
assertThat(savedPerson, is(notNullValue(Optional.class)));
assertThat(savedPerson.isPresent(), is(true));

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.cassandra.test.integration.repository.querymethods.declared.anno;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -78,4 +80,5 @@ public interface PersonRepositoryWithQueryAnnotations extends PersonRepository {
@Override
@Query("select * from person")
Stream<Person> findAllPeople();
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.cassandra.test.integration.repository.querymethods.declared.base;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -55,5 +57,4 @@ public interface PersonRepository extends CassandraRepository<Person> {
Optional<Person> findOptionalWithLastnameAndFirstname(String last, String first);
Stream<Person> findAllPeople();
}

View File

@@ -9,4 +9,7 @@ Person.findSingleCool=select cool from person where lastname = ?0 and firstname
Person.findSingleNumberOfChildren=select numberofchildren from person where lastname = ?0 and firstname = ?1
Person.findOptionalWithLastnameAndFirstname=select * from person where lastname = ?0 and firstname = ?1
Person.findAllPeople=select * from person
Person.findPeopleWithCreatedDate=select * from person where createddate = ?0
Person.findPeopleWithZoneId=select * from person where zoneid = ?0

View File

@@ -7,7 +7,14 @@ Excluded-Imports:
Import-Package:
sun.reflect;version="0";resolution:=optional
Import-Template:
com.datastax.driver.core.*;resolution:="optional";version="[0.1.0, 1.0.0)",
com.google.common.*;resolution:="optional";version="[11.0.0, 20.0.0)",
javax.enterprise.*;version="${cdi:[=.=.=,+1.0.0)}";resolution:=optional,
javax.xml.transform.*;resolution:="optional";version="0",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.codehaus.jackson.*;resolution:="optional";version="[1.6, 2.0.0)",
org.joda.time.*;version="${jodatime:[=.=.=,+1.0.0)}";resolution:=optional,
org.springframework.beans.*;version="[3.1.0, 4.0.0)",
org.springframework.cache.*;version="[3.1.0, 4.0.0)",
org.springframework.context.*;version="[3.1.0, 4.0.0)",
@@ -20,15 +27,6 @@ Import-Template:
org.springframework.data.*;version="[1.5.0, 2.0.0)",
org.springframework.expression.*;version="[3.1.0, 4.0.0)",
org.springframework.cassandra.*;version="[1.0.0,2.0.0)",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.w3c.dom.*;version="0",
javax.xml.transform.*;resolution:="optional";version="0",
com.datastax.driver.core.*;resolution:="optional";version="[0.1.0, 1.0.0)",
org.apache.cassandra.db.marshal.*;version="[1.2.0, 1.3.0)",
org.threeten.bp.*;version="${threetenbp:[=.=.=,+1.0.0)}";resolution:=optional,
org.slf4j.*;version="[1.5.0, 1.8.0)",
org.idevlab.rjc.*;resolution:="optional";version="[0.6.4, 0.6.4]",
org.apache.commons.pool.impl.*;resolution:="optional";version="[1.0.0, 3.0.0)",
org.codehaus.jackson.*;resolution:="optional";version="[1.6, 2.0.0)",
org.apache.commons.beanutils.*;resolution:="optional";version=1.8.5,
com.google.common.*;resolution:="optional";version="[11.0.0, 20.0.0)"
org.w3c.dom.*;version="0"