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);
}
}
}