Browse Source

DATAJDBC-326 - Support conversion of backreferences.

Ids used as backreferences now get properly converted.

Introduced Identifier to hold information about data that needs to be considered for inserts or updates but is not part of the entity.
Apart from column names and values they also hold information about the desired JdbcType in order to facilitate conversions.
This replaces the Map handed around in the past.

Original pull request: #118.
pull/122/head
Jens Schauder 7 years ago committed by Mark Paluch
parent
commit
ade3324e2c
  1. 12
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/CascadingDataAccessStrategy.java
  2. 19
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DataAccessStrategy.java
  3. 61
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java
  4. 34
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java
  5. 10
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DelegatingDataAccessStrategy.java
  6. 8
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
  7. 89
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java
  8. 14
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java
  9. 24
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreterUnitTests.java
  10. 167
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcIdentifierBuilderUnitTests.java
  11. 2
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithMapsIntegrationTests.java
  12. 2
      spring-data-jdbc/src/test/resources/logback.xml
  13. 6
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-hsql.sql
  14. 4
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithMapsIntegrationTests-hsql.sql
  15. 3
      spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/AggregateChange.java
  16. 8
      spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java
  17. 2
      spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java
  18. 13
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java
  19. 2
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java
  20. 91
      spring-data-relational/src/main/java/org/springframework/data/relational/domain/Identifier.java
  21. 13
      spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/AggregateChangeUnitTests.java
  22. 82
      spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityWriterUnitTests.java
  23. 2
      spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/event/IdentifierUnitTests.java
  24. 37
      spring-data-relational/src/test/java/org/springframework/data/relational/domain/IdentifierUnitTests.java

12
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/CascadingDataAccessStrategy.java

@ -21,6 +21,7 @@ import java.util.Map; @@ -21,6 +21,7 @@ import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
@ -47,6 +48,15 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy { @@ -47,6 +48,15 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy {
return collect(das -> das.insert(instance, domainType, additionalParameters));
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, org.springframework.data.jdbc.core.ParentKeys)
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Identifier identifier) {
return collect(das -> das.insert(instance, domainType, identifier));
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#update(java.lang.Object, java.lang.Class)
@ -149,7 +159,7 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy { @@ -149,7 +159,7 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy {
private <T> T collect(Function<DataAccessStrategy, T> function) {
// Keep <T> as Eclipse fails to compile if <> is used.
return strategies.stream().collect(new FunctionCollector<T>(function));
return strategies.stream().collect(new FunctionCollector<>(function));
}
private void collectVoid(Consumer<DataAccessStrategy> consumer) {

19
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DataAccessStrategy.java

@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core; @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core;
import java.util.Map;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.lang.Nullable;
@ -39,9 +40,27 @@ public interface DataAccessStrategy { @@ -39,9 +40,27 @@ public interface DataAccessStrategy {
* to get referenced are contained in this map. Must not be {@code null}.
* @param <T> the type of the instance.
* @return the id generated by the database if any.
*
* @deprecated use {@link #insert(Object, Class, Identifier)} instead.
*/
@Deprecated
<T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters);
/**
* Inserts a the data of a single entity. Referenced entities don't get handled.
*
* @param instance the instance to be stored. Must not be {@code null}.
* @param domainType the type of the instance. Must not be {@code null}.
* @param identifier information about data that needs to be considered for the insert but which is not part of the entity.
* Namely references back to a parent entity and key/index columns for entities that are stored in a {@link Map} or {@link java.util.List}.
* @param <T> the type of the instance.
* @return the id generated by the database if any.
*/
default <T> Object insert(T instance, Class<T> domainType, Identifier identifier){
return insert(instance, domainType, identifier.getParametersByName());
}
/**
* Updates the data of a single entity in the database. Referenced entities don't get handled.
*

61
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java

@ -29,6 +29,7 @@ import java.util.stream.StreamSupport; @@ -29,6 +29,7 @@ import java.util.stream.StreamSupport;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder;
import org.springframework.data.jdbc.support.JdbcUtil;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PersistentPropertyPath;
@ -37,6 +38,7 @@ import org.springframework.data.relational.core.conversion.RelationalConverter; @@ -37,6 +38,7 @@ import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
@ -87,10 +89,24 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { @@ -87,10 +89,24 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
return insert(instance, domainType, JdbcIdentifierBuilder.from(additionalParameters).build());
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Identifier identifier) {
KeyHolder holder = new GeneratedKeyHolder();
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
Map<String, Object> parameters = new LinkedHashMap<>(additionalParameters);
Map<String, Object> parameters = new LinkedHashMap<>();
identifier.forEach(identifierValue -> {
parameters.put(identifierValue.getName(),
converter.writeValue(identifierValue.getValue(), ClassTypeInformation.from(identifierValue.getTargetType())));
});
MapSqlParameterSource parameterSource = getPropertyMap(instance, persistentEntity, "");
@ -282,7 +298,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { @@ -282,7 +298,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
return result;
}
private <S, T> MapSqlParameterSource getPropertyMap(final S instance, RelationalPersistentEntity<S> persistentEntity, String prefix) {
private <S, T> MapSqlParameterSource getPropertyMap(final S instance, RelationalPersistentEntity<S> persistentEntity,
String prefix) {
MapSqlParameterSource parameters = new MapSqlParameterSource();
@ -294,23 +311,26 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { @@ -294,23 +311,26 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
return;
}
if(property.isEmbedded()){
if (property.isEmbedded()) {
Object value = propertyAccessor.getProperty(property);
final RelationalPersistentEntity<?> embeddedEntity = context.getPersistentEntity(property.getType());
final MapSqlParameterSource additionalParameters = getPropertyMap((T)value, (RelationalPersistentEntity<T>) embeddedEntity, prefix + property.getEmbeddedPrefix());
final RelationalPersistentEntity<?> embeddedEntity = context.getPersistentEntity(property.getType());
final MapSqlParameterSource additionalParameters = getPropertyMap((T) value,
(RelationalPersistentEntity<T>) embeddedEntity, prefix + property.getEmbeddedPrefix());
parameters.addValues(additionalParameters.getValues());
} else {
Object value = propertyAccessor.getProperty(property);
Object convertedValue = convertForWrite(property, value);
parameters.addValue(prefix + property.getColumnName(), convertedValue, JdbcUtil.sqlTypeFor(property.getColumnType()));
parameters.addValue(prefix + property.getColumnName(), convertedValue,
JdbcUtil.sqlTypeFor(property.getColumnType()));
}
});
return parameters;
}
@Nullable
private Object convertForWrite(RelationalPersistentProperty property, @Nullable Object value) {
@ -327,9 +347,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { @@ -327,9 +347,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
String typeName = JDBCType.valueOf(JdbcUtil.sqlTypeFor(componentType)).getName();
return operations.getJdbcOperations().execute(
(Connection c) -> c.createArrayOf(typeName, (Object[]) convertedValue)
);
return operations.getJdbcOperations()
.execute((Connection c) -> c.createArrayOf(typeName, (Object[]) convertedValue));
}
@SuppressWarnings("unchecked")
@ -354,22 +373,22 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { @@ -354,22 +373,22 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
@Nullable
private <S> Object getIdFromHolder(KeyHolder holder, RelationalPersistentEntity<S> persistentEntity) {
try {
// MySQL just returns one value with a special name
return holder.getKey();
} catch (DataRetrievalFailureException | InvalidDataAccessApiUsageException e) {
// Postgres returns a value for each column
try {
// MySQL just returns one value with a special name
return holder.getKey();
} catch (DataRetrievalFailureException | InvalidDataAccessApiUsageException e) {
// Postgres returns a value for each column
// MS SQL Server returns a value that might be null.
Map<String, Object> keys = holder.getKeys();
Map<String, Object> keys = holder.getKeys();
if (keys == null || persistentEntity.getIdProperty() == null) {
return null;
}
if (keys == null || persistentEntity.getIdProperty() == null) {
return null;
}
return keys.get(persistentEntity.getIdColumn());
}
}
return keys.get(persistentEntity.getIdColumn());
}
}
private EntityRowMapper<?> getEntityRowMapper(Class<?> domainType) {
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), context, converter, accessStrategy);

34
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java

@ -18,9 +18,10 @@ package org.springframework.data.jdbc.core; @@ -18,9 +18,10 @@ package org.springframework.data.jdbc.core;
import lombok.RequiredArgsConstructor;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.conversion.DbAction;
import org.springframework.data.relational.core.conversion.DbAction.Delete;
@ -58,7 +59,7 @@ class DefaultJdbcInterpreter implements Interpreter { @@ -58,7 +59,7 @@ class DefaultJdbcInterpreter implements Interpreter {
@Override
public <T> void interpret(Insert<T> insert) {
Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), createAdditionalColumnValues(insert));
Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), getParentKeys(insert));
insert.setGeneratedId(id);
}
@ -101,7 +102,7 @@ class DefaultJdbcInterpreter implements Interpreter { @@ -101,7 +102,7 @@ class DefaultJdbcInterpreter implements Interpreter {
// temporary implementation
if (!accessStrategy.update(merge.getEntity(), merge.getEntityType())) {
accessStrategy.insert(merge.getEntity(), merge.getEntityType(), createAdditionalColumnValues(merge));
accessStrategy.insert(merge.getEntity(), merge.getEntityType(), getParentKeys(merge));
}
}
@ -141,27 +142,21 @@ class DefaultJdbcInterpreter implements Interpreter { @@ -141,27 +142,21 @@ class DefaultJdbcInterpreter implements Interpreter {
accessStrategy.deleteAll(deleteAllRoot.getEntityType());
}
private <T> Map<String, Object> createAdditionalColumnValues(DbAction.WithDependingOn<T> action) {
Map<String, Object> additionalColumnValues = new HashMap<>();
addDependingOnInformation(action, additionalColumnValues);
additionalColumnValues.putAll(action.getAdditionalValues());
return additionalColumnValues;
}
private <T> void addDependingOnInformation(DbAction.WithDependingOn<T> action,
Map<String, Object> additionalColumnValues) {
private <T> Identifier getParentKeys(DbAction.WithDependingOn<?> action) {
DbAction.WithEntity<?> dependingOn = action.getDependingOn();
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(dependingOn.getEntityType());
String columnName = getColumnNameForReverseColumn(action);
Object id = getIdFromEntityDependingOn(dependingOn, persistentEntity);
JdbcIdentifierBuilder identifier = JdbcIdentifierBuilder //
.forBackReferences(action.getPropertyPath(), id);
Object identifier = getIdFromEntityDependingOn(dependingOn, persistentEntity);
for (Map.Entry<PersistentPropertyPath<RelationalPersistentProperty>, Object> qualifier : action.getQualifiers().entrySet()) {
identifier = identifier.withQualifier(qualifier.getKey(), qualifier.getValue());
}
additionalColumnValues.put(columnName, identifier);
return identifier.build();
}
@Nullable
@ -182,9 +177,4 @@ class DefaultJdbcInterpreter implements Interpreter { @@ -182,9 +177,4 @@ class DefaultJdbcInterpreter implements Interpreter {
return persistentEntity.getIdentifierAccessor(entity).getIdentifier();
}
private String getColumnNameForReverseColumn(DbAction.WithPropertyPath<?> action) {
PersistentPropertyPath<RelationalPersistentProperty> path = action.getPropertyPath();
return path.getRequiredLeafProperty().getReverseColumnName();
}
}

10
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DelegatingDataAccessStrategy.java

@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core; @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core;
import java.util.Map;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.util.Assert;
@ -40,6 +41,15 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy { @@ -40,6 +41,15 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy {
return delegate.insert(instance, domainType, additionalParameters);
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, org.springframework.data.jdbc.core.ParentKeys)
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Identifier identifier) {
return delegate.insert(instance, domainType, identifier);
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#update(java.lang.Object, java.lang.Class)

8
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java

@ -20,17 +20,23 @@ import org.slf4j.LoggerFactory; @@ -20,17 +20,23 @@ import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.relational.core.conversion.BasicRelationalConverter;
import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import java.sql.Array;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* {@link RelationalConverter} that uses a {@link MappingContext} to apply basic conversion of relational values to
@ -122,4 +128,6 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc @@ -122,4 +128,6 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
return super.writeValue(value, type);
}
}

89
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java

@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
/*
* Copyright 2019 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.jdbc.core.convert;
import java.util.Map;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.lang.Nullable;
/**
* @author Jens Schauder
* @since 1.1
*/
public class JdbcIdentifierBuilder {
private Identifier identifier;
private JdbcIdentifierBuilder(Identifier identifier) {
this.identifier = identifier;
}
public static JdbcIdentifierBuilder empty() {
return new JdbcIdentifierBuilder(Identifier.empty());
}
public static JdbcIdentifierBuilder from(Map<String, Object> additionalParameters) {
Identifier[] identifier = new Identifier[] { Identifier.empty() };
additionalParameters
.forEach((k, v) -> identifier[0] = identifier[0].add(k, v, v == null ? Object.class : v.getClass()));
return new JdbcIdentifierBuilder(identifier[0]);
}
/**
* Creates ParentKeys with backreference for the given path and value of the parents id.
*/
public static JdbcIdentifierBuilder forBackReferences(PersistentPropertyPath<RelationalPersistentProperty> path,
@Nullable Object value) {
Identifier identifier = Identifier.simple( //
path.getRequiredLeafProperty().getReverseColumnName(), //
value, //
getLastIdProperty(path).getColumnType() //
);
return new JdbcIdentifierBuilder(identifier);
}
public JdbcIdentifierBuilder withQualifier(PersistentPropertyPath<RelationalPersistentProperty> path, Object value) {
RelationalPersistentProperty leafProperty = path.getRequiredLeafProperty();
identifier = identifier.add(leafProperty.getKeyColumn(), value, leafProperty.getQualifierColumnType());
return this;
}
public Identifier build() {
return identifier;
}
private static RelationalPersistentProperty getLastIdProperty(
PersistentPropertyPath<RelationalPersistentProperty> path) {
RelationalPersistentProperty idProperty = path.getRequiredLeafProperty().getOwner().getIdProperty();
if (idProperty != null) {
return idProperty;
}
return getLastIdProperty(path.getParentPath());
}
}

14
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java

@ -26,6 +26,7 @@ import org.springframework.data.jdbc.core.CascadingDataAccessStrategy; @@ -26,6 +26,7 @@ import org.springframework.data.jdbc.core.CascadingDataAccessStrategy;
import org.springframework.data.jdbc.core.DataAccessStrategy;
import org.springframework.data.jdbc.core.DefaultDataAccessStrategy;
import org.springframework.data.jdbc.core.DelegatingDataAccessStrategy;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.jdbc.core.SqlGeneratorSource;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PropertyPath;
@ -136,6 +137,19 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { @@ -136,6 +137,19 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
return myBatisContext.getId();
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, ParentKeys)
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Identifier identifier) {
MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType, identifier.getParametersByName());
sqlSession().insert(namespace(domainType) + ".insert", myBatisContext);
return myBatisContext.getId();
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#update(java.lang.Object, java.lang.Class)

24
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreterUnitTests.java

@ -16,12 +16,9 @@ @@ -16,12 +16,9 @@
package org.springframework.data.jdbc.core;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.data.annotation.Id;
@ -31,6 +28,7 @@ import org.springframework.data.relational.core.conversion.DbAction.InsertRoot; @@ -31,6 +28,7 @@ import org.springframework.data.relational.core.conversion.DbAction.InsertRoot;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
/**
* Unit tests for {@link DefaultJdbcInterpreter}
@ -67,10 +65,12 @@ public class DefaultJdbcInterpreterUnitTests { @@ -67,10 +65,12 @@ public class DefaultJdbcInterpreterUnitTests {
interpreter.interpret(insert);
ArgumentCaptor<Map<String, Object>> argumentCaptor = ArgumentCaptor.forClass(Map.class);
ArgumentCaptor<Identifier> argumentCaptor = ArgumentCaptor.forClass(Identifier.class);
verify(dataAccessStrategy).insert(eq(element), eq(Element.class), argumentCaptor.capture());
assertThat(argumentCaptor.getValue()).containsExactly(new SimpleEntry(BACK_REFERENCE, CONTAINER_ID));
assertThat(argumentCaptor.getValue().getParameters()) //
.extracting("name", "value", "targetType") //
.containsExactly(tuple(BACK_REFERENCE, CONTAINER_ID, Long.class));
}
@Test // DATAJDBC-251
@ -80,10 +80,12 @@ public class DefaultJdbcInterpreterUnitTests { @@ -80,10 +80,12 @@ public class DefaultJdbcInterpreterUnitTests {
interpreter.interpret(insert);
ArgumentCaptor<Map<String, Object>> argumentCaptor = ArgumentCaptor.forClass(Map.class);
ArgumentCaptor<Identifier> argumentCaptor = ArgumentCaptor.forClass(Identifier.class);
verify(dataAccessStrategy).insert(eq(element), eq(Element.class), argumentCaptor.capture());
assertThat(argumentCaptor.getValue()).containsExactly(new SimpleEntry(BACK_REFERENCE, CONTAINER_ID));
assertThat(argumentCaptor.getValue().getParameters()) //
.extracting("name", "value", "targetType") //
.containsExactly(tuple(BACK_REFERENCE, CONTAINER_ID, Long.class));
}
@Test // DATAJDBC-251
@ -93,10 +95,12 @@ public class DefaultJdbcInterpreterUnitTests { @@ -93,10 +95,12 @@ public class DefaultJdbcInterpreterUnitTests {
interpreter.interpret(insert);
ArgumentCaptor<Map<String, Object>> argumentCaptor = ArgumentCaptor.forClass(Map.class);
ArgumentCaptor<Identifier> argumentCaptor = ArgumentCaptor.forClass(Identifier.class);
verify(dataAccessStrategy).insert(eq(element), eq(Element.class), argumentCaptor.capture());
assertThat(argumentCaptor.getValue()).containsExactly(new SimpleEntry(BACK_REFERENCE, CONTAINER_ID));
assertThat(argumentCaptor.getValue().getParameters()) //
.extracting("name", "value", "targetType") //
.containsExactly(tuple(BACK_REFERENCE, CONTAINER_ID, Long.class));
}
static class Container {

167
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcIdentifierBuilderUnitTests.java

@ -0,0 +1,167 @@ @@ -0,0 +1,167 @@
/*
* Copyright 2019 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.jdbc.core;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.jdbc.core.PropertyPathUtils.*;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.assertj.core.groups.Tuple;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
/**
* Unit tests for the {@link JdbcIdentifierBuilder}.
*
* @author Jens Schauder
*/
public class JdbcIdentifierBuilderUnitTests {
JdbcMappingContext context = new JdbcMappingContext();
@Test // DATAJDBC-326
public void parametersWithStringKeysUseTheValuesType() {
HashMap<String, Object> parameters = new HashMap<>();
parameters.put("one", "eins");
parameters.put("two", 2L);
Identifier identifier = JdbcIdentifierBuilder.from(parameters).build();
assertThat(identifier.getParameters()) //
.extracting("name", "value", "targetType") //
.containsExactlyInAnyOrder( //
tuple("one", "eins", String.class), //
tuple("two", 2L, Long.class) //
);
}
@Test // DATAJDBC-326
public void parametersWithStringKeysUseObjectAsTypeForNull() {
HashMap<String, Object> parameters = new HashMap<>();
parameters.put("one", null);
Identifier identifier = JdbcIdentifierBuilder.from(parameters).build();
assertThat(identifier.getParameters()) //
.extracting("name", "value", "targetType") //
.containsExactly( //
tuple("one", null, Object.class) //
);
}
@Test // DATAJDBC-326
public void parametersWithPropertyKeysUseTheParentPropertyJdbcType() {
Identifier identifier = JdbcIdentifierBuilder.forBackReferences(getPath("child"), "eins").build();
assertThat(identifier.getParameters()) //
.extracting("name", "value", "targetType") //
.containsExactly( //
tuple("dummy_entity", "eins", UUID.class) //
);
}
@Test // DATAJDBC-326
public void qualifiersForMaps() {
PersistentPropertyPath<RelationalPersistentProperty> path = getPath("children");
Identifier identifier = JdbcIdentifierBuilder //
.forBackReferences(path, "parent-eins") //
.withQualifier(path, "map-key-eins") //
.build();
assertThat(identifier.getParameters()) //
.extracting("name", "value", "targetType") //
.containsExactlyInAnyOrder( //
tuple("dummy_entity", "parent-eins", UUID.class), //
tuple("dummy_entity_key", "map-key-eins", String.class) //
);
}
@Test // DATAJDBC-326
public void qualifiersForLists() {
PersistentPropertyPath<RelationalPersistentProperty> path = getPath("moreChildren");
Identifier identifier = JdbcIdentifierBuilder //
.forBackReferences(path, "parent-eins") //
.withQualifier(path, "list-index-eins") //
.build();
assertThat(identifier.getParameters()) //
.extracting("name", "value", "targetType") //
.containsExactlyInAnyOrder( //
tuple("dummy_entity", "parent-eins", UUID.class), //
tuple("dummy_entity_key", "list-index-eins", Integer.class) //
);
}
@Test // DATAJDBC-326
public void backreferenceAcrossEmbeddable() {
Identifier identifier = JdbcIdentifierBuilder //
.forBackReferences(getPath("embeddable.child"), "parent-eins") //
.build();
assertThat(identifier.getParameters()) //
.extracting("name", "value", "targetType") //
.containsExactly( //
tuple("embeddable", "parent-eins", UUID.class) //
);
}
@NotNull
private PersistentPropertyPath<RelationalPersistentProperty> getPath(String dotPath) {
return toPath(dotPath, DummyEntity.class, context);
}
@SuppressWarnings("unused")
static class DummyEntity {
@Id UUID id;
String one;
Long two;
Child child;
Map<String, Child> children;
List<Child> moreChildren;
Embeddable embeddable;
}
@SuppressWarnings("unused")
static class Embeddable {
Child child;
}
static class Child {}
}

2
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithMapsIntegrationTests.java

@ -96,7 +96,9 @@ public class JdbcRepositoryWithMapsIntegrationTests { @@ -96,7 +96,9 @@ public class JdbcRepositoryWithMapsIntegrationTests {
public void saveAndLoadNonEmptyMap() {
Element element1 = new Element();
element1.content = "element 1";
Element element2 = new Element();
element2.content = "element 2";
DummyEntity entity = createDummyEntity();
entity.content.put("one", element1);

2
spring-data-jdbc/src/test/resources/logback.xml

@ -12,7 +12,7 @@ @@ -12,7 +12,7 @@
<!--<logger name="org.springframework.data.jdbc.mybatis.DummyEntityMapper" level="trace" />-->
<root level="warn">
<appender-ref ref="console" />
<appender-ref ref="console"/>
</root>
</configuration>

6
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-hsql.sql

@ -8,6 +8,10 @@ CREATE TABLE ONE_TO_ONE_PARENT ( id3 BIGINT GENERATED BY DEFAULT AS IDENTITY(STA @@ -8,6 +8,10 @@ CREATE TABLE ONE_TO_ONE_PARENT ( id3 BIGINT GENERATED BY DEFAULT AS IDENTITY(STA
CREATE TABLE Child_No_Id (ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, content VARCHAR(30));
CREATE TABLE LIST_PARENT ( id4 BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE element_no_id ( content VARCHAR(100), LIST_PARENT_key BIGINT, LIST_PARENT BIGINT);
CREATE TABLE ELEMENT_NO_ID ( content VARCHAR(100), LIST_PARENT_KEY BIGINT, LIST_PARENT BIGINT);
ALTER TABLE ELEMENT_NO_ID
ADD FOREIGN KEY (LIST_PARENT)
REFERENCES LIST_PARENT(id4);
CREATE TABLE ARRAY_OWNER (ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, DIGITS VARCHAR(20) ARRAY[10] NOT NULL, MULTIDIMENSIONAL VARCHAR(20) ARRAY[10] NULL);

4
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithMapsIntegrationTests-hsql.sql

@ -1,2 +1,6 @@ @@ -1,2 +1,6 @@
CREATE TABLE dummy_entity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE element (id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, content VARCHAR(100), Dummy_Entity_key VARCHAR(100), dummy_entity BIGINT);
ALTER TABLE ELEMENT
ADD FOREIGN KEY (dummy_entity)
REFERENCES dummy_entity(id);

3
spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/AggregateChange.java

@ -114,8 +114,7 @@ public class AggregateChange<T> { @@ -114,8 +114,7 @@ public class AggregateChange<T> {
if (leafProperty.isQualified()) {
String keyColumn = leafProperty.getKeyColumn();
Object keyObject = action.getAdditionalValues().get(keyColumn);
Object keyObject = action.getQualifiers().get(propertyPathToEntity);
if (List.class.isAssignableFrom(leafProperty.getType())) {
setIdInElementOfList(converter, action, generatedId, (List) currentPropertyValue, (int) keyObject);

8
spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java

@ -75,7 +75,7 @@ public interface DbAction<T> { @@ -75,7 +75,7 @@ public interface DbAction<T> {
@NonNull final PersistentPropertyPath<RelationalPersistentProperty> propertyPath;
@NonNull final WithEntity<?> dependingOn;
Map<String, Object> additionalValues = new HashMap<>();
Map<PersistentPropertyPath<RelationalPersistentProperty>, Object> qualifiers = new HashMap<>();
private Object generatedId;
@ -154,7 +154,7 @@ public interface DbAction<T> { @@ -154,7 +154,7 @@ public interface DbAction<T> {
@NonNull PersistentPropertyPath<RelationalPersistentProperty> propertyPath;
@NonNull WithEntity<?> dependingOn;
Map<String, Object> additionalValues = new HashMap<>();
Map<PersistentPropertyPath<RelationalPersistentProperty>, Object> qualifiers = new HashMap<>();
@Override
public void doExecuteWith(Interpreter interpreter) {
@ -248,7 +248,7 @@ public interface DbAction<T> { @@ -248,7 +248,7 @@ public interface DbAction<T> {
* become available once the parent entity got persisted.
*
* @return Guaranteed to be not {@code null}.
* @see #getAdditionalValues()
* @see #getQualifiers()
*/
WithEntity<?> getDependingOn();
@ -259,7 +259,7 @@ public interface DbAction<T> { @@ -259,7 +259,7 @@ public interface DbAction<T> {
*
* @return Guaranteed to be not {@code null}.
*/
Map<String, Object> getAdditionalValues();
Map<PersistentPropertyPath<RelationalPersistentProperty>, Object> getQualifiers();
@Override
default Class<T> getEntityType() {

2
spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java

@ -127,7 +127,7 @@ class WritingContext { @@ -127,7 +127,7 @@ class WritingContext {
@SuppressWarnings("unchecked")
Pair<Object, Object> value = (Pair) node.getValue();
insert = new DbAction.Insert<>(value.getSecond(), path, getAction(node.getParent()));
insert.getAdditionalValues().put(node.getPath().getRequiredLeafProperty().getKeyColumn(), value.getFirst());
insert.getQualifiers().put(node.getPath(), value.getFirst());
} else {
insert = new DbAction.Insert<>(node.getValue(), path, getAction(node.getParent()));

13
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java

@ -195,6 +195,19 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent @@ -195,6 +195,19 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent
return isMap() || isListLike();
}
@Override
public Class<?> getQualifierColumnType() {
Assert.isTrue(isQualified(), "The qualifier column type is only defined for properties that are qualified");
if (isMap()) {
return getTypeInformation().getComponentType().getType();
}
// for lists and arrays
return Integer.class;
}
@Override
public boolean isOrdered() {
return isListLike();

2
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java

@ -67,6 +67,8 @@ public interface RelationalPersistentProperty extends PersistentProperty<Relatio @@ -67,6 +67,8 @@ public interface RelationalPersistentProperty extends PersistentProperty<Relatio
*/
boolean isQualified();
Class<?> getQualifierColumnType();
/**
* Returns whether this property is an ordered property.
*/

91
spring-data-relational/src/main/java/org/springframework/data/relational/domain/Identifier.java

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
/*
* Copyright 2019 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.relational.domain;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Value;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* {@literal Identifier} represents a multi part id of an entity. Parts or all of the entity might not have a
* representation as a property in the entity but might only be derived from other entities referencing it.
*
* @author Jens Schauder
* @since 1.1
*/
public final class Identifier {
private final List<SingleIdentifierValue> keys;
private Identifier(List<SingleIdentifierValue> keys) {
this.keys = keys;
}
static public Identifier empty() {
return new Identifier(Collections.emptyList());
}
static public Identifier simple(String name, Object value, Class<?> targetType) {
return new Identifier(Collections.singletonList(new SingleIdentifierValue(name, value, targetType)));
}
public Identifier add(String name, Object value, Class<?> targetType) {
List<SingleIdentifierValue> keys = new ArrayList<>(this.keys);
keys.add(new SingleIdentifierValue(name, value, targetType));
return new Identifier(keys);
}
@Deprecated
public Map<String, Object> getParametersByName() {
HashMap<String, Object> result = new HashMap<>();
forEach(v -> result.put(v.name, v.value));
return result;
}
public Collection<SingleIdentifierValue> getParameters() {
return keys;
}
public void forEach(Consumer<SingleIdentifierValue> consumer) {
getParameters().forEach(consumer);
}
/**
* A single value of an Identifier consisting of the column name, the value and the target type which is to be used to
* store the element in the database.
*
* @author Jens Schauder
* @since 1.1
*/
@Value
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public static class SingleIdentifierValue {
String name;
Object value;
Class<?> targetType;
}
}

13
spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/AggregateChangeUnitTests.java

@ -27,7 +27,10 @@ import java.util.Set; @@ -27,7 +27,10 @@ import java.util.Set;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PersistentPropertyPaths;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
/**
* Unit tests for the {@link AggregateChange}.
@ -52,7 +55,7 @@ public class AggregateChangeUnitTests { @@ -52,7 +55,7 @@ public class AggregateChangeUnitTests {
DbAction.Insert<Object> insert = new DbAction.Insert<>(value,
context.getPersistentPropertyPath(propertyName, DummyEntity.class), rootInsert);
insert.getAdditionalValues().put("dummy_entity_key", key);
insert.getQualifiers().put(toPath(propertyName, DummyEntity.class), key);
return insert;
}
@ -112,6 +115,14 @@ public class AggregateChangeUnitTests { @@ -112,6 +115,14 @@ public class AggregateChangeUnitTests {
.containsExactlyInAnyOrder(tuple("one", 23));
}
PersistentPropertyPath<RelationalPersistentProperty> toPath(String path, Class source) {
PersistentPropertyPaths<?, RelationalPersistentProperty> persistentPropertyPaths = context
.findPersistentPropertyPaths(source, p -> true);
return persistentPropertyPaths.filter(p -> p.toDotPath().equals(path)).stream().findFirst().orElse(null);
}
private static class DummyEntity {
@Id Integer rootId;

82
spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityWriterUnitTests.java

@ -30,6 +30,8 @@ import org.junit.Test; @@ -30,6 +30,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PersistentPropertyPaths;
import org.springframework.data.relational.core.conversion.AggregateChange.Kind;
import org.springframework.data.relational.core.conversion.DbAction.Delete;
import org.springframework.data.relational.core.conversion.DbAction.Insert;
@ -37,6 +39,7 @@ import org.springframework.data.relational.core.conversion.DbAction.InsertRoot; @@ -37,6 +39,7 @@ import org.springframework.data.relational.core.conversion.DbAction.InsertRoot;
import org.springframework.data.relational.core.conversion.DbAction.UpdateRoot;
import org.springframework.data.relational.core.mapping.Embedded;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
/**
* Unit tests for the {@link RelationalEntityWriter}
@ -47,8 +50,13 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext @@ -47,8 +50,13 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
@RunWith(MockitoJUnitRunner.class)
public class RelationalEntityWriterUnitTests {
public static final long SOME_ENTITY_ID = 23L;
RelationalEntityWriter converter = new RelationalEntityWriter(new RelationalMappingContext());
static final long SOME_ENTITY_ID = 23L;
final RelationalMappingContext context = new RelationalMappingContext();
final RelationalEntityWriter converter = new RelationalEntityWriter(context);
final PersistentPropertyPath<RelationalPersistentProperty> listContainerElements = toPath("elements", ListContainer.class, context);
private final PersistentPropertyPath<RelationalPersistentProperty> mapContainerElements = toPath("elements", MapContainer.class, context);
@Test // DATAJDBC-112
public void newEntityGetsConvertedToOneInsert() {
@ -60,8 +68,8 @@ public class RelationalEntityWriterUnitTests { @@ -60,8 +68,8 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
DbActionTestSupport::isWithDependsOn) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
.containsExactly( //
tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false) //
);
@ -79,8 +87,8 @@ public class RelationalEntityWriterUnitTests { @@ -79,8 +87,8 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
DbActionTestSupport::isWithDependsOn) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
.containsExactly( //
tuple(InsertRoot.class, EmbeddedReferenceEntity.class, "", EmbeddedReferenceEntity.class, false) //
);
@ -98,8 +106,8 @@ public class RelationalEntityWriterUnitTests { @@ -98,8 +106,8 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
DbActionTestSupport::isWithDependsOn) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
.containsExactly( //
tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false), //
tuple(Insert.class, Element.class, "other", Element.class, true) //
@ -117,8 +125,8 @@ public class RelationalEntityWriterUnitTests { @@ -117,8 +125,8 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
DbActionTestSupport::isWithDependsOn) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
.containsExactly( //
tuple(Delete.class, Element.class, "other", null, false), //
tuple(UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false) //
@ -131,14 +139,14 @@ public class RelationalEntityWriterUnitTests { @@ -131,14 +139,14 @@ public class RelationalEntityWriterUnitTests {
SingleReferenceEntity entity = new SingleReferenceEntity(SOME_ENTITY_ID);
entity.other = new Element(null);
AggregateChange<SingleReferenceEntity> aggregateChange = new AggregateChange<>(Kind.SAVE, SingleReferenceEntity.class,
entity);
AggregateChange<SingleReferenceEntity> aggregateChange = new AggregateChange<>(Kind.SAVE,
SingleReferenceEntity.class, entity);
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
DbActionTestSupport::isWithDependsOn) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
.containsExactly( //
tuple(Delete.class, Element.class, "other", null, false), //
tuple(UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false), //
@ -150,14 +158,14 @@ public class RelationalEntityWriterUnitTests { @@ -150,14 +158,14 @@ public class RelationalEntityWriterUnitTests {
public void newEntityWithEmptySetResultsInSingleInsert() {
SetContainer entity = new SetContainer(null);
AggregateChange<RelationalEntityWriterUnitTests.SetContainer> aggregateChange = new AggregateChange<>(
Kind.SAVE, SetContainer.class, entity);
AggregateChange<RelationalEntityWriterUnitTests.SetContainer> aggregateChange = new AggregateChange<>(Kind.SAVE,
SetContainer.class, entity);
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
DbActionTestSupport::isWithDependsOn) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
.containsExactly( //
tuple(InsertRoot.class, SetContainer.class, "", SetContainer.class, false));
}
@ -173,8 +181,8 @@ public class RelationalEntityWriterUnitTests { @@ -173,8 +181,8 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
DbActionTestSupport::isWithDependsOn) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
.containsExactly( //
tuple(InsertRoot.class, SetContainer.class, "", SetContainer.class, false), //
tuple(Insert.class, Element.class, "elements", Element.class, true), //
@ -203,8 +211,8 @@ public class RelationalEntityWriterUnitTests { @@ -203,8 +211,8 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
DbActionTestSupport::isWithDependsOn) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
.containsExactly( //
tuple(InsertRoot.class, CascadingReferenceEntity.class, "", CascadingReferenceEntity.class, false), //
tuple(Insert.class, CascadingReferenceMiddleElement.class, "other", CascadingReferenceMiddleElement.class,
@ -239,8 +247,8 @@ public class RelationalEntityWriterUnitTests { @@ -239,8 +247,8 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
DbActionTestSupport::isWithDependsOn) //
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
.containsExactly( //
tuple(Delete.class, Element.class, "other.element", null, false),
tuple(Delete.class, CascadingReferenceMiddleElement.class, "other", null, false),
@ -264,7 +272,8 @@ public class RelationalEntityWriterUnitTests { @@ -264,7 +272,8 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
.containsExactly( //
tuple(InsertRoot.class, MapContainer.class, ""));
}
@ -341,7 +350,8 @@ public class RelationalEntityWriterUnitTests { @@ -341,7 +350,8 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
.containsExactly( //
tuple(InsertRoot.class, ListContainer.class, ""));
}
@ -418,17 +428,29 @@ public class RelationalEntityWriterUnitTests { @@ -418,17 +428,29 @@ public class RelationalEntityWriterUnitTests {
}
private Object getMapKey(DbAction a) {
return a instanceof DbAction.WithDependingOn
? ((DbAction.WithDependingOn) a).getAdditionalValues().get("map_container_key")
return a instanceof DbAction.WithDependingOn //
? ((DbAction.WithDependingOn) a).getQualifiers().get(mapContainerElements) //
: null;
}
private Object getListKey(DbAction a) {
return a instanceof DbAction.WithDependingOn
? ((DbAction.WithDependingOn) a).getAdditionalValues().get("list_container_key")
return a instanceof DbAction.WithDependingOn //
? ((DbAction.WithDependingOn) a).getQualifiers()
.get(listContainerElements) //
: null;
}
static PersistentPropertyPath<RelationalPersistentProperty> toPath(String path, Class source,
RelationalMappingContext context) {
PersistentPropertyPaths<?, RelationalPersistentProperty> persistentPropertyPaths = context
.findPersistentPropertyPaths(source, p -> true);
return persistentPropertyPaths.filter(p -> p.toDotPath().equals(path)).stream().findFirst().orElse(null);
}
@RequiredArgsConstructor
static class SingleReferenceEntity {

2
spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/event/IdentifierTest.java → spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/event/IdentifierUnitTests.java

@ -26,7 +26,7 @@ import org.junit.Test; @@ -26,7 +26,7 @@ import org.junit.Test;
*
* @author Jens Schauder
*/
public class IdentifierTest {
public class IdentifierUnitTests {
@SuppressWarnings("unchecked")
@Test

37
spring-data-relational/src/test/java/org/springframework/data/relational/domain/IdentifierUnitTests.java

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
/*
* Copyright 2019 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.relational.domain;
import org.junit.Test;
import java.util.AbstractMap;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Jens Schauder
*/
public class IdentifierUnitTests {
@Test // DATAJDBC-326
public void getParametersByName() {
Identifier identifier = Identifier.simple("aName", "aValue", String.class);;
assertThat(identifier.getParametersByName())
.containsExactly(new AbstractMap.SimpleEntry<>("aName", "aValue"));
}
}
Loading…
Cancel
Save