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

This commit is contained in:
Jens Schauder
2018-01-09 15:47:04 +01:00
committed by Greg Turnquist
parent 2163a26917
commit bc209b1114
24 changed files with 442 additions and 53 deletions

View File

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

View File

@@ -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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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.

View File

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

View File

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

View File

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

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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();

View File

@@ -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 -> {

View File

@@ -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, //

View File

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

View File

@@ -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 -> {}) //
);
}
}

View File

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