DATAJDBC-164 - Add support for basic @Query annotation.
This commit is contained in:
committed by
Greg Turnquist
parent
2163a26917
commit
bc209b1114
@@ -24,7 +24,11 @@ import java.util.stream.StreamSupport;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
import org.springframework.data.jdbc.mapping.model.*;
|
||||
import org.springframework.data.jdbc.mapping.model.BasicJdbcPersistentEntityInformation;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntityInformation;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.jdbc.support.JdbcUtil;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
@@ -278,7 +282,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
private <T> EntityRowMapper<T> getEntityRowMapper(Class<T> domainType) {
|
||||
public <T> EntityRowMapper<T> getEntityRowMapper(Class<T> domainType) {
|
||||
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), context.getConversions(), context, accessStrategy);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.springframework.jdbc.core.RowMapper;
|
||||
* @author Oliver Gierke
|
||||
* @since 2.0
|
||||
*/
|
||||
class EntityRowMapper<T> implements RowMapper<T> {
|
||||
public class EntityRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
private final JdbcPersistentEntity<T> entity;
|
||||
private final EntityInstantiator instantiator = new ClassGeneratingEntityInstantiator();
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
|
||||
/**
|
||||
* {@link MappingContext} implementation for JDBC.
|
||||
@@ -53,20 +54,22 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt
|
||||
Temporal.class //
|
||||
));
|
||||
|
||||
private final @Getter NamingStrategy namingStrategy;
|
||||
@Getter private final NamingStrategy namingStrategy;
|
||||
@Getter private final NamedParameterJdbcOperations template;
|
||||
private GenericConversionService conversions = getDefaultConversionService();
|
||||
|
||||
public JdbcMappingContext(NamingStrategy namingStrategy, ConversionCustomizer customizer) {
|
||||
public JdbcMappingContext(NamingStrategy namingStrategy, NamedParameterJdbcOperations template,
|
||||
ConversionCustomizer customizer) {
|
||||
|
||||
this.namingStrategy = namingStrategy;
|
||||
this.template = template;
|
||||
|
||||
customizer.customize(conversions);
|
||||
|
||||
setSimpleTypeHolder(new SimpleTypeHolder(CUSTOM_SIMPLE_TYPES, true));
|
||||
}
|
||||
|
||||
public JdbcMappingContext() {
|
||||
this(new DefaultNamingStrategy(), __ -> {});
|
||||
public JdbcMappingContext(NamedParameterJdbcOperations template) {
|
||||
this(new DefaultNamingStrategy(), template, __ -> {});
|
||||
}
|
||||
|
||||
public List<PropertyPath> referencedEntities(Class<?> rootType, PropertyPath path) {
|
||||
@@ -126,5 +129,4 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt
|
||||
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.data.jdbc.mapping.model.ConversionCustomizer;
|
||||
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
|
||||
/**
|
||||
* Beans that must be registered for Spring Data JDBC to work.
|
||||
@@ -33,11 +34,10 @@ import org.springframework.data.jdbc.mapping.model.NamingStrategy;
|
||||
public class JdbcConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcMappingContext jdbcMappingContext(Optional<NamingStrategy> namingStrategy,
|
||||
JdbcMappingContext jdbcMappingContext(NamedParameterJdbcTemplate template, Optional<NamingStrategy> namingStrategy,
|
||||
Optional<ConversionCustomizer> conversionCustomizer) {
|
||||
|
||||
return new JdbcMappingContext(
|
||||
namingStrategy.orElse(new DefaultNamingStrategy()),
|
||||
conversionCustomizer.orElse(conversionService -> {}));
|
||||
namingStrategy.orElse(new DefaultNamingStrategy()), template, conversionCustomizer.orElse(conversionService -> {}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository.query;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.annotation.QueryAnnotation;
|
||||
|
||||
/**
|
||||
* Annotation to provide SQL statements that will get used for executing the method.
|
||||
*
|
||||
* The SQL statement may contain named parameters as supported by {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}.
|
||||
* Those parameters will get bound to the arguments of the annotated method.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@QueryAnnotation
|
||||
@Documented
|
||||
public @interface Query {
|
||||
String value();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.data.jdbc.core.DataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.EntityRowMapper;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.EvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
* {@link QueryLookupStrategy} for JDBC repositories. Currently only supports annotated queries.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
class JdbcQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private final JdbcMappingContext context;
|
||||
private final DataAccessStrategy accessStrategy;
|
||||
|
||||
JdbcQueryLookupStrategy(EvaluationContextProvider evaluationContextProvider, JdbcMappingContext context,
|
||||
DataAccessStrategy accessStrategy) {
|
||||
|
||||
this.context = context;
|
||||
this.accessStrategy = accessStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata,
|
||||
ProjectionFactory projectionFactory, NamedQueries namedQueries) {
|
||||
|
||||
JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory);
|
||||
Class<?> domainType = queryMethod.getReturnedObjectType();
|
||||
RowMapper<?> rowMapper = new EntityRowMapper<>(context.getRequiredPersistentEntity(domainType),
|
||||
context.getConversions(), context, accessStrategy);
|
||||
|
||||
return new JdbcRepositoryQuery(queryMethod, context, rowMapper);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.data.jdbc.repository.query.Query;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
|
||||
/**
|
||||
* {@link QueryMethod} implementation that implements a method by executing the query from a {@link Query} annotation on
|
||||
* that method.
|
||||
*
|
||||
* Binds method arguments to named parameters in the SQL statement.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class JdbcQueryMethod extends QueryMethod {
|
||||
|
||||
private final Method method;
|
||||
|
||||
public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) {
|
||||
super(method, metadata, factory);
|
||||
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getAnnotatedQuery() {
|
||||
|
||||
Query queryAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
|
||||
|
||||
return queryAnnotation == null ? null : queryAnnotation.value();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.jdbc.core.DataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.JdbcEntityTemplate;
|
||||
@@ -25,8 +27,12 @@ import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.EvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
|
||||
/**
|
||||
* Creates repository implementation based on JDBC.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Greg Turnquist
|
||||
* @since 2.0
|
||||
@@ -67,4 +73,17 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
|
||||
return SimpleJdbcRepository.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(QueryLookupStrategy.Key key,
|
||||
EvaluationContextProvider 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));
|
||||
}
|
||||
|
||||
return Optional.of(new JdbcQueryLookupStrategy(evaluationContextProvider, context, accessStrategy));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
|
||||
/**
|
||||
* A query to be executed based on a repository method, it's annotated SQL query and the arguments provided to the
|
||||
* method.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
class JdbcRepositoryQuery implements RepositoryQuery {
|
||||
|
||||
private static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to provide names for method parameters. Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters.";
|
||||
|
||||
private final JdbcQueryMethod queryMethod;
|
||||
private final JdbcMappingContext context;
|
||||
private final RowMapper<?> rowMapper;
|
||||
|
||||
JdbcRepositoryQuery(JdbcQueryMethod queryMethod, JdbcMappingContext context, RowMapper rowMapper) {
|
||||
|
||||
this.queryMethod = queryMethod;
|
||||
this.context = context;
|
||||
|
||||
this.rowMapper = rowMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Object[] objects) {
|
||||
|
||||
String query = queryMethod.getAnnotatedQuery();
|
||||
|
||||
MapSqlParameterSource parameters = new MapSqlParameterSource();
|
||||
queryMethod.getParameters().getBindableParameters().forEach(p -> {
|
||||
|
||||
String parameterName = p.getName().orElseThrow(() -> new IllegalStateException(PARAMETER_NEEDS_TO_BE_NAMED));
|
||||
parameters.addValue(parameterName, objects[p.getIndex()]);
|
||||
});
|
||||
|
||||
return context.getTemplate().query(query, parameters, rowMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcQueryMethod getQueryMethod() {
|
||||
return queryMethod;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user