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:
21
pom.xml
21
pom.xml
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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))));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user