Browse Source

Consider convertible argument types in in JDBC query derivation.

Closes #2059
Original pull request: #2249

Signed-off-by: wonderfulrosemari <whwlsgur1419@naver.com>
4.0.x
wonderfulrosemari 2 weeks ago committed by Mark Paluch
parent
commit
3bc65275b9
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 20
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java
  2. 3
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java
  3. 77
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java

20
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java

@ -24,6 +24,7 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter; @@ -24,6 +24,7 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.conversion.AbstractRelationalConverter;
import org.springframework.data.relational.core.mapping.AggregatePath;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
@ -47,6 +48,7 @@ import org.springframework.util.Assert; @@ -47,6 +48,7 @@ import org.springframework.util.Assert;
* @author Jens Schauder
* @author Myeonghyeon Lee
* @author Diego Krupitza
* @author wonderfulrosemari
* @since 2.0
*/
public class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> {
@ -152,7 +154,8 @@ public class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> @@ -152,7 +154,8 @@ public class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery>
* @param tree the tree structure defining the predicate of the query.
* @param parameters parameters for the predicate.
*/
static void validate(PartTree tree, Parameters<?, ?> parameters, RelationalMappingContext context) {
static void validate(PartTree tree, Parameters<?, ?> parameters, RelationalMappingContext context,
JdbcConverter converter) {
RelationalQueryCreator.validate(tree, parameters);
@ -163,12 +166,12 @@ public class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> @@ -163,12 +166,12 @@ public class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery>
.getPersistentPropertyPath(part.getProperty());
AggregatePath path = context.getAggregatePath(propertyPath);
path.forEach(JdbcQueryCreator::validateProperty);
path.forEach(pathElement -> validateProperty(pathElement, converter));
}
}
}
private static void validateProperty(AggregatePath path) {
private static void validateProperty(AggregatePath path, JdbcConverter converter) {
if (path.isRoot()) {
return;
@ -183,11 +186,20 @@ public class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> @@ -183,11 +186,20 @@ public class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery>
String.format("Cannot query by multi-valued property: %s", path.getRequiredLeafProperty().getName()));
}
if (!path.isEmbedded() && path.isEntity()) {
if (!path.isEmbedded() && path.isEntity() && !hasCustomWriteTarget(path, converter)) {
throw new IllegalArgumentException(String.format("Cannot query by nested entity: %s", path.toDotPath()));
}
}
private static boolean hasCustomWriteTarget(AggregatePath path, JdbcConverter converter) {
if (!(converter instanceof AbstractRelationalConverter relationalConverter)) {
return false;
}
return relationalConverter.getConversions().hasCustomWriteTarget(path.getRequiredLeafProperty().getActualType());
}
/**
* Creates {@link ParametrizedQuery} applying the given {@link Criteria} and {@link Sort} definition.
*

3
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java

@ -61,6 +61,7 @@ import org.springframework.util.Assert; @@ -61,6 +61,7 @@ import org.springframework.util.Assert;
* @author Mikhail Polivakha
* @author Yunyoung LEE
* @author Nikita Konev
* @author wonderfulrosemari
* @since 2.0
*/
public class PartTreeJdbcQuery extends AbstractJdbcQuery {
@ -145,7 +146,7 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery { @@ -145,7 +146,7 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery {
this.converter = converter;
this.tree = new PartTree(queryMethod.getName(), queryMethod.getResultProcessor().getReturnedType().getDomainType());
JdbcQueryCreator.validate(this.tree, this.parameters, this.converter.getMappingContext());
JdbcQueryCreator.validate(this.tree, this.parameters, this.converter.getMappingContext(), this.converter);
this.cachedRowMapperFactory = new CachedRowMapperFactory(tree, rowMapperFactory, converter,
queryMethod.getResultProcessor());

77
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java

@ -33,6 +33,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @@ -33,6 +33,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@ -73,6 +74,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; @@ -73,6 +74,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
* @author Jens Schauder
* @author Myeonghyeon Lee
* @author Diego Krupitza
* @author wonderfulrosemari
*/
@ExtendWith(MockitoExtension.class)
public class PartTreeJdbcQueryUnitTests {
@ -753,15 +755,46 @@ public class PartTreeJdbcQueryUnitTests { @@ -753,15 +755,46 @@ public class PartTreeJdbcQueryUnitTests {
.isEqualTo("SELECT COUNT(*) FROM " + TABLE + " WHERE " + TABLE + ".\"FIRST_NAME\" = :first_name");
}
@Test // GH-2059
void createsQueryBySimpleDomainPrimitiveWithCustomConverters() throws Exception {
JdbcCustomConversions conversions = JdbcCustomConversions.create(JdbcPostgresDialect.INSTANCE, it -> {
it.registerConverter(CustomerRefToStringConverter.INSTANCE);
it.registerConverter(StringToCustomerRefConverter.INSTANCE);
});
JdbcMappingContext localContext = new JdbcMappingContext();
JdbcConverter localConverter = new MappingJdbcConverter(localContext, mock(RelationResolver.class), conversions,
JdbcTypeFactory.unsupported());
JdbcQueryMethod queryMethod = getQueryMethod(CustomerRepository.class, localContext, "findAllByRef",
CustomerRef.class);
PartTreeJdbcQuery jdbcQuery = new PartTreeJdbcQuery(localContext, queryMethod, JdbcH2Dialect.INSTANCE,
localConverter, mock(NamedParameterJdbcOperations.class), mock(RowMapper.class));
ParametrizedQuery query = jdbcQuery.createQuery(getAccessor(queryMethod, new Object[] { new CustomerRef("abc") }),
returnedType);
QueryAssert.assertThat(query).contains(" WHERE \"customers\".\"REF\" = :ref").hasBindValue("ref", "abc");
}
private PartTreeJdbcQuery createQuery(JdbcQueryMethod queryMethod) {
return new PartTreeJdbcQuery(mappingContext, queryMethod, JdbcH2Dialect.INSTANCE, converter,
mock(NamedParameterJdbcOperations.class), mock(RowMapper.class));
}
private JdbcQueryMethod getQueryMethod(String methodName, Class<?>... parameterTypes) throws Exception {
Method method = UserRepository.class.getMethod(methodName, parameterTypes);
return new JdbcQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory(), new PropertiesBasedNamedQueries(new Properties()), mappingContext);
return getQueryMethod(UserRepository.class, methodName, parameterTypes);
}
private JdbcQueryMethod getQueryMethod(Class<?> repositoryType, String methodName, Class<?>... parameterTypes)
throws Exception {
return getQueryMethod(repositoryType, mappingContext, methodName, parameterTypes);
}
private JdbcQueryMethod getQueryMethod(Class<?> repositoryType, JdbcMappingContext mappingContext, String methodName,
Class<?>... parameterTypes) throws Exception {
Method method = repositoryType.getMethod(methodName, parameterTypes);
return new JdbcQueryMethod(method, new DefaultRepositoryMetadata(repositoryType), new SpelAwareProxyProjectionFactory(),
new PropertiesBasedNamedQueries(new Properties()), mappingContext);
}
private RelationalParametersParameterAccessor getAccessor(JdbcQueryMethod queryMethod, Object[] values) {
@ -866,6 +899,12 @@ public class PartTreeJdbcQueryUnitTests { @@ -866,6 +899,12 @@ public class PartTreeJdbcQueryUnitTests {
long countByFirstName(String name);
}
@NoRepositoryBean
interface CustomerRepository extends Repository<Customer, Long> {
List<Customer> findAllByRef(CustomerRef ref);
}
@Table("users")
static class User {
@ -890,6 +929,16 @@ public class PartTreeJdbcQueryUnitTests { @@ -890,6 +929,16 @@ public class PartTreeJdbcQueryUnitTests {
record UserId(Long id, String subId) {
}
@Table("customers")
static class Customer {
@Id Long id;
CustomerRef ref;
}
record CustomerRef(String value) {
}
record Address(String street, String city) {
}
@ -914,4 +963,26 @@ public class PartTreeJdbcQueryUnitTests { @@ -914,4 +963,26 @@ public class PartTreeJdbcQueryUnitTests {
return JdbcValue.of(source.ordinal(), JdbcPostgresDialect.INSTANCE.createSqlType("foo", 4711));
}
}
@WritingConverter
enum CustomerRefToStringConverter implements Converter<CustomerRef, String> {
INSTANCE;
@Override
public String convert(CustomerRef source) {
return source.value();
}
}
@ReadingConverter
enum StringToCustomerRefConverter implements Converter<String, CustomerRef> {
INSTANCE;
@Override
public CustomerRef convert(String source) {
return new CustomerRef(source);
}
}
}

Loading…
Cancel
Save