DATAJDBC-223 - Polishing.

Simplify conditionals with nested ternary operators. Migrate RelationResolver.findAllBy to returning Iterable of Object instead of T as there is no type contract present. Use Spring utilities where applicable. Simplify tests. Fix warnings, Javadoc, Formatting.

Original pull request: #153.
This commit is contained in:
Mark Paluch
2019-05-22 11:56:38 +02:00
parent 097190ee42
commit f69aa31958
26 changed files with 307 additions and 375 deletions

View File

@@ -262,8 +262,7 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
@SuppressWarnings("unchecked")
private ReadingContext(PersistentPropertyPathExtension rootPath, ResultSet resultSet, Identifier identifier,
Object key) {
Object key) {
RelationalPersistentEntity<T> entity = (RelationalPersistentEntity<T>) rootPath.getLeafEntity();
@@ -273,7 +272,8 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
this.resultSet = resultSet;
this.rootPath = rootPath;
this.path = new PersistentPropertyPathExtension(
(MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty>) getMappingContext(), this.entity);
(MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty>) getMappingContext(),
this.entity);
this.identifier = identifier;
this.key = key;
}
@@ -291,8 +291,9 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
}
private <S> ReadingContext<S> extendBy(RelationalPersistentProperty property) {
return new ReadingContext<S>((RelationalPersistentEntity<S>) getMappingContext().getRequiredPersistentEntity(property.getActualType()), resultSet,
rootPath.extendBy(property), path.extendBy(property), identifier, key);
return new ReadingContext<S>(
(RelationalPersistentEntity<S>) getMappingContext().getRequiredPersistentEntity(property.getActualType()),
resultSet, rootPath.extendBy(property), path.extendBy(property), identifier, key);
}
T mapRow() {
@@ -399,25 +400,22 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
}
@Nullable
@SuppressWarnings("unchecked")
private Object readEntityFrom(RelationalPersistentProperty property, PersistentPropertyPathExtension path) {
ReadingContext<?> newContext = extendBy(property);
RelationalPersistentEntity<?> entity = getMappingContext()
.getRequiredPersistentEntity(property.getActualType());
RelationalPersistentEntity<?> entity = getMappingContext().getRequiredPersistentEntity(property.getActualType());
RelationalPersistentProperty idProperty = entity.getIdProperty();
Object idValue = null;
Object idValue;
if (idProperty != null) {
idValue = newContext.readFrom(idProperty);
} else {
idValue = newContext.getObjectFromResultSet(path.extendBy(property).getReverseColumnNameAlias());
}
if ((idProperty != null //
? idValue //
: newContext.getObjectFromResultSet(path.extendBy(property).getReverseColumnNameAlias()) //
) == null) {
if (idValue == null) {
return null;
}

View File

@@ -145,7 +145,8 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy {
* @see org.springframework.data.jdbc.core.RelationResolver#findAllByPath(org.springframework.data.relational.domain.Identifier, org.springframework.data.mapping.PersistentPropertyPath)
*/
@Override
public <T> Iterable<T> findAllByPath(Identifier identifier, PersistentPropertyPath<RelationalPersistentProperty> path) {
public Iterable<Object> findAllByPath(Identifier identifier,
PersistentPropertyPath<RelationalPersistentProperty> path) {
return collect(das -> das.findAllByPath(identifier, path));
}

View File

@@ -149,8 +149,8 @@ public interface DataAccessStrategy extends RelationResolver {
* @see org.springframework.data.jdbc.core.RelationResolver#findAllByPath(org.springframework.data.relational.domain.Identifier, org.springframework.data.mapping.PersistentPropertyPath)
*/
@Override
default <T> Iterable<T> findAllByPath(Identifier identifier,
PersistentPropertyPath<RelationalPersistentProperty> path) {
default Iterable<Object> findAllByPath(Identifier identifier,
PersistentPropertyPath<RelationalPersistentProperty> path) {
Object rootId = identifier.toMap().get(path.getRequiredLeafProperty().getReverseColumnName());
return findAllByProperty(rootId, path.getRequiredLeafProperty());

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.data.jdbc.core.convert;
import lombok.NonNull;
import java.sql.JDBCType;
import java.util.ArrayList;
import java.util.Arrays;
@@ -60,10 +58,10 @@ import org.springframework.util.Assert;
*/
public class DefaultDataAccessStrategy implements DataAccessStrategy {
private final @NonNull SqlGeneratorSource sqlGeneratorSource;
private final @NonNull RelationalMappingContext context;
private final @NonNull JdbcConverter converter;
private final @NonNull NamedParameterJdbcOperations operations;
private final SqlGeneratorSource sqlGeneratorSource;
private final RelationalMappingContext context;
private final JdbcConverter converter;
private final NamedParameterJdbcOperations operations;
/**
* Creates a {@link DefaultDataAccessStrategy}
@@ -75,7 +73,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
* @since 1.1
*/
public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, RelationalMappingContext context,
JdbcConverter converter, NamedParameterJdbcOperations operations) {
JdbcConverter converter, NamedParameterJdbcOperations operations) {
Assert.notNull(sqlGeneratorSource, "SqlGeneratorSource must not be null");
Assert.notNull(context, "RelationalMappingContext must not be null");
@@ -211,16 +209,15 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, java.lang.Class)
*/
@SuppressWarnings("unchecked")
@Override
@SuppressWarnings("unchecked")
public <T> T findById(Object id, Class<T> domainType) {
String findOneSql = sql(domainType).getFindOne();
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
try {
return operations.queryForObject(findOneSql, parameter,
(RowMapper<T>) getEntityRowMapper(domainType));
return operations.queryForObject(findOneSql, parameter, (RowMapper<T>) getEntityRowMapper(domainType));
} catch (EmptyResultDataAccessException e) {
return null;
}
@@ -230,19 +227,18 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findAll(java.lang.Class)
*/
@SuppressWarnings("unchecked")
@Override
@SuppressWarnings("unchecked")
public <T> Iterable<T> findAll(Class<T> domainType) {
return operations.query(sql(domainType).getFindAll(),
(RowMapper<T>) getEntityRowMapper(domainType));
return operations.query(sql(domainType).getFindAll(), (RowMapper<T>) getEntityRowMapper(domainType));
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findAllById(java.lang.Iterable, java.lang.Class)
*/
@SuppressWarnings("unchecked")
@Override
@SuppressWarnings("unchecked")
public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
RelationalPersistentProperty idProperty = getRequiredPersistentEntity(domainType).getRequiredIdProperty();
@@ -252,18 +248,16 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
String findAllInListSql = sql(domainType).getFindAllInList();
return operations.query(findAllInListSql, parameterSource,
(RowMapper<T>) getEntityRowMapper(domainType));
return operations.query(findAllInListSql, parameterSource, (RowMapper<T>) getEntityRowMapper(domainType));
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.RelationResolver#findAllByPath(org.springframework.data.relational.domain.Identifier, org.springframework.data.mapping.PersistentPropertyPath)
*/
@SuppressWarnings("unchecked")
@Override
public <T> Iterable<T> findAllByPath(Identifier identifier,
@SuppressWarnings("unchecked")
public Iterable<Object> findAllByPath(Identifier identifier,
PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
Assert.notNull(identifier, "identifier must not be null.");
@@ -275,16 +269,12 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
String findAllByProperty = sql(actualType) //
.getFindAllByProperty(identifier, path.getQualifierColumn(), path.isOrdered());
MapSqlParameterSource parameters = new MapSqlParameterSource();
MapSqlParameterSource parameters = new MapSqlParameterSource(identifier.toMap());
identifier.forEach((name, value, targetType) -> {
parameters.addValue(name, value);
});
RowMapper<?> rowMapper = path.isMap() ? this.getMapEntityRowMapper(path, identifier)
: this.getEntityRowMapper(path, identifier);
return operations.query(findAllByProperty, parameters, //
(RowMapper<T>) (path.isMap() //
? this.getMapEntityRowMapper(path, identifier) //
: this.getEntityRowMapper(path, identifier)));
return operations.query(findAllByProperty, parameters, (RowMapper<Object>) rowMapper);
}
/*
@@ -293,7 +283,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
*/
@Override
@SuppressWarnings("unchecked")
public <T> Iterable<T> findAllByProperty(Object rootId, RelationalPersistentProperty property) {
public Iterable<Object> findAllByProperty(Object rootId, RelationalPersistentProperty property) {
Assert.notNull(rootId, "rootId must not be null.");
@@ -313,8 +303,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
Boolean result = operations.queryForObject(existsSql, parameter, Boolean.class);
Assert.notNull(result, "The result of an exists query must not be null");
Assert.state(result != null, "The result of an exists query must not be null");
return result;
}
@@ -355,8 +344,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
return parameters;
}
@SuppressWarnings("unchecked")
@Nullable
@SuppressWarnings("unchecked")
private <S, ID> ID getIdValueOrNull(S instance, RelationalPersistentEntity<S> persistentEntity) {
ID idValue = (ID) persistentEntity.getIdentifierAccessor(instance).getIdentifier();
@@ -398,7 +387,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), converter);
}
private EntityRowMapper<?> getEntityRowMapper(PersistentPropertyPathExtension path, Identifier identifier) {
private EntityRowMapper<?> getEntityRowMapper(PersistentPropertyPathExtension path, Identifier identifier) {
return new EntityRowMapper<>(path, converter, identifier);
}
@@ -465,7 +454,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
convertedIds.add(jdbcValue.getValue());
}
Assert.notNull(jdbcValue, "JdbcValue must be not null at this point. Please report this as a bug.");
Assert.state(jdbcValue != null, "JdbcValue must be not null at this point. Please report this as a bug.");
JDBCType jdbcType = jdbcValue.getJdbcType();
int typeNumber = jdbcType == null ? JdbcUtils.TYPE_UNKNOWN : jdbcType.getVendorTypeNumber();
@@ -499,7 +488,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
/**
* A {@link PersistentPropertyAccessor} implementation always returning null
*
*
* @param <T>
*/
static class NoValuePropertyAccessor<T> implements PersistentPropertyAccessor<T> {

View File

@@ -140,7 +140,8 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy {
* @see org.springframework.data.jdbc.core.RelationResolver#findAllByPath(org.springframework.data.relational.domain.Identifier, org.springframework.data.mapping.PersistentPropertyPath)
*/
@Override
public <T> Iterable<T> findAllByPath(Identifier identifier, PersistentPropertyPath<RelationalPersistentProperty> path) {
public Iterable<Object> findAllByPath(Identifier identifier,
PersistentPropertyPath<RelationalPersistentProperty> path) {
return delegate.findAllByPath(identifier, path);
}

View File

@@ -40,6 +40,7 @@ public class EntityRowMapper<T> implements RowMapper<T> {
private final JdbcConverter converter;
private final Identifier identifier;
@SuppressWarnings("unchecked")
public EntityRowMapper(PersistentPropertyPathExtension path, JdbcConverter converter, Identifier identifier) {
this.entity = (RelationalPersistentEntity<T>) path.getLeafEntity();

View File

@@ -34,8 +34,8 @@ import org.springframework.lang.Nullable;
public interface JdbcConverter extends RelationalConverter {
/**
* Convert a property value into a {@link JdbcValue} that contains the converted value and information how to bind
* it to JDBC parameters.
* Convert a property value into a {@link JdbcValue} that contains the converted value and information how to bind it
* to JDBC parameters.
*
* @param value a value as it is used in the object model. May be {@code null}.
* @param type {@link TypeInformation} into which the value is to be converted. Must not be {@code null}.
@@ -44,7 +44,26 @@ public interface JdbcConverter extends RelationalConverter {
*/
JdbcValue writeJdbcValue(@Nullable Object value, Class<?> type, int sqlType);
/**
* Read the current row from {@link ResultSet} to an {@link RelationalPersistentEntity#getType() entity}.
*
* @param entity the persistent entity type.
* @param resultSet the {@link ResultSet} to read from.
* @param key primary key.
* @param <T>
* @return
*/
<T> T mapRow(RelationalPersistentEntity<T> entity, ResultSet resultSet, Object key);
/**
* Read the current row from {@link ResultSet} to an {@link PersistentPropertyPathExtension#getActualType() entity}.
*
* @param path path to the owning property.
* @param resultSet the {@link ResultSet} to read from.
* @param identifier entity identifier.
* @param key primary key.
* @param <T>
* @return
*/
<T> T mapRow(PersistentPropertyPathExtension path, ResultSet resultSet, Identifier identifier, Object key);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.jdbc.core.convert;
import lombok.RequiredArgsConstructor;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
@@ -32,6 +34,7 @@ import org.springframework.lang.NonNull;
*
* @author Jens Schauder
*/
@RequiredArgsConstructor
class MapEntityRowMapper<T> implements RowMapper<Map.Entry<Object, T>> {
private final PersistentPropertyPathExtension path;
@@ -40,21 +43,6 @@ class MapEntityRowMapper<T> implements RowMapper<Map.Entry<Object, T>> {
private final String keyColumn;
/**
* @param path
* @param converter
* @param identifier
* @param keyColumn the name of the key column.
*/
MapEntityRowMapper(PersistentPropertyPathExtension path, JdbcConverter converter,
Identifier identifier, String keyColumn) {
this.path = path;
this.converter = converter;
this.identifier = identifier;
this.keyColumn = keyColumn;
}
@NonNull
@Override
public Map.Entry<Object, T> mapRow(ResultSet rs, int rowNum) throws SQLException {

View File

@@ -23,7 +23,6 @@ import org.springframework.data.relational.domain.Identifier;
* Resolves relations within an aggregate.
*
* @author Jens Schauder
*
* @since 1.1
*/
public interface RelationResolver {
@@ -31,11 +30,10 @@ public interface RelationResolver {
/**
* Finds all entities reachable via {@literal path}.
*
* @param identifier the combination of Id, map keys and list indexes that identify the parent of the entity to be loaded. Must not be {@literal null}.
* @param identifier the combination of Id, map keys and list indexes that identify the parent of the entity to be
* loaded. Must not be {@literal null}.
* @param path the path from the aggregate root to the entities to be resolved. Must not be {@literal null}.
* @param <T> the type of entity created by this class
* @return Guaranteed to be not {@literal null}.
* @return guaranteed to be not {@literal null}.
*/
<T> Iterable<T> findAllByPath(Identifier identifier,
PersistentPropertyPath<RelationalPersistentProperty> path);
Iterable<Object> findAllByPath(Identifier identifier, PersistentPropertyPath<RelationalPersistentProperty> path);
}

View File

@@ -184,7 +184,6 @@ class SqlGenerator {
Table table = getTable();
Condition condition = buildConditionForBackReference(parentIdentifier, table);
SelectBuilder.SelectWhereAndOr withWhereClause = builder.where(condition);
Select select = ordered //
@@ -200,7 +199,6 @@ class SqlGenerator {
for (String backReferenceColumn : parentIdentifier.toMap().keySet()) {
Condition newCondition = table.column(backReferenceColumn).isEqualTo(getBindMarker(backReferenceColumn));
condition = condition == null ? newCondition : condition.and(newCondition);
}

View File

@@ -243,7 +243,7 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
}
@Override
public <T> Iterable<T> findAllByPath(Identifier identifier,
public Iterable<Object> findAllByPath(Identifier identifier,
PersistentPropertyPath<RelationalPersistentProperty> path) {
return sqlSession().selectList(namespace(path.getBaseProperty().getOwner().getType()) + ".findAllByPath",
new MyBatisContext(identifier, null, path.getLeafProperty().getType(), Collections.emptyMap()));

View File

@@ -36,7 +36,6 @@ import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
/**
@@ -86,8 +85,8 @@ public abstract class AbstractJdbcConfiguration {
/**
* Register custom {@link Converter}s in a {@link JdbcCustomConversions} object if required. These
* {@link JdbcCustomConversions} will be registered with the
* {@link #jdbcConverter(RelationalMappingContext, JdbcOperations)}. Returns an empty {@link JdbcCustomConversions}
* instance by default.
* {@link #jdbcConverter(RelationalMappingContext, NamedParameterJdbcOperations, RelationResolver)}. Returns an empty
* {@link JdbcCustomConversions} instance by default.
*
* @return must not be {@literal null}.
*/

View File

@@ -76,9 +76,10 @@ public class JdbcConfiguration {
* @return must not be {@literal null}.
*/
@Bean
public RelationalConverter relationalConverter(RelationalMappingContext mappingContext, @Lazy RelationResolver relationalResolver) {
return new BasicJdbcConverter(mappingContext, relationalResolver, jdbcCustomConversions(), JdbcTypeFactory.unsupported());
public RelationalConverter relationalConverter(RelationalMappingContext mappingContext,
@Lazy RelationResolver relationalResolver) {
return new BasicJdbcConverter(mappingContext, relationalResolver, jdbcCustomConversions(),
JdbcTypeFactory.unsupported());
}
/**
@@ -120,9 +121,7 @@ public class JdbcConfiguration {
*/
@Bean
public DataAccessStrategy dataAccessStrategy(RelationalMappingContext context, JdbcConverter converter,
NamedParameterJdbcOperations operations) {
return new DefaultDataAccessStrategy(new SqlGeneratorSource(context),
context, converter, operations);
NamedParameterJdbcOperations operations) {
return new DefaultDataAccessStrategy(new SqlGeneratorSource(context), context, converter, operations);
}
}

View File

@@ -15,10 +15,11 @@
*/
package org.springframework.data.jdbc.repository.support;
import lombok.RequiredArgsConstructor;
import java.lang.reflect.Method;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.EntityRowMapper;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.repository.QueryMappingConfiguration;
@@ -32,7 +33,6 @@ import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.util.Assert;
/**
* {@link QueryLookupStrategy} for JDBC repositories. Currently only supports annotated queries.
@@ -43,43 +43,15 @@ import org.springframework.util.Assert;
* @author Mark Paluch
* @author Maciej Walkowiak
*/
@RequiredArgsConstructor
class JdbcQueryLookupStrategy implements QueryLookupStrategy {
private final ApplicationEventPublisher publisher;
private final RelationalMappingContext context;
private final JdbcConverter converter;
private final DataAccessStrategy accessStrategy;
private final QueryMappingConfiguration queryMappingConfiguration;
private final NamedParameterJdbcOperations operations;
/**
* Creates a new {@link JdbcQueryLookupStrategy} for the given {@link RelationalMappingContext},
* {@link DataAccessStrategy} and {@link QueryMappingConfiguration}.
*
* @param publisher must not be {@literal null}.
* @param context must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param accessStrategy must not be {@literal null}.
* @param queryMappingConfiguration must not be {@literal null}.
*/
JdbcQueryLookupStrategy(ApplicationEventPublisher publisher, RelationalMappingContext context,
JdbcConverter converter, DataAccessStrategy accessStrategy, QueryMappingConfiguration queryMappingConfiguration,
NamedParameterJdbcOperations operations) {
Assert.notNull(publisher, "Publisher must not be null!");
Assert.notNull(context, "RelationalMappingContext must not be null!");
Assert.notNull(converter, "RelationalConverter must not be null!");
Assert.notNull(accessStrategy, "DataAccessStrategy must not be null!");
Assert.notNull(queryMappingConfiguration, "RowMapperMap must not be null!");
this.publisher = publisher;
this.context = context;
this.converter = converter;
this.accessStrategy = accessStrategy;
this.queryMappingConfiguration = queryMappingConfiguration;
this.operations = operations;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)

View File

@@ -57,14 +57,15 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
/**
* Creates a new {@link JdbcRepositoryFactory} for the given {@link DataAccessStrategy},
* {@link RelationalMappingContext} and {@link ApplicationEventPublisher}.
* @param dataAccessStrategy must not be {@literal null}.
*
* @param dataAccessStrategy must not be {@literal null}.
* @param context must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param publisher must not be {@literal null}.
* @param operations must not be {@literal null}.
*/
public JdbcRepositoryFactory(DataAccessStrategy dataAccessStrategy, RelationalMappingContext context,
JdbcConverter converter, ApplicationEventPublisher publisher, NamedParameterJdbcOperations operations) {
JdbcConverter converter, ApplicationEventPublisher publisher, NamedParameterJdbcOperations operations) {
Assert.notNull(dataAccessStrategy, "DataAccessStrategy must not be null!");
Assert.notNull(context, "RelationalMappingContext must not be null!");
@@ -136,14 +137,14 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable QueryLookupStrategy.Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
if (key != null //
&& key != QueryLookupStrategy.Key.USE_DECLARED_QUERY //
&& key != QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND //
) {
throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
if (key == null || key == QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND
|| key == QueryLookupStrategy.Key.USE_DECLARED_QUERY) {
JdbcQueryLookupStrategy strategy = new JdbcQueryLookupStrategy(publisher, context, converter,
queryMappingConfiguration, operations);
return Optional.of(strategy);
}
return Optional.of(new JdbcQueryLookupStrategy(publisher, context, converter, accessStrategy,
queryMappingConfiguration, operations));
throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
}
}

View File

@@ -20,8 +20,11 @@ import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.jdbc.core.PropertyPathTestingUtils.*;
import java.util.List;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
@@ -32,12 +35,11 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import java.util.List;
/**
* Unit tests for {@link DefaultJdbcInterpreter}
*
* @author Jens Schauder
* @author Mark Paluch
*/
public class DefaultJdbcInterpreterUnitTests {
@@ -53,10 +55,8 @@ public class DefaultJdbcInterpreterUnitTests {
Element element = new Element();
InsertRoot<Container> containerInsert = new InsertRoot<>(container);
Insert<?> elementInsert = new Insert<>(element, toPath("element", Container.class, context),
containerInsert);
Insert<?> element1Insert = new Insert<>(element, toPath("element.element1", Container.class, context),
elementInsert);
Insert<?> elementInsert = new Insert<>(element, toPath("element", Container.class, context), containerInsert);
Insert<?> element1Insert = new Insert<>(element, toPath("element.element1", Container.class, context), elementInsert);
@Test // DATAJDBC-145
public void insertDoesHonourNamingStrategyForBackReference() {
@@ -122,22 +122,22 @@ public class DefaultJdbcInterpreterUnitTests {
@Test // DATAJDBC-223
public void generateCascadingIds() {
RootWithList rootWithList = new RootWithList();
WithList listContainer = new WithList();
ListListContainer listListContainer = new ListListContainer();
ListContainer listContainer = new ListContainer();
InsertRoot<RootWithList> listListContainerInsert = new InsertRoot<>(rootWithList);
InsertRoot<ListListContainer> listListContainerInsert = new InsertRoot<>(listListContainer);
PersistentPropertyPath<RelationalPersistentProperty> listContainersPath = toPath("listContainers", ListListContainer.class, context);
PersistentPropertyPath<RelationalPersistentProperty> listContainersPath = toPath("listContainers",
RootWithList.class, context);
Insert<?> listContainerInsert = new Insert<>(listContainer, listContainersPath, listListContainerInsert);
listContainerInsert.getQualifiers().put(listContainersPath, 3);
PersistentPropertyPath<RelationalPersistentProperty> listContainersElementsPath = toPath("listContainers.elements", ListListContainer.class, context);
PersistentPropertyPath<RelationalPersistentProperty> listContainersElementsPath = toPath("listContainers.elements",
RootWithList.class, context);
Insert<?> elementInsertInList = new Insert<>(element, listContainersElementsPath, listContainerInsert);
elementInsertInList.getQualifiers().put(listContainersElementsPath, 6);
elementInsertInList.getQualifiers().put(listContainersPath, 3);
listListContainerInsert.setGeneratedId(CONTAINER_ID);
interpreter.interpret(elementInsertInList);
@@ -147,10 +147,9 @@ public class DefaultJdbcInterpreterUnitTests {
assertThat(argumentCaptor.getValue().getParts()) //
.extracting("name", "value", "targetType") //
.containsExactly(
tuple("list_list_container", CONTAINER_ID, Long.class), // the top level id
tuple("list_list_container_key", 3, Integer.class), // midlevel key
tuple("list_container_key", 6, Integer.class) // lowlevel key
.containsOnly(tuple("root_with_list", CONTAINER_ID, Long.class), // the top level id
tuple("root_with_list_key", 3, Integer.class), // midlevel key
tuple("with_list_key", 6, Integer.class) // lowlevel key
);
}
@@ -158,7 +157,6 @@ public class DefaultJdbcInterpreterUnitTests {
static class Container {
@Id Long id;
Element element;
}
@@ -167,18 +165,15 @@ public class DefaultJdbcInterpreterUnitTests {
Element1 element1;
}
static class Element1 {
static class Element1 {}
static class RootWithList {
@Id Long id;
List<WithList> listContainers;
}
static class ListListContainer {
@Id
Long id;
List<ListContainer> listContainers;
}
private static class ListContainer {
private static class WithList {
List<Element> elements;
}
}

View File

@@ -19,6 +19,7 @@ import static java.util.Collections.*;
import static org.assertj.core.api.Assertions.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.Arrays;
@@ -27,13 +28,14 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.IntStream;
import org.assertj.core.api.SoftAssertions;
import org.jetbrains.annotations.NotNull;
import org.junit.Assume;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
@@ -505,9 +507,13 @@ public class JdbcAggregateTemplateIntegrationTests {
assertThat(saved.four).describedAs("Something went wrong during saving").isNotNull();
NoIdListChain4 reloaded = template.findById(saved.four, NoIdListChain4.class);
assertThat(reloaded).isEqualTo(saved);
}
assertIsUnchanged(saved, reloaded);
@Test // DATAJDBC-223
public void shouldDeleteChainOfListsWithoutIds() {
NoIdListChain4 saved = template.save(createNoIdTree());
template.deleteById(saved.four, NoIdListChain4.class);
SoftAssertions.assertSoftly(softly -> {
@@ -518,7 +524,6 @@ public class JdbcAggregateTemplateIntegrationTests {
softly.assertThat(count("NO_ID_LIST_CHAIN1")).describedAs("Chain1 elements got deleted").isEqualTo(0);
softly.assertThat(count("NO_ID_LIST_CHAIN0")).describedAs("Chain0 elements got deleted").isEqualTo(0);
});
}
/**
@@ -528,84 +533,43 @@ public class JdbcAggregateTemplateIntegrationTests {
* <li>the xxxValue of each element is a {@literal v} followed by the indices used to navigate to the given instance.
* </li>
* </ul>
*
* @return Guaranteed to be not {@literal null}.
*/
@NotNull
private JdbcAggregateTemplateIntegrationTests.NoIdListChain4 createNoIdTree() {
private static NoIdListChain4 createNoIdTree() {
NoIdListChain4 chain4 = new NoIdListChain4();
chain4.fourValue = "v";
for (int _3 = 0; _3 <= 1; _3++) {
IntStream.of(0, 1).forEach(i -> {
NoIdListChain3 c3 = new NoIdListChain3();
c3.threeValue = chain4.fourValue + _3;
c3.threeValue = chain4.fourValue + i;
chain4.chain3.add(c3);
for (int _2 = 0; _2 <= 1; _2++) {
IntStream.of(0, 1).forEach(j -> {
NoIdListChain2 c2 = new NoIdListChain2();
c2.twoValue = c3.threeValue + _2;
c2.twoValue = c3.threeValue + j;
c3.chain2.add(c2);
for (int _1 = 0; _1 <= 1; _1++) {
IntStream.of(0, 1).forEach(k -> {
NoIdListChain1 c1 = new NoIdListChain1();
c1.oneValue = c2.twoValue + _1;
c1.oneValue = c2.twoValue + k;
c2.chain1.add(c1);
for (int _0 = 0; _0 <= 1; _0++) {
IntStream.of(0, 1).forEach(m -> {
NoIdListChain0 c0 = new NoIdListChain0();
c0.zeroValue = c1.oneValue + _0;
c0.zeroValue = c1.oneValue + m;
c1.chain0.add(c0);
}
}
}
}
});
});
});
});
return chain4;
}
private void assertIsUnchanged(NoIdListChain4 original, NoIdListChain4 reloaded) {
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(reloaded.fourValue).isEqualTo("v");
softly.assertThat(reloaded.chain3).hasSize(2);
for (int _3 = 0; _3 <= 1; _3++) {
NoIdListChain3 c3 = reloaded.chain3.get(_3);
softly.assertThat(c3.threeValue).isEqualTo(original.fourValue + _3);
softly.assertThat(c3.chain2).hasSize(2);
for (int _2 = 0; _2 <= 1; _2++) {
NoIdListChain2 c2 = c3.chain2.get(_2);
softly.assertThat(c2.twoValue).isEqualTo(c3.threeValue + _2);
softly.assertThat(c2.chain1).hasSize(2);
for (int _1 = 0; _1 <= 1; _1++) {
NoIdListChain1 c1 = c2.chain1.get(_1);
softly.assertThat(c1.oneValue).isEqualTo(c2.twoValue + _1);
softly.assertThat(c1.chain0).hasSize(2);
for (int _0 = 0; _0 <= 1; _0++) {
NoIdListChain0 c0 = c1.chain0.get(_0);
softly.assertThat(c0.zeroValue).isEqualTo(c1.oneValue + _0);
}
}
}
}
});
}
@Test // DATAJDBC-223
public void saveAndLoadLongChainOfMapsWithoutIds() {
@@ -614,9 +578,13 @@ public class JdbcAggregateTemplateIntegrationTests {
assertThat(saved.four).isNotNull();
NoIdMapChain4 reloaded = template.findById(saved.four, NoIdMapChain4.class);
assertThat(reloaded).isEqualTo(saved);
}
assertIsUnchanged(saved, reloaded);
@Test // DATAJDBC-223
public void shouldDeleteChainOfMapsWithoutIds() {
NoIdMapChain4 saved = template.save(createNoIdMapTree());
template.deleteById(saved.four, NoIdMapChain4.class);
SoftAssertions.assertSoftly(softly -> {
@@ -627,86 +595,45 @@ public class JdbcAggregateTemplateIntegrationTests {
softly.assertThat(count("NO_ID_MAP_CHAIN1")).describedAs("Chain1 elements got deleted").isEqualTo(0);
softly.assertThat(count("NO_ID_MAP_CHAIN0")).describedAs("Chain0 elements got deleted").isEqualTo(0);
});
}
@NotNull
private JdbcAggregateTemplateIntegrationTests.NoIdMapChain4 createNoIdMapTree() {
private static NoIdMapChain4 createNoIdMapTree() {
NoIdMapChain4 chain4 = new NoIdMapChain4();
chain4.fourValue = "v";
for (int _3 = 0; _3 <= 1; _3++) {
IntStream.of(0, 1).forEach(i -> {
NoIdMapChain3 c3 = new NoIdMapChain3();
c3.threeValue = chain4.fourValue + _3;
chain4.chain3.put(asString(_3), c3);
c3.threeValue = chain4.fourValue + i;
chain4.chain3.put(asString(i), c3);
for (int _2 = 0; _2 <= 1; _2++) {
IntStream.of(0, 1).forEach(j -> {
NoIdMapChain2 c2 = new NoIdMapChain2();
c2.twoValue = c3.threeValue + _2;
c3.chain2.put(asString(_2), c2);
c2.twoValue = c3.threeValue + j;
c3.chain2.put(asString(j), c2);
for (int _1 = 0; _1 <= 1; _1++) {
IntStream.of(0, 1).forEach(k -> {
NoIdMapChain1 c1 = new NoIdMapChain1();
c1.oneValue = c2.twoValue + _1;
c2.chain1.put(asString(_1), c1);
c1.oneValue = c2.twoValue + k;
c2.chain1.put(asString(k), c1);
for (int _0 = 0; _0 <= 1; _0++) {
IntStream.of(0, 1).forEach(it -> {
NoIdMapChain0 c0 = new NoIdMapChain0();
c0.zeroValue = c1.oneValue + _0;
c1.chain0.put(asString(_0), c0);
c0.zeroValue = c1.oneValue + it;
c1.chain0.put(asString(it), c0);
});
});
});
});
}
}
}
}
return chain4;
}
private void assertIsUnchanged(NoIdMapChain4 original, NoIdMapChain4 reloaded) {
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(reloaded.fourValue).isEqualTo("v");
softly.assertThat(reloaded.chain3).hasSize(2);
for (int _3 = 0; _3 <= 1; _3++) {
NoIdMapChain3 c3 = reloaded.chain3.get(asString(_3));
softly.assertThat(c3.threeValue).isEqualTo(original.fourValue + _3);
softly.assertThat(c3.chain2).hasSize(2);
for (int _2 = 0; _2 <= 1; _2++) {
NoIdMapChain2 c2 = c3.chain2.get(asString(_2));
softly.assertThat(c2.twoValue).isEqualTo(c3.threeValue + _2);
softly.assertThat(c2.chain1).hasSize(2);
for (int _1 = 0; _1 <= 1; _1++) {
NoIdMapChain1 c1 = c2.chain1.get(asString(_1));
softly.assertThat(c1.oneValue).isEqualTo(c2.twoValue + _1);
softly.assertThat(c1.chain0).hasSize(2);
for (int _0 = 0; _0 <= 1; _0++) {
NoIdMapChain0 c0 = c1.chain0.get(asString(_0));
softly.assertThat(c0.zeroValue).isEqualTo(c1.oneValue + _0);
}
}
}
}
});
}
private static String asString(int i) {
return "_" + i;
}
@@ -881,25 +808,30 @@ public class JdbcAggregateTemplateIntegrationTests {
/**
* One may think of ChainN as a chain with N further elements
*/
@EqualsAndHashCode
static class NoIdListChain0 {
String zeroValue;
}
@EqualsAndHashCode
static class NoIdListChain1 {
String oneValue;
List<NoIdListChain0> chain0 = new ArrayList<>();
}
@EqualsAndHashCode
static class NoIdListChain2 {
String twoValue;
List<NoIdListChain1> chain1 = new ArrayList<>();
}
@EqualsAndHashCode
static class NoIdListChain3 {
String threeValue;
List<NoIdListChain2> chain2 = new ArrayList<>();
}
@EqualsAndHashCode
static class NoIdListChain4 {
@Id Long four;
String fourValue;
@@ -909,25 +841,30 @@ public class JdbcAggregateTemplateIntegrationTests {
/**
* One may think of ChainN as a chain with N further elements
*/
@EqualsAndHashCode
static class NoIdMapChain0 {
String zeroValue;
}
@EqualsAndHashCode
static class NoIdMapChain1 {
String oneValue;
Map<String, NoIdMapChain0> chain0 = new HashMap<>();
}
@EqualsAndHashCode
static class NoIdMapChain2 {
String twoValue;
Map<String, NoIdMapChain1> chain1 = new HashMap<>();
}
@EqualsAndHashCode
static class NoIdMapChain3 {
String threeValue;
Map<String, NoIdMapChain2> chain2 = new HashMap<>();
}
@EqualsAndHashCode
static class NoIdMapChain4 {
@Id Long four;
String fourValue;

View File

@@ -64,8 +64,8 @@ public class DefaultDataAccessStrategyUnitTests {
public void before() {
DelegatingDataAccessStrategy relationResolver = new DelegatingDataAccessStrategy();
converter = new BasicJdbcConverter(context, relationResolver,
new JdbcCustomConversions(), new DefaultJdbcTypeFactory(jdbcOperations));
converter = new BasicJdbcConverter(context, relationResolver, new JdbcCustomConversions(),
new DefaultJdbcTypeFactory(jdbcOperations));
accessStrategy = new DefaultDataAccessStrategy( //
new SqlGeneratorSource(context), //
context, //

View File

@@ -23,6 +23,7 @@ import static org.mockito.Mockito.*;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@@ -31,14 +32,14 @@ import lombok.experimental.Wither;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.naming.OperationNotSupportedException;
@@ -47,6 +48,7 @@ import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
@@ -415,6 +417,7 @@ public class EntityRowMapperUnitTests {
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@Getter
static class Trivial {
@Id Long id;
@@ -575,6 +578,7 @@ public class EntityRowMapperUnitTests {
return createRowMapper(type, NamingStrategy.INSTANCE);
}
@SuppressWarnings("unchecked")
private <T> EntityRowMapper<T> createRowMapper(Class<T> type, NamingStrategy namingStrategy) {
RelationalMappingContext context = new JdbcMappingContext(namingStrategy);
@@ -582,20 +586,14 @@ public class EntityRowMapperUnitTests {
DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class);
// the ID of the entity is used to determine what kind of ResultSet is needed for subsequent selects.
HashSet<Trivial> trivials = new HashSet<>(asList( //
new Trivial(1L, "one"), //
new Trivial(2L, "two") //
));
Set<Trivial> trivials = Stream.of(new Trivial(1L, "one"), //
new Trivial(2L, "two")) //
.collect(Collectors.toSet());
HashSet<SimpleEntry<Integer, Trivial>> simpleEntriesWithInts = new HashSet<>(asList( //
new SimpleEntry<>(1, new Trivial(1L, "one")), //
new SimpleEntry<>(2, new Trivial(2L, "two")) //
));
HashSet<SimpleEntry<String, Trivial>> simpleEntriesWithStringKeys = new HashSet<>(asList( //
new SimpleEntry<>("one", new Trivial(1L, "one")), //
new SimpleEntry<>("two", new Trivial(2L, "two")) //
));
Set<Map.Entry<Integer, Trivial>> simpleEntriesWithInts = trivials.stream()
.collect(Collectors.toMap(it -> it.getId().intValue(), Function.identity())).entrySet();
Set<Map.Entry<String, Trivial>> simpleEntriesWithStringKeys = trivials.stream()
.collect(Collectors.toMap(Trivial::getName, Function.identity())).entrySet();
doReturn(trivials).when(accessStrategy).findAllByProperty(eq(ID_FOR_ENTITY_NOT_REFERENCING_MAP),
any(RelationalPersistentProperty.class));
@@ -663,16 +661,12 @@ public class EntityRowMapperUnitTests {
return result;
}
@RequiredArgsConstructor
private static class ResultSetAnswer implements Answer {
private final List<Map<String, Object>> values;
private int index = -1;
public ResultSetAnswer(List<Map<String, Object>> values) {
this.values = values;
}
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
@@ -691,9 +685,7 @@ public class EntityRowMapperUnitTests {
return this.toString();
default:
throw new OperationNotSupportedException(invocation.getMethod().getName());
}
}
private boolean isAfterLast() {

View File

@@ -38,6 +38,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.ReflectionUtils;
/**
* Unit tests for {@link JdbcQueryLookupStrategy}.
@@ -65,7 +66,6 @@ public class JdbcQueryLookupStrategyUnitTests {
this.metadata = mock(RepositoryMetadata.class);
doReturn(NumberFormat.class).when(metadata).getReturnedDomainClass(any(Method.class));
}
@Test // DATAJDBC-166
@@ -73,7 +73,8 @@ public class JdbcQueryLookupStrategyUnitTests {
public void typeBasedRowMapperGetsUsedForQuery() {
RowMapper<? extends NumberFormat> numberFormatMapper = mock(RowMapper.class);
QueryMappingConfiguration mappingConfiguration = new DefaultQueryMappingConfiguration().registerRowMapper(NumberFormat.class, numberFormatMapper);
QueryMappingConfiguration mappingConfiguration = new DefaultQueryMappingConfiguration()
.registerRowMapper(NumberFormat.class, numberFormatMapper);
RepositoryQuery repositoryQuery = getRepositoryQuery("returningNumberFormat", mappingConfiguration);
@@ -84,25 +85,17 @@ public class JdbcQueryLookupStrategyUnitTests {
private RepositoryQuery getRepositoryQuery(String name, QueryMappingConfiguration mappingConfiguration) {
JdbcQueryLookupStrategy queryLookupStrategy = new JdbcQueryLookupStrategy(publisher, mappingContext, converter, accessStrategy,
JdbcQueryLookupStrategy queryLookupStrategy = new JdbcQueryLookupStrategy(publisher, mappingContext, converter,
mappingConfiguration, operations);
return queryLookupStrategy.resolveQuery(getMethod(name), metadata, projectionFactory, namedQueries);
Method method = ReflectionUtils.findMethod(MyRepository.class, name);
return queryLookupStrategy.resolveQuery(method, metadata, projectionFactory, namedQueries);
}
// NumberFormat is just used as an arbitrary non simple type.
@Query("some SQL")
private NumberFormat returningNumberFormat() {
return null;
interface MyRepository {
// NumberFormat is just used as an arbitrary non simple type.
@Query("some SQL")
NumberFormat returningNumberFormat();
}
private static Method getMethod(String name) {
try {
return JdbcQueryLookupStrategyUnitTests.class.getDeclaredMethod(name);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -217,7 +217,7 @@ public class BasicRelationalConverter implements RelationalConverter {
* {@link Enum} handling or returns the value as is.
*
* @param value to be converted. May be {@code null}..
* @param target May be {@code null}..
* @param target may be {@code null}..
* @return the converted value if a conversion applies or the original value. Might return {@code null}.
*/
@Nullable

View File

@@ -247,7 +247,7 @@ public interface DbAction<T> {
* persist the entity, that are not part of the current entity, especially the id of the parent, which might only
* become available once the parent entity got persisted.
*
* @return Guaranteed to be not {@code null}.
* @return guaranteed to be not {@code null}.
* @see #getQualifiers()
*/
WithEntity<?> getDependingOn();
@@ -257,7 +257,7 @@ public interface DbAction<T> {
* <p>
* Values come from parent entities but one might also add values manually.
*
* @return Guaranteed to be not {@code null}.
* @return guaranteed to be not {@code null}.
*/
Map<PersistentPropertyPath<RelationalPersistentProperty>, Object> getQualifiers();

View File

@@ -69,7 +69,7 @@ public interface RelationalConverter {
*
* @param persistentEntity the kind of entity to operate on. Must not be {@code null}.
* @param instance the instance to operate on. Must not be {@code null}.
* @return Guaranteed to be not {@code null}.
* @return guaranteed to be not {@code null}.
*/
<T> PersistentPropertyAccessor<T> getPropertyAccessor(PersistentEntity<T, ?> persistentEntity, T instance);

View File

@@ -49,8 +49,8 @@ public class RelationalEntityDeleteWriter implements EntityWriter<Object, Aggreg
* identified by {@code id}. If {@code id} is {@code null} it is interpreted as "Delete all aggregates of the type
* indicated by the aggregateChange".
*
* @param id May be {@code null}.
* @param aggregateChange Must not be {@code null}.
* @param id may be {@code null}.
* @param aggregateChange must not be {@code null}.
*/
@Override
public void write(@Nullable Object id, AggregateChange<?> aggregateChange) {

View File

@@ -39,7 +39,7 @@ public class PersistentPropertyPathExtension {
/**
* Creates the empty path referencing the root itself.
*
*
* @param context Must not be {@literal null}.
* @param entity Root entity of the path. Must not be {@literal null}.
*/
@@ -56,10 +56,10 @@ public class PersistentPropertyPathExtension {
}
/**
* Creates a non empty path
*
* @param context Must not be {@literal null}.
* @param path Must not be {@literal null}.
* Creates a non-empty path.
*
* @param context must not be {@literal null}.
* @param path must not be {@literal null}.
*/
public PersistentPropertyPathExtension(
MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty> context,
@@ -202,19 +202,22 @@ public class PersistentPropertyPathExtension {
}
/**
* Returns the longest ancestor path that has an Id property.
*
* Returns the longest ancestor path that has an {@link org.springframework.data.annotation.Id} property.
*
* @return A path that starts just as this path but is shorter. Guaranteed to be not {@literal null}.
*/
public PersistentPropertyPathExtension getIdDefiningParentPath() {
PersistentPropertyPathExtension parent = getParentPath();
if (parent.path == null) {
return parent;
}
if (!parent.hasIdProperty()) {
return parent.getIdDefiningParentPath();
}
return parent;
}
@@ -267,7 +270,7 @@ public class PersistentPropertyPathExtension {
/**
* Tests if {@code this} and the argument represent the same path.
*
*
* @param path to which this path gets compared. May be {@literal null}.
* @return Whence the argument matches the path represented by this instance.
*/
@@ -277,7 +280,7 @@ public class PersistentPropertyPathExtension {
/**
* The id property of the final element of the path.
*
*
* @return Guaranteed to be not {@literal null}.
* @throws IllegalStateException if no such property exists.
*/
@@ -287,7 +290,7 @@ public class PersistentPropertyPathExtension {
/**
* The column name used for the list index or map key of the leaf property of this path.
*
*
* @return May be {@literal null}.
*/
@Nullable
@@ -298,7 +301,7 @@ public class PersistentPropertyPathExtension {
/**
* The type of the qualifier column of the leaf property of this path or {@literal null} if this is not applicable.
*
* @return May be {@literal null}.
* @return may be {@literal null}.
*/
@Nullable
public Class<?> getQualifierColumnType() {
@@ -307,8 +310,8 @@ public class PersistentPropertyPathExtension {
/**
* Creates a new path by extending the current path by the property passed as an argument.
*
* @param property Must not be {@literal null}.
*
* @param property must not be {@literal null}.
* @return Guaranteed to be not {@literal null}.
*/
public PersistentPropertyPathExtension extendBy(RelationalPersistentProperty property) {
@@ -331,7 +334,7 @@ public class PersistentPropertyPathExtension {
/**
* For empty paths this is the type of the entity. For non empty paths this is the actual type of the leaf property.
*
*
* @return Guaranteed to be not {@literal null}.
* @see PersistentProperty#getActualType()
*/
@@ -360,7 +363,7 @@ public class PersistentPropertyPathExtension {
/**
* Converts this path to a non-null {@link PersistentPropertyPath}.
*
*
* @return Guaranteed to be not {@literal null}.
* @throws IllegalStateException if this path is empty.
*/

View File

@@ -29,6 +29,7 @@ import java.util.Set;
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;
@@ -47,6 +48,7 @@ import org.springframework.lang.Nullable;
*
* @author Jens Schauder
* @author Bastian Wilhelm
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class RelationalEntityWriterUnitTests {
@@ -67,8 +69,8 @@ public class RelationalEntityWriterUnitTests {
private final PersistentPropertyPath<RelationalPersistentProperty> listMapContainerMaps = toPath("maps",
ListMapContainer.class, context);
private final PersistentPropertyPath<RelationalPersistentProperty> noIdListMapContainerElements = toPath("maps.elements",
NoIdListMapContainer.class, context);
private final PersistentPropertyPath<RelationalPersistentProperty> noIdListMapContainerElements = toPath(
"maps.elements", NoIdListMapContainer.class, context);
private final PersistentPropertyPath<RelationalPersistentProperty> noIdListMapContainerMaps = toPath("maps",
NoIdListMapContainer.class, context);
@@ -83,8 +85,11 @@ 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) //
);
@@ -102,8 +107,11 @@ 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) //
);
@@ -121,8 +129,11 @@ 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) //
@@ -140,8 +151,11 @@ 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) //
@@ -160,8 +174,11 @@ 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), //
@@ -179,8 +196,11 @@ 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));
}
@@ -195,9 +215,11 @@ public class RelationalEntityWriterUnitTests {
AggregateChange<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) //
assertThat(aggregateChange.getActions()).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), //
@@ -225,9 +247,11 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
assertThat(aggregateChange.getActions()).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,
@@ -261,9 +285,11 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
assertThat(aggregateChange.getActions()).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),
@@ -287,8 +313,9 @@ 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, ""));
}
@@ -303,8 +330,10 @@ public class RelationalEntityWriterUnitTests {
AggregateChange<MapContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, MapContainer.class, entity);
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, DbActionTestSupport::extractPath) //
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, //
DbAction::getEntityType, //
this::getMapKey, //
DbActionTestSupport::extractPath) //
.containsExactlyInAnyOrder( //
tuple(InsertRoot.class, MapContainer.class, null, ""), //
tuple(Insert.class, Element.class, "one", "elements"), //
@@ -322,6 +351,7 @@ public class RelationalEntityWriterUnitTests {
public void newEntityWithFullMapResultsInAdditionalInsertPerElement() {
MapContainer entity = new MapContainer(null);
entity.elements.put("1", new Element(null));
entity.elements.put("2", new Element(null));
entity.elements.put("3", new Element(null));
@@ -338,8 +368,10 @@ public class RelationalEntityWriterUnitTests {
AggregateChange<MapContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, MapContainer.class, entity);
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, DbActionTestSupport::extractPath) //
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, //
DbAction::getEntityType, //
this::getMapKey, //
DbActionTestSupport::extractPath) //
.containsExactlyInAnyOrder( //
tuple(InsertRoot.class, MapContainer.class, null, ""), //
tuple(Insert.class, Element.class, "1", "elements"), //
@@ -365,8 +397,9 @@ 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, ""));
}
@@ -381,8 +414,10 @@ public class RelationalEntityWriterUnitTests {
AggregateChange<ListContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, ListContainer.class, entity);
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, this::getListKey, DbActionTestSupport::extractPath) //
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, //
DbAction::getEntityType, //
this::getListKey, //
DbActionTestSupport::extractPath) //
.containsExactlyInAnyOrder( //
tuple(InsertRoot.class, ListContainer.class, null, ""), //
tuple(Insert.class, Element.class, 0, "elements"), //
@@ -407,7 +442,10 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, DbActionTestSupport::extractPath) //
.extracting(DbAction::getClass, //
DbAction::getEntityType, //
this::getMapKey, //
DbActionTestSupport::extractPath) //
.containsExactly( //
tuple(Delete.class, Element.class, null, "elements"), //
tuple(UpdateRoot.class, MapContainer.class, null, ""), //
@@ -426,7 +464,10 @@ public class RelationalEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, this::getListKey, DbActionTestSupport::extractPath) //
.extracting(DbAction::getClass, //
DbAction::getEntityType, //
this::getListKey, //
DbActionTestSupport::extractPath) //
.containsExactly( //
tuple(Delete.class, Element.class, null, "elements"), //
tuple(UpdateRoot.class, ListContainer.class, null, ""), //
@@ -447,7 +488,11 @@ public class RelationalEntityWriterUnitTests {
converter.write(listMapContainer, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, a -> getQualifier(a, listMapContainerMaps),a -> getQualifier(a, listMapContainerElements), DbActionTestSupport::extractPath) //
.extracting(DbAction::getClass, //
DbAction::getEntityType, //
a -> getQualifier(a, listMapContainerMaps), //
a -> getQualifier(a, listMapContainerElements), //
DbActionTestSupport::extractPath) //
.containsExactly( //
tuple(Delete.class, Element.class, null, null, "maps.elements"), //
tuple(Delete.class, MapContainer.class, null, null, "maps"), //
@@ -470,7 +515,11 @@ public class RelationalEntityWriterUnitTests {
converter.write(listMapContainer, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, a -> getQualifier(a, noIdListMapContainerMaps),a -> getQualifier(a, noIdListMapContainerElements), DbActionTestSupport::extractPath) //
.extracting(DbAction::getClass, //
DbAction::getEntityType, //
a -> getQualifier(a, noIdListMapContainerMaps), //
a -> getQualifier(a, noIdListMapContainerElements), //
DbActionTestSupport::extractPath) //
.containsExactly( //
tuple(Delete.class, NoIdElement.class, null, null, "maps.elements"), //
tuple(Delete.class, NoIdMapContainer.class, null, null, "maps"), //
@@ -598,7 +647,6 @@ public class RelationalEntityWriterUnitTests {
@Id final Long id;
}
@RequiredArgsConstructor
private static class NoIdListMapContainer {