Browse Source

DATAJDBC-164 - Add support for basic @Query annotation.

pull/34/head
Jens Schauder 8 years ago committed by Greg Turnquist
parent
commit
bc209b1114
No known key found for this signature in database
GPG Key ID: CB2FA4D512B5C413
  1. 22
      README.adoc
  2. 8
      src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java
  3. 2
      src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java
  4. 14
      src/main/java/org/springframework/data/jdbc/mapping/model/JdbcMappingContext.java
  5. 6
      src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java
  6. 40
      src/main/java/org/springframework/data/jdbc/repository/query/Query.java
  7. 59
      src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java
  8. 50
      src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryMethod.java
  9. 19
      src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java
  10. 64
      src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryQuery.java
  11. 12
      src/test/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategyUnitTests.java
  12. 10
      src/test/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreterUnitTests.java
  13. 3
      src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java
  14. 4
      src/test/java/org/springframework/data/jdbc/core/SqlGeneratorContextBasedNamingStrategyUnitTests.java
  15. 4
      src/test/java/org/springframework/data/jdbc/core/SqlGeneratorFixedNamingStrategyUnitTests.java
  16. 9
      src/test/java/org/springframework/data/jdbc/core/SqlGeneratorUnitTests.java
  17. 5
      src/test/java/org/springframework/data/jdbc/core/conversion/JdbcEntityDeleteWriterUnitTests.java
  18. 4
      src/test/java/org/springframework/data/jdbc/core/conversion/JdbcEntityWriterUnitTests.java
  19. 6
      src/test/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentEntityInformationUnitTests.java
  20. 4
      src/test/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentPropertyUnitTests.java
  21. 2
      src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
  22. 117
      src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java
  23. 30
      src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java
  24. 1
      src/test/resources/org.springframework.data.jdbc.repository.query/QueryAnnotationHsqlIntegrationTests-hsql.sql

22
README.adoc

@ -111,6 +111,19 @@ But the default mapping itself will stay limited. @@ -111,6 +111,19 @@ But the default mapping itself will stay limited.
If you want highly customizable mappings which support almost everything one can imagine you will probably be much happier with (Spring Data) JPA.
Which is a very powerful and mature technology.
=== 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.
@ -254,9 +267,12 @@ Note that the type used for prefixing the statement name is the name of the aggr @@ -254,9 +267,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
@ -274,7 +290,7 @@ Currently you will need to build it locally. @@ -274,7 +290,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].

8
src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java

@ -24,7 +24,11 @@ import java.util.stream.StreamSupport; @@ -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 { @@ -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);
}

2
src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java

@ -43,7 +43,7 @@ import org.springframework.jdbc.core.RowMapper; @@ -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();

14
src/main/java/org/springframework/data/jdbc/mapping/model/JdbcMappingContext.java

@ -37,6 +37,7 @@ import org.springframework.data.mapping.context.MappingContext; @@ -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 @@ -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 @@ -126,5 +129,4 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt
return conversionService;
}
}

6
src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java

@ -23,6 +23,7 @@ import org.springframework.data.jdbc.mapping.model.ConversionCustomizer; @@ -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; @@ -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 -> {}));
}
}

40
src/main/java/org/springframework/data/jdbc/repository/query/Query.java

@ -0,0 +1,40 @@ @@ -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();
}

59
src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java

@ -0,0 +1,59 @@ @@ -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);
}
}

50
src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryMethod.java

@ -0,0 +1,50 @@ @@ -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();
}
}

19
src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java

@ -15,6 +15,8 @@ @@ -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; @@ -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 { @@ -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));
}
}

64
src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryQuery.java

@ -0,0 +1,64 @@ @@ -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;
}
}

12
src/test/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategyUnitTests.java

@ -15,12 +15,15 @@ @@ -15,12 +15,15 @@
*/
package org.springframework.data.jdbc.core;
import java.util.HashMap;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import lombok.RequiredArgsConstructor;
import java.util.HashMap;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
@ -28,9 +31,6 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; @@ -28,9 +31,6 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.KeyHolder;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* @author Jens Schauder
*/
@ -39,8 +39,8 @@ public class DefaultDataAccessStrategyUnitTests { @@ -39,8 +39,8 @@ public class DefaultDataAccessStrategyUnitTests {
public static final long ID_FROM_ADDITIONAL_VALUES = 23L;
public static final long ORIGINAL_ID = 4711L;
JdbcMappingContext context = new JdbcMappingContext(new DefaultNamingStrategy(), __ -> {});
NamedParameterJdbcOperations jdbcOperations = mock(NamedParameterJdbcOperations.class);
JdbcMappingContext context = new JdbcMappingContext(new DefaultNamingStrategy(), jdbcOperations, __ -> {});
HashMap<String, Object> additionalParameters = new HashMap<>();
ArgumentCaptor<SqlParameterSource> captor = ArgumentCaptor.forClass(SqlParameterSource.class);

10
src/test/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreterUnitTests.java

@ -15,12 +15,14 @@ @@ -15,12 +15,14 @@
*/
package org.springframework.data.jdbc.core;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.conversion.DbAction;
import org.springframework.data.jdbc.core.conversion.DbAction.Insert;
@ -28,9 +30,7 @@ import org.springframework.data.jdbc.core.conversion.JdbcPropertyPath; @@ -28,9 +30,7 @@ import org.springframework.data.jdbc.core.conversion.JdbcPropertyPath;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
/**
* Unit tests for {@link DefaultJdbcInterpreter}
@ -47,7 +47,7 @@ public class DefaultJdbcInterpreterUnitTests { @@ -47,7 +47,7 @@ public class DefaultJdbcInterpreterUnitTests {
public String getReverseColumnName(JdbcPersistentProperty property) {
return BACK_REFERENCE;
}
}, __ -> {});
}, mock(NamedParameterJdbcOperations.class), __ -> {});
DataAccessStrategy dataAccessStrategy = mock(DataAccessStrategy.class);
DefaultJdbcInterpreter interpreter = new DefaultJdbcInterpreter(context, dataAccessStrategy);

3
src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java

@ -43,6 +43,7 @@ import org.springframework.data.convert.Jsr310Converters; @@ -43,6 +43,7 @@ import org.springframework.data.convert.Jsr310Converters;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.util.Assert;
/**
@ -117,7 +118,7 @@ public class EntityRowMapperUnitTests { @@ -117,7 +118,7 @@ public class EntityRowMapperUnitTests {
private <T> EntityRowMapper<T> createRowMapper(Class<T> type) {
JdbcMappingContext context = new JdbcMappingContext();
JdbcMappingContext context = new JdbcMappingContext(mock(NamedParameterJdbcOperations.class));
DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class);
// the ID of the entity is used to determin what kind of resultset is needed for subsequent selects.

4
src/test/java/org/springframework/data/jdbc/core/SqlGeneratorContextBasedNamingStrategyUnitTests.java

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
package org.springframework.data.jdbc.core;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -29,6 +30,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext; @@ -29,6 +30,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
/**
* Unit tests to verify a contextual {@link NamingStrategy} implementation that customizes using a user-centric {@link ThreadLocal}.
@ -185,7 +187,7 @@ public class SqlGeneratorContextBasedNamingStrategyUnitTests { @@ -185,7 +187,7 @@ public class SqlGeneratorContextBasedNamingStrategyUnitTests {
*/
private SqlGenerator configureSqlGenerator(NamingStrategy namingStrategy) {
JdbcMappingContext context = new JdbcMappingContext(namingStrategy, __ -> {});
JdbcMappingContext context = new JdbcMappingContext(namingStrategy, mock(NamedParameterJdbcOperations.class), __ -> {});
JdbcPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class);
return new SqlGenerator(context, persistentEntity, new SqlGeneratorSource(context));

4
src/test/java/org/springframework/data/jdbc/core/SqlGeneratorFixedNamingStrategyUnitTests.java

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
package org.springframework.data.jdbc.core;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
@ -26,6 +27,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity; @@ -26,6 +27,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
/**
* Unit tests the {@link SqlGenerator} with a fixed {@link NamingStrategy} implementation containing a hard wired
@ -180,7 +182,7 @@ public class SqlGeneratorFixedNamingStrategyUnitTests { @@ -180,7 +182,7 @@ public class SqlGeneratorFixedNamingStrategyUnitTests {
*/
private SqlGenerator configureSqlGenerator(NamingStrategy namingStrategy) {
JdbcMappingContext context = new JdbcMappingContext(namingStrategy, __ -> {});
JdbcMappingContext context = new JdbcMappingContext(namingStrategy, mock(NamedParameterJdbcOperations.class), __ -> {});
JdbcPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class);
return new SqlGenerator(context, persistentEntity, new SqlGeneratorSource(context));
}

9
src/test/java/org/springframework/data/jdbc/core/SqlGeneratorUnitTests.java

@ -15,13 +15,15 @@ @@ -15,13 +15,15 @@
*/
package org.springframework.data.jdbc.core;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.Map;
import java.util.Set;
import org.assertj.core.api.SoftAssertions;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
@ -29,8 +31,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity; @@ -29,8 +31,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.mapping.PropertyPath;
import static org.assertj.core.api.Assertions.*;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
/**
* Unit tests for the {@link SqlGenerator}.
@ -46,7 +47,7 @@ public class SqlGeneratorUnitTests { @@ -46,7 +47,7 @@ public class SqlGeneratorUnitTests {
public void setUp() {
NamingStrategy namingStrategy = new PrefixingNamingStrategy();
JdbcMappingContext context = new JdbcMappingContext(namingStrategy, __ -> {});
JdbcMappingContext context = new JdbcMappingContext(namingStrategy, mock(NamedParameterJdbcOperations.class), __ -> {});
JdbcPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class);
this.sqlGenerator = new SqlGenerator(context, persistentEntity, new SqlGeneratorSource(context));
}

5
src/test/java/org/springframework/data/jdbc/core/conversion/JdbcEntityDeleteWriterUnitTests.java

@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
*/
package org.springframework.data.jdbc.core.conversion;
import static org.mockito.Mockito.*;
import lombok.Data;
import org.assertj.core.api.Assertions;
@ -26,6 +28,7 @@ import org.springframework.data.annotation.Id; @@ -26,6 +28,7 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.conversion.AggregateChange.Kind;
import org.springframework.data.jdbc.core.conversion.DbAction.Delete;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
/**
* Unit tests for the {@link JdbcEntityDeleteWriter}
@ -35,7 +38,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext; @@ -35,7 +38,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
@RunWith(MockitoJUnitRunner.class)
public class JdbcEntityDeleteWriterUnitTests {
JdbcEntityDeleteWriter converter = new JdbcEntityDeleteWriter(new JdbcMappingContext());
JdbcEntityDeleteWriter converter = new JdbcEntityDeleteWriter(new JdbcMappingContext(mock(NamedParameterJdbcOperations.class)));
@Test
public void deleteDeletesTheEntityAndReferencedEntities() {

4
src/test/java/org/springframework/data/jdbc/core/conversion/JdbcEntityWriterUnitTests.java

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
package org.springframework.data.jdbc.core.conversion;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import lombok.RequiredArgsConstructor;
@ -33,6 +34,7 @@ import org.springframework.data.jdbc.core.conversion.DbAction.Delete; @@ -33,6 +34,7 @@ import org.springframework.data.jdbc.core.conversion.DbAction.Delete;
import org.springframework.data.jdbc.core.conversion.DbAction.Insert;
import org.springframework.data.jdbc.core.conversion.DbAction.Update;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
/**
* Unit tests for the {@link JdbcEntityWriter}
@ -43,7 +45,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext; @@ -43,7 +45,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
public class JdbcEntityWriterUnitTests {
public static final long SOME_ENTITY_ID = 23L;
JdbcEntityWriter converter = new JdbcEntityWriter(new JdbcMappingContext());
JdbcEntityWriter converter = new JdbcEntityWriter(new JdbcMappingContext(mock(NamedParameterJdbcOperations.class)));
@Test // DATAJDBC-112
public void newEntityGetsConvertedToOneInsert() {

6
src/test/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentEntityInformationUnitTests.java

@ -15,11 +15,13 @@ @@ -15,11 +15,13 @@
*/
package org.springframework.data.jdbc.mapping.model;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.assertj.core.api.Java6Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Persistable;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.lang.Nullable;
/**
@ -27,7 +29,7 @@ import org.springframework.lang.Nullable; @@ -27,7 +29,7 @@ import org.springframework.lang.Nullable;
*/
public class BasicJdbcPersistentEntityInformationUnitTests {
JdbcMappingContext context = new JdbcMappingContext(new DefaultNamingStrategy(), cs -> {});
JdbcMappingContext context = new JdbcMappingContext(new DefaultNamingStrategy(), mock(NamedParameterJdbcOperations.class), cs -> {});
private DummyEntity dummyEntity = new DummyEntity();
private PersistableDummyEntity persistableDummyEntity = new PersistableDummyEntity();

4
src/test/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentPropertyUnitTests.java

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
package org.springframework.data.jdbc.mapping.model;
import static org.assertj.core.api.AssertionsForClassTypes.*;
import static org.mockito.Mockito.*;
import lombok.Data;
@ -26,6 +27,7 @@ import java.util.Date; @@ -26,6 +27,7 @@ import java.util.Date;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
/**
* Unit tests for the {@link BasicJdbcPersistentProperty}.
@ -37,7 +39,7 @@ public class BasicJdbcPersistentPropertyUnitTests { @@ -37,7 +39,7 @@ public class BasicJdbcPersistentPropertyUnitTests {
@Test // DATAJDBC-104
public void enumGetsStoredAsString() {
JdbcPersistentEntity<?> persistentEntity = new JdbcMappingContext()
JdbcPersistentEntity<?> persistentEntity = new JdbcMappingContext(mock(NamedParameterJdbcOperations.class))
.getRequiredPersistentEntity(DummyEntity.class);
persistentEntity.doWithProperties((PropertyHandler<JdbcPersistentProperty>) p -> {

2
src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java

@ -46,7 +46,7 @@ public class SimpleJdbcRepositoryEventsUnitTests { @@ -46,7 +46,7 @@ public class SimpleJdbcRepositoryEventsUnitTests {
@Before
public void before() {
final JdbcMappingContext context = new JdbcMappingContext();
final JdbcMappingContext context = new JdbcMappingContext(mock(NamedParameterJdbcOperations.class));
JdbcRepositoryFactory factory = new JdbcRepositoryFactory( //
publisher, //
context, //

117
src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java

@ -0,0 +1,117 @@ @@ -0,0 +1,117 @@
/*
* 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 static org.assertj.core.api.Assertions.*;
import java.util.List;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
import org.springframework.data.jdbc.testing.TestConfiguration;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;
import org.springframework.transaction.annotation.Transactional;
/**
* Tests the execution of queries from {@link Query} annotations on repository methods.
*
* @author Jens Schauder
*/
@ContextConfiguration
@Transactional
public class QueryAnnotationHsqlIntegrationTests {
@Autowired DummyEntityRepository repository;
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
@Test // DATAJDBC-164
public void executeCustomQueryWithoutParameter() {
repository.save(dummyEntity("Example"));
repository.save(dummyEntity("example"));
repository.save(dummyEntity("EXAMPLE"));
List<DummyEntity> entities = repository.findByNameContainingCapitalLetter();
assertThat(entities) //
.extracting(e -> e.name) //
.containsExactlyInAnyOrder("Example", "EXAMPLE");
}
@Test // DATAJDBC-164
public void executeCustomQueryWithNamedParameters() {
repository.save(dummyEntity("a"));
repository.save(dummyEntity("b"));
repository.save(dummyEntity("c"));
List<DummyEntity> entities = repository.findByNamedRangeWithNamedParameter("a", "c");
assertThat(entities) //
.extracting(e -> e.name) //
.containsExactlyInAnyOrder("b");
}
private DummyEntity dummyEntity(String name) {
DummyEntity entity = new DummyEntity();
entity.name = name;
return entity;
}
@Configuration
@Import(TestConfiguration.class)
@EnableJdbcRepositories(considerNestedRepositories = true)
static class Config {
@Bean
Class<?> testClass() {
return QueryAnnotationHsqlIntegrationTests.class;
}
}
private static class DummyEntity {
@Id Long id;
String name;
}
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
@Query("SELECT * FROM DUMMYENTITY WHERE lower(name) <> name")
List<DummyEntity> findByNameContainingCapitalLetter();
@Query("SELECT * FROM DUMMYENTITY WHERE name < :upper and name > :lower")
List<DummyEntity> findByNamedRangeWithNamedParameter(@Param("lower") String lower, @Param("upper") String upper);
}
}

30
src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java

@ -30,7 +30,10 @@ import org.springframework.data.jdbc.core.DataAccessStrategy; @@ -30,7 +30,10 @@ import org.springframework.data.jdbc.core.DataAccessStrategy;
import org.springframework.data.jdbc.core.DefaultDataAccessStrategy;
import org.springframework.data.jdbc.core.DelegatingDataAccessStrategy;
import org.springframework.data.jdbc.core.SqlGeneratorSource;
import org.springframework.data.jdbc.mapping.model.*;
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.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@ -54,19 +57,16 @@ public class TestConfiguration { @@ -54,19 +57,16 @@ public class TestConfiguration {
@Bean
JdbcRepositoryFactory jdbcRepositoryFactory() {
final JdbcMappingContext context = new JdbcMappingContext(new DefaultNamingStrategy() {
@Override
public String getColumnName(JdbcPersistentProperty property) {
return super.getColumnName(property);
}
}, __ -> {});
NamedParameterJdbcTemplate jdbcTemplate = namedParameterJdbcTemplate();
final JdbcMappingContext context = new JdbcMappingContext(new DefaultNamingStrategy(), jdbcTemplate, __ -> {});
return new JdbcRepositoryFactory( //
publisher, //
context, //
new DefaultDataAccessStrategy( //
new SqlGeneratorSource(context), //
namedParameterJdbcTemplate(), //
jdbcTemplate, //
context) //
);
}
@ -83,7 +83,7 @@ public class TestConfiguration { @@ -83,7 +83,7 @@ public class TestConfiguration {
@Bean
DataAccessStrategy defaultDataAccessStrategy(JdbcMappingContext context,
@Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations operations) {
@Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations operations) {
DelegatingDataAccessStrategy accessStrategy = new DelegatingDataAccessStrategy();
@ -98,11 +98,13 @@ public class TestConfiguration { @@ -98,11 +98,13 @@ public class TestConfiguration {
}
@Bean
JdbcMappingContext jdbcMappingContext(Optional<NamingStrategy> namingStrategy,
Optional<ConversionCustomizer> conversionCustomizer) {
JdbcMappingContext jdbcMappingContext(NamedParameterJdbcOperations template, Optional<NamingStrategy> namingStrategy,
Optional<ConversionCustomizer> conversionCustomizer) {
return new JdbcMappingContext(
namingStrategy.orElse(new DefaultNamingStrategy()),
conversionCustomizer.orElse(conversionService -> {}));
return new JdbcMappingContext( //
namingStrategy.orElse(new DefaultNamingStrategy()), //
template, //
conversionCustomizer.orElse(conversionService -> {}) //
);
}
}

1
src/test/resources/org.springframework.data.jdbc.repository.query/QueryAnnotationHsqlIntegrationTests-hsql.sql

@ -0,0 +1 @@ @@ -0,0 +1 @@
CREATE TABLE dummyentity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100))
Loading…
Cancel
Save