Skip to content

DATAJDBC-164 - Added support of basic @Query annotation. #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ public void someMethod() {
}
----

=== Query annotation

You can annotate a query method with `@Query` to specify a SQL statement to be used for that method.
You can bind method arguments using named parameters in the SQL statement like in the following example:

[source,java]
----
@Query("SELECT * FROM DUMMYENTITY WHERE name < :upper and name > :lower")
List<DummyEntity> findByNameRange(@Param("lower") String lower, @Param("upper") String upper);
----

If you compile your sources with the `-parameters` compiler flag you can omit the `@Param` annotations.

=== Id generation

Spring Data JDBC uses the id to identify entities but also to determine if an entity is new or already existing in the database.
Expand Down Expand Up @@ -200,9 +213,12 @@ Note that the type used for prefixing the statement name is the name of the aggr

== Features planned for the not to far future

=== Query annotation
=== Advance query annotation support

Just annotate a method with a SQL query to use this whenever the method gets called.
* customizable `RowMapper`
* projections
* modifying queries
* SpEL expressions

=== MyBatis per method support

Expand All @@ -218,7 +234,7 @@ Currently you will need to build it locally.
== Getting Help

Right now the best source of information is the source code in this repository.
Especially the integration tests (type `t` and then `IntegrationTests.java`)
Especially the integration tests (When you are reading this on github type `t` and then `IntegrationTests.java`)

We are keeping an eye on the (soon to be created) https://stackoverflow.com/questions/tagged/spring-data-jdbc[spring-data-jdbc tag on stackoverflow].

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jdbc</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.0.DATAJDBC-164-SNAPSHOT</version>

<name>Spring Data JDBC</name>
<description>Spring Data module for JDBC repositories.</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@
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;
Expand Down Expand Up @@ -278,7 +282,7 @@ private <S> Optional<Object> getIdFromHolder(KeyHolder holder, JdbcPersistentEnt
}
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
* @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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
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.
Expand All @@ -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) {
Expand Down Expand Up @@ -126,5 +129,4 @@ private static GenericConversionService getDefaultConversionService() {

return conversionService;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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.
Expand All @@ -33,11 +34,10 @@
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 -> {}));
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,8 +27,12 @@
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
Expand Down Expand Up @@ -67,4 +73,17 @@ protected Class<?> getRepositoryBaseClass(RepositoryMetadata repositoryMetadata)
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));
}
}
Loading