DATACASS-400 - Use custom conversion and UDT mapping with entity instantiator.
We now apply custom conversion and UDT mapping to the value provider that is used with entity instantiators. This allows entities to be instantiated with mapped UDTs and using custom conversion with properties that are provided using a constructor. Previously, columns were not converted which caused a parameter mismatch so entities couldn't be instantiated.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors
|
||||
* Copyright 2013-2017 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,23 +17,13 @@ package org.springframework.data.cassandra.convert;
|
||||
|
||||
import static org.springframework.data.cassandra.repository.support.BasicMapId.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.datastax.driver.core.CodecRegistry;
|
||||
import com.datastax.driver.core.DataType;
|
||||
import com.datastax.driver.core.Row;
|
||||
import com.datastax.driver.core.TypeCodec;
|
||||
import com.datastax.driver.core.UDTValue;
|
||||
import com.datastax.driver.core.UserType;
|
||||
import com.datastax.driver.core.querybuilder.Clause;
|
||||
import com.datastax.driver.core.querybuilder.Delete;
|
||||
import com.datastax.driver.core.querybuilder.Insert;
|
||||
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||
import com.datastax.driver.core.querybuilder.Select;
|
||||
import com.datastax.driver.core.querybuilder.Update;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -66,6 +56,19 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.datastax.driver.core.CodecRegistry;
|
||||
import com.datastax.driver.core.DataType;
|
||||
import com.datastax.driver.core.Row;
|
||||
import com.datastax.driver.core.TypeCodec;
|
||||
import com.datastax.driver.core.UDTValue;
|
||||
import com.datastax.driver.core.UserType;
|
||||
import com.datastax.driver.core.querybuilder.Clause;
|
||||
import com.datastax.driver.core.querybuilder.Delete;
|
||||
import com.datastax.driver.core.querybuilder.Insert;
|
||||
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||
import com.datastax.driver.core.querybuilder.Select;
|
||||
import com.datastax.driver.core.querybuilder.Update;
|
||||
|
||||
/**
|
||||
* {@link CassandraConverter} that uses a {@link MappingContext} to do sophisticated mapping of domain objects to
|
||||
* {@link Row}.
|
||||
@@ -136,8 +139,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
|
||||
return getConversionService().convert(row, type);
|
||||
}
|
||||
|
||||
CassandraPersistentEntity<R> persistentEntity = (CassandraPersistentEntity<R>)
|
||||
getMappingContext().getPersistentEntity(typeInfo);
|
||||
CassandraPersistentEntity<R> persistentEntity = (CassandraPersistentEntity<R>) getMappingContext()
|
||||
.getPersistentEntity(typeInfo);
|
||||
|
||||
if (persistentEntity == null) {
|
||||
throw new MappingException(String.format("No mapping metadata found for %s", rawType.getName()));
|
||||
@@ -160,8 +163,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
|
||||
DefaultSpELExpressionEvaluator expressionEvaluator = new DefaultSpELExpressionEvaluator(row, spELContext);
|
||||
BasicCassandraRowValueProvider rowValueProvider = new BasicCassandraRowValueProvider(row, expressionEvaluator);
|
||||
|
||||
CassandraPersistentEntityParameterValueProvider parameterProvider =
|
||||
new CassandraPersistentEntityParameterValueProvider(entity, rowValueProvider, null);
|
||||
CassandraPersistentEntityParameterValueProvider parameterProvider = new CassandraPersistentEntityParameterValueProvider(
|
||||
entity, new MappingAndConvertingValueProvider(rowValueProvider), null);
|
||||
|
||||
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
|
||||
S instance = instantiator.createInstance(entity, parameterProvider);
|
||||
@@ -175,11 +178,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
|
||||
|
||||
DefaultSpELExpressionEvaluator expressionEvaluator = new DefaultSpELExpressionEvaluator(udtValue, spELContext);
|
||||
|
||||
CassandraUDTValueProvider valueProvider = new CassandraUDTValueProvider(
|
||||
udtValue, CodecRegistry.DEFAULT_INSTANCE, expressionEvaluator);
|
||||
CassandraUDTValueProvider valueProvider = new CassandraUDTValueProvider(udtValue, CodecRegistry.DEFAULT_INSTANCE,
|
||||
expressionEvaluator);
|
||||
|
||||
CassandraPersistentEntityParameterValueProvider parameterProvider =
|
||||
new CassandraPersistentEntityParameterValueProvider(entity, valueProvider, null);
|
||||
CassandraPersistentEntityParameterValueProvider parameterProvider = new CassandraPersistentEntityParameterValueProvider(
|
||||
entity, new MappingAndConvertingValueProvider(valueProvider), null);
|
||||
|
||||
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
|
||||
S instance = instantiator.createInstance(entity, parameterProvider);
|
||||
@@ -833,4 +836,40 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
|
||||
private TypeCodec<Object> getCodec(CassandraPersistentProperty property) {
|
||||
return CodecRegistry.DEFAULT_INSTANCE.codecFor(mappingContext.getDataType(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CassandraRowValueProvider} that delegates reads to {@link CassandraValueProvider} applying mapping and
|
||||
* custom conversion from {@link MappingCassandraConverter}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.5.1
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
class MappingAndConvertingValueProvider implements CassandraValueProvider {
|
||||
|
||||
private final CassandraValueProvider parent;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.convert.CassandraValueProvider#hasProperty(org.springframework.data.cassandra.mapping.CassandraPersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public boolean hasProperty(CassandraPersistentProperty property) {
|
||||
return parent.hasProperty(property);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public Object getPropertyValue(CassandraPersistentProperty property) {
|
||||
|
||||
Object readValue = getReadValue(parent, property);
|
||||
|
||||
if(readValue == null || property.getType().isAssignableFrom(readValue.getClass())){
|
||||
return readValue;
|
||||
}
|
||||
|
||||
return conversionService.convert(readValue, property.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ package org.springframework.data.cassandra.convert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -291,11 +293,10 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
|
||||
assertThat(addressBook.getOtherCurrencies()).hasSize(1).contains(Currency.getInstance("EUR"));
|
||||
}
|
||||
|
||||
@Test // DATACASS-172
|
||||
@Test // DATACASS-172, DATACASS-400
|
||||
public void shouldWriteUdtWithCustomConversion() {
|
||||
|
||||
Bank bank = new Bank();
|
||||
bank.setCurrency(Currency.getInstance("EUR"));
|
||||
Bank bank = new Bank(null, Currency.getInstance("EUR"), null);
|
||||
|
||||
Insert insert = QueryBuilder.insertInto("bank");
|
||||
converter.write(bank, insert);
|
||||
@@ -316,12 +317,10 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
|
||||
assertThat(update.toString()).isEqualTo("UPDATE money WHERE currency={currency:'EUR'};");
|
||||
}
|
||||
|
||||
@Test // DATACASS-172
|
||||
@Test // DATACASS-172, DATACASS-400
|
||||
public void shouldWriteUdtUpdateAssignmentsWithCustomConversion() {
|
||||
|
||||
MoneyTransfer money = new MoneyTransfer();
|
||||
money.setId("1");
|
||||
money.setCurrency(Currency.getInstance("EUR"));
|
||||
MoneyTransfer money = new MoneyTransfer("1", Currency.getInstance("EUR"));
|
||||
|
||||
Update update = QueryBuilder.update("money");
|
||||
converter.write(money, update);
|
||||
@@ -353,11 +352,10 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
|
||||
assertThat(delete.toString()).isEqualTo("DELETE FROM money WHERE currency={currency:'EUR'};");
|
||||
}
|
||||
|
||||
@Test // DATACASS-172
|
||||
@Test // DATACASS-172, DATACASS-400
|
||||
public void shouldWriteUdtListWithCustomConversion() {
|
||||
|
||||
Bank bank = new Bank();
|
||||
bank.setOtherCurrencies(Collections.singletonList(Currency.getInstance("EUR")));
|
||||
Bank bank = new Bank(null, null, Collections.singletonList(Currency.getInstance("EUR")));
|
||||
|
||||
Insert insert = QueryBuilder.insertInto("bank");
|
||||
converter.write(bank, insert);
|
||||
@@ -379,20 +377,14 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
|
||||
assertThat(car.getEngine().getManufacturer().getName()).isEqualTo("a good one");
|
||||
}
|
||||
|
||||
@Test // DATACASS-172
|
||||
@Test // DATACASS-172, DATACASS-400
|
||||
public void shouldWriteNestedUdt() {
|
||||
|
||||
session.execute("INSERT INTO car (id, engine) VALUES ('1', {manufacturer: {name:'a good one'}});");
|
||||
|
||||
Manufacturer manufacturer = new Manufacturer();
|
||||
manufacturer.setName("a good one");
|
||||
Engine engine = new Engine(new Manufacturer("a good one"));
|
||||
|
||||
Engine engine = new Engine();
|
||||
engine.setManufacturer(manufacturer);
|
||||
|
||||
Car car = new Car();
|
||||
car.setId("1");
|
||||
car.setEngine(engine);
|
||||
Car car = new Car("1", engine);
|
||||
|
||||
Insert insert = QueryBuilder.insertInto("car");
|
||||
converter.write(car, insert);
|
||||
@@ -402,7 +394,8 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
|
||||
}
|
||||
|
||||
@Table
|
||||
@Data
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
private static class Bank {
|
||||
|
||||
@Id String id;
|
||||
@@ -416,8 +409,9 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
|
||||
@Id private Currency currency;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Table
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public static class MoneyTransfer {
|
||||
|
||||
@Id String id;
|
||||
@@ -426,7 +420,8 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
|
||||
}
|
||||
|
||||
@Table
|
||||
@Data
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
private static class Car {
|
||||
|
||||
@Id String id;
|
||||
@@ -434,13 +429,15 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
|
||||
}
|
||||
|
||||
@UserDefinedType
|
||||
@Data
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
private static class Engine {
|
||||
Manufacturer manufacturer;
|
||||
}
|
||||
|
||||
@UserDefinedType
|
||||
@Data
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
private static class Manufacturer {
|
||||
String name;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.cassandra.RowMockUtil.*;
|
||||
import static org.springframework.data.cassandra.repository.support.BasicMapId.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
@@ -482,11 +484,11 @@ public class MappingCassandraConverterUnitTests {
|
||||
assertThat(result.localDate.getDayOfMonth()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test // DATACASS-296
|
||||
@Test // DATACASS-296, DATACASS-400
|
||||
public void shouldCreateInsertWithLocalDateUsingCassandraDateCorrectly() {
|
||||
|
||||
TypeWithLocalDateMappedToDate typeWithLocalDate = new TypeWithLocalDateMappedToDate();
|
||||
typeWithLocalDate.localDate = java.time.LocalDate.of(2010, 7, 4);
|
||||
TypeWithLocalDateMappedToDate typeWithLocalDate = new TypeWithLocalDateMappedToDate(null,
|
||||
java.time.LocalDate.of(2010, 7, 4));
|
||||
|
||||
Insert insert = QueryBuilder.insertInto("table");
|
||||
|
||||
@@ -498,8 +500,8 @@ public class MappingCassandraConverterUnitTests {
|
||||
@Test // DATACASS-296
|
||||
public void shouldCreateUpdateWithLocalDateUsingCassandraDateCorrectly() {
|
||||
|
||||
TypeWithLocalDateMappedToDate typeWithLocalDate = new TypeWithLocalDateMappedToDate();
|
||||
typeWithLocalDate.localDate = java.time.LocalDate.of(2010, 7, 4);
|
||||
TypeWithLocalDateMappedToDate typeWithLocalDate = new TypeWithLocalDateMappedToDate(null,
|
||||
java.time.LocalDate.of(2010, 7, 4));
|
||||
|
||||
Update update = QueryBuilder.update("table");
|
||||
|
||||
@@ -1042,6 +1044,7 @@ public class MappingCassandraConverterUnitTests {
|
||||
* Uses Cassandra's {@link Name#DATE} which maps by default to {@link LocalDate}
|
||||
*/
|
||||
@Table
|
||||
@AllArgsConstructor
|
||||
public static class TypeWithLocalDateMappedToDate {
|
||||
|
||||
@PrimaryKey private String id;
|
||||
|
||||
Reference in New Issue
Block a user