DATAJDBC-164 - Add support for basic @Query annotation.
This commit is contained in:
committed by
Greg Turnquist
parent
2163a26917
commit
bc209b1114
22
README.adoc
22
README.adoc
@@ -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
|
||||
|
||||
== 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.
|
||||
== 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].
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
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 {
|
||||
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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
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 {
|
||||
public String getReverseColumnName(JdbcPersistentProperty property) {
|
||||
return BACK_REFERENCE;
|
||||
}
|
||||
}, __ -> {});
|
||||
}, mock(NamedParameterJdbcOperations.class), __ -> {});
|
||||
|
||||
DataAccessStrategy dataAccessStrategy = mock(DataAccessStrategy.class);
|
||||
DefaultJdbcInterpreter interpreter = new DefaultJdbcInterpreter(context, dataAccessStrategy);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
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.
|
||||
|
||||
@@ -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;
|
||||
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 {
|
||||
*/
|
||||
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));
|
||||
|
||||
@@ -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;
|
||||
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 {
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
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;
|
||||
@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() {
|
||||
|
||||
@@ -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;
|
||||
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;
|
||||
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() {
|
||||
|
||||
@@ -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;
|
||||
*/
|
||||
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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
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 {
|
||||
@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 -> {
|
||||
|
||||
@@ -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, //
|
||||
|
||||
@@ -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,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 {
|
||||
@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 {
|
||||
|
||||
@Bean
|
||||
DataAccessStrategy defaultDataAccessStrategy(JdbcMappingContext context,
|
||||
@Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations operations) {
|
||||
@Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations operations) {
|
||||
|
||||
DelegatingDataAccessStrategy accessStrategy = new DelegatingDataAccessStrategy();
|
||||
|
||||
@@ -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 -> {}) //
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
CREATE TABLE dummyentity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100))
|
||||
Reference in New Issue
Block a user