DATAJPA-107 - Add support to specify TemporalType for Date query parameters.
Introduced JpaParameters abstraction to support custom jpa-specific annotations on query parameters. Adjusted Parameter binders to use the JpaParameters abstraction. Added special handling for temporal JpaParameters to ParameterBinder.bind(…) and CriteriaQueryParameterBinder.bind(…). Adapted changes introduced in DATACMNS-350. Original pull request: #31.
This commit is contained in:
committed by
Oliver Gierke
parent
fbb88e8c76
commit
619c1e6a55
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2013 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.jpa.repository.query;
|
||||
|
||||
import static javax.persistence.TemporalType.*;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.jpa.repository.Temporal;
|
||||
import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link JpaParameters}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class JpaParametersUnitTests {
|
||||
|
||||
@Test
|
||||
public void findsTemporalParameterConfiguration() throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod("foo", Date.class, String.class);
|
||||
|
||||
JpaParameters parameters = new JpaParameters(method);
|
||||
|
||||
JpaParameter parameter = parameters.getBindableParameter(0);
|
||||
assertThat(parameter.isSpecialParameter(), is(false));
|
||||
assertThat(parameter.isTemporalParameter(), is(true));
|
||||
assertThat(parameter.getTemporalType(), is(TemporalType.TIMESTAMP));
|
||||
|
||||
parameter = parameters.getBindableParameter(1);
|
||||
assertThat(parameter.isTemporalParameter(), is(false));
|
||||
}
|
||||
|
||||
interface SampleRepository {
|
||||
|
||||
void foo(@Temporal(TIMESTAMP) Date date, String firstname);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryExecution.PagedExecution;
|
||||
import org.springframework.data.repository.query.DefaultParameters;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
|
||||
/**
|
||||
@@ -44,17 +45,12 @@ import org.springframework.data.repository.query.Parameters;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class JpaQueryExecutionUnitTests {
|
||||
|
||||
@Mock
|
||||
EntityManager em;
|
||||
@Mock
|
||||
AbstractStringBasedJpaQuery jpaQuery;
|
||||
@Mock
|
||||
Query query;
|
||||
@Mock
|
||||
JpaQueryMethod method;
|
||||
@Mock EntityManager em;
|
||||
@Mock AbstractStringBasedJpaQuery jpaQuery;
|
||||
@Mock Query query;
|
||||
@Mock JpaQueryMethod method;
|
||||
|
||||
@Mock
|
||||
TypedQuery<Long> countQuery;
|
||||
@Mock TypedQuery<Long> countQuery;
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullQuery() {
|
||||
@@ -121,7 +117,7 @@ public class JpaQueryExecutionUnitTests {
|
||||
@Test
|
||||
public void pagedExecutionDoesNotRetrieveObjectsForPageableOutOfRange() throws Exception {
|
||||
|
||||
Parameters parameters = new Parameters(getClass().getMethod("sampleMethod", Pageable.class));
|
||||
Parameters<?, ?> parameters = new DefaultParameters(getClass().getMethod("sampleMethod", Pageable.class));
|
||||
when(jpaQuery.createCountQuery(Mockito.any(Object[].class))).thenReturn(countQuery);
|
||||
when(jpaQuery.createQuery(Mockito.any(Object[].class))).thenReturn(query);
|
||||
when(countQuery.getResultList()).thenReturn(Arrays.asList(20L));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008-2011 the original author or authors.
|
||||
* Copyright 2008-2013 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.
|
||||
@@ -15,15 +15,18 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import static javax.persistence.TemporalType.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -32,21 +35,21 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.repository.Temporal;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
|
||||
/**
|
||||
* Unit test for {@link ParameterBinder}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ParameterBinderUnitTests {
|
||||
|
||||
private Method valid;
|
||||
|
||||
@Mock
|
||||
private Query query;
|
||||
@Mock private Query query;
|
||||
private Method useIndexedParameters;
|
||||
private Method indexedParametersWithSort;
|
||||
|
||||
@@ -74,24 +77,31 @@ public class ParameterBinderUnitTests {
|
||||
User validWithPageable(@Param("username") String username, Pageable pageable);
|
||||
|
||||
User validWithSort(@Param("username") String username, Sort sort);
|
||||
|
||||
User validWithDefaultTemporalTypeParameter(@Temporal Date registerDate);
|
||||
|
||||
User validWithCustomTemporalTypeParameter(@Temporal(TIMESTAMP) Date registerDate);
|
||||
|
||||
User invalidWithTemporalTypeParameter(@Temporal String registerDate);
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsToManyParameters() throws Exception {
|
||||
|
||||
new ParameterBinder(new Parameters(valid), new Object[] { "foo", "bar" });
|
||||
new ParameterBinder(new JpaParameters(valid), new Object[] { "foo", "bar" });
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullParameters() throws Exception {
|
||||
|
||||
new ParameterBinder(new Parameters(valid), (Object[]) null);
|
||||
new ParameterBinder(new JpaParameters(valid), (Object[]) null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsToLittleParameters() throws SecurityException, NoSuchMethodException {
|
||||
|
||||
Parameters parameters = new Parameters(valid);
|
||||
JpaParameters parameters = new JpaParameters(valid);
|
||||
new ParameterBinder(parameters);
|
||||
}
|
||||
|
||||
@@ -100,7 +110,7 @@ public class ParameterBinderUnitTests {
|
||||
|
||||
Method method = SampleRepository.class.getMethod("validWithPageable", String.class, Pageable.class);
|
||||
|
||||
Parameters parameters = new Parameters(method);
|
||||
JpaParameters parameters = new JpaParameters(method);
|
||||
ParameterBinder binder = new ParameterBinder(parameters, new Object[] { "foo", null });
|
||||
|
||||
assertThat(binder.getPageable(), is(nullValue()));
|
||||
@@ -111,7 +121,7 @@ public class ParameterBinderUnitTests {
|
||||
|
||||
Method validWithSort = SampleRepository.class.getMethod("validWithSort", String.class, Sort.class);
|
||||
|
||||
new ParameterBinder(new Parameters(validWithSort), new Object[] { "foo", null }).bind(query);
|
||||
new ParameterBinder(new JpaParameters(validWithSort), new Object[] { "foo", null }).bind(query);
|
||||
verify(query).setParameter(eq(1), eq("foo"));
|
||||
}
|
||||
|
||||
@@ -120,14 +130,14 @@ public class ParameterBinderUnitTests {
|
||||
|
||||
Method validWithPageable = SampleRepository.class.getMethod("validWithPageable", String.class, Pageable.class);
|
||||
|
||||
new ParameterBinder(new Parameters(validWithPageable), new Object[] { "foo", null }).bind(query);
|
||||
new ParameterBinder(new JpaParameters(validWithPageable), new Object[] { "foo", null }).bind(query);
|
||||
verify(query).setParameter(eq(1), eq("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesIndexedParametersIfNoParamAnnotationPresent() throws Exception {
|
||||
|
||||
new ParameterBinder(new Parameters(useIndexedParameters), new Object[] { "foo" }).bind(query);
|
||||
new ParameterBinder(new JpaParameters(useIndexedParameters), new Object[] { "foo" }).bind(query);
|
||||
verify(query).setParameter(eq(1), anyObject());
|
||||
}
|
||||
|
||||
@@ -135,7 +145,7 @@ public class ParameterBinderUnitTests {
|
||||
public void usesParameterNameIfAnnotated() throws Exception {
|
||||
|
||||
when(query.setParameter(eq("username"), anyObject())).thenReturn(query);
|
||||
new ParameterBinder(new Parameters(valid), new Object[] { "foo" }) {
|
||||
new ParameterBinder(new JpaParameters(valid), new Object[] { "foo" }) {
|
||||
|
||||
@Override
|
||||
boolean hasNamedParameter(Query query) {
|
||||
@@ -150,7 +160,7 @@ public class ParameterBinderUnitTests {
|
||||
public void bindsEmbeddableCorrectly() throws Exception {
|
||||
|
||||
Method method = getClass().getMethod("findByEmbeddable", SampleEmbeddable.class);
|
||||
Parameters parameters = new Parameters(method);
|
||||
JpaParameters parameters = new JpaParameters(method);
|
||||
SampleEmbeddable embeddable = new SampleEmbeddable();
|
||||
|
||||
new ParameterBinder(parameters, new Object[] { embeddable }).bind(query);
|
||||
@@ -162,11 +172,53 @@ public class ParameterBinderUnitTests {
|
||||
public void bindsSortForIndexedParameters() throws Exception {
|
||||
|
||||
Sort sort = new Sort("name");
|
||||
ParameterBinder binder = new ParameterBinder(new Parameters(indexedParametersWithSort),
|
||||
new Object[] { "name", sort });
|
||||
ParameterBinder binder = new ParameterBinder(new JpaParameters(indexedParametersWithSort), new Object[] { "name",
|
||||
sort });
|
||||
assertThat(binder.getSort(), is(sort));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-107
|
||||
*/
|
||||
@Test
|
||||
public void shouldSetTemporalQueryParameterToDate() throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod("validWithDefaultTemporalTypeParameter", Date.class);
|
||||
JpaParameters parameters = new JpaParameters(method);
|
||||
Date date = new Date();
|
||||
|
||||
new ParameterBinder(parameters, new Object[] { date }).bind(query);
|
||||
|
||||
verify(query).setParameter(eq(1), eq(date), eq(TemporalType.DATE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-107
|
||||
*/
|
||||
@Test
|
||||
public void shouldSetTemporalQueryParameterToTimestamp() throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod("validWithCustomTemporalTypeParameter", Date.class);
|
||||
JpaParameters parameters = new JpaParameters(method);
|
||||
Date date = new Date();
|
||||
|
||||
new ParameterBinder(parameters, new Object[] { date }).bind(query);
|
||||
|
||||
verify(query).setParameter(eq(1), eq(date), eq(TemporalType.TIMESTAMP));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-107
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowIllegalArgumentExceptionIfIsAnnotatedWithTemporalParamAndParameterTypeIsNotDate()
|
||||
throws Exception {
|
||||
Method method = SampleRepository.class.getMethod("invalidWithTemporalTypeParameter", String.class);
|
||||
|
||||
JpaParameters parameters = new JpaParameters(method);
|
||||
new ParameterBinder(parameters, new Object[] { "foo", "" });
|
||||
}
|
||||
|
||||
public SampleEntity findByEmbeddable(SampleEmbeddable embeddable) {
|
||||
|
||||
return null;
|
||||
|
||||
@@ -12,6 +12,7 @@ import javax.persistence.criteria.ParameterExpression;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.repository.query.DefaultParameters;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
@@ -27,8 +28,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
public class ParameterExpressionProviderTests {
|
||||
|
||||
@PersistenceContext
|
||||
EntityManager em;
|
||||
@PersistenceContext EntityManager em;
|
||||
|
||||
/**
|
||||
* @see DATADOC-99
|
||||
@@ -38,7 +38,7 @@ public class ParameterExpressionProviderTests {
|
||||
public void createsParameterExpressionWithMostConcreteType() throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod("findByIdGreaterThan", int.class);
|
||||
Parameters parameters = new Parameters(method);
|
||||
Parameters<?, ?> parameters = new DefaultParameters(method);
|
||||
ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, new Object[] { 1 });
|
||||
Part part = new Part("IdGreaterThan", User.class);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License
|
||||
import org.springframework.aop.framework.Advised;
|
||||
@@ -23,13 +23,21 @@ import static org.springframework.test.util.ReflectionTestUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Parameter;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.ejb.HibernateQuery;
|
||||
import org.hibernate.engine.TypedValue;
|
||||
import org.hibernate.type.TimestampType;
|
||||
import org.hibernate.type.Type;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
@@ -38,9 +46,11 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.repository.Temporal;
|
||||
import org.springframework.data.jpa.repository.support.PersistenceProvider;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -53,11 +63,9 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
public class PartTreeJpaQueryIntegrationTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
@Rule public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@PersistenceContext
|
||||
EntityManager entityManager;
|
||||
@PersistenceContext EntityManager entityManager;
|
||||
|
||||
/**
|
||||
* @see DATADOC-90
|
||||
@@ -111,6 +119,32 @@ public class PartTreeJpaQueryIntegrationTests {
|
||||
assertThat(hibernateQuery.getHibernateQuery().getQueryString(), endsWith("firstname is null"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @see https://jira.springsource.org/browse/DATAJPA-107
|
||||
*/
|
||||
@Test
|
||||
public void shouldSetTemporalQueryParameterToTimestamp() throws Exception {
|
||||
|
||||
Method method = UserRepository.class.getMethod("findByCreatedAtAfter", Date.class);
|
||||
JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
|
||||
PersistenceProvider.fromEntityManager(entityManager));
|
||||
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager);
|
||||
Date date = new Date();
|
||||
Query query = jpaQuery.createQuery(new Object[] { date });
|
||||
|
||||
HibernateQuery hibernateQuery = getValue(query, "h.target.val$jpaqlQuery");
|
||||
Parameter<?> parameter = hibernateQuery.getParameter("param0");
|
||||
Object parameterValue = hibernateQuery.getParameterValue(parameter);
|
||||
Map<?, ?> namedParameterType = getValue(hibernateQuery.getHibernateQuery(), "namedParameters");
|
||||
TypedValue refDateParam = (TypedValue) namedParameterType.get("param0");
|
||||
|
||||
assertThat(parameter, is(notNullValue()));
|
||||
assertThat(hibernateQuery.getHibernateQuery().getQueryString(), endsWith("createdAt>:param0"));
|
||||
assertThat(parameterValue, is((Object) date));
|
||||
assertThat(refDateParam.getType(), is((Type) TimestampType.INSTANCE));
|
||||
}
|
||||
|
||||
private void testIgnoreCase(String methodName, Object... values) throws Exception {
|
||||
|
||||
Class<?>[] parameterTypes = new Class[values.length];
|
||||
@@ -144,5 +178,7 @@ public class PartTreeJpaQueryIntegrationTests {
|
||||
User findByIdIgnoringCase(Integer id);
|
||||
|
||||
User findByIdAllIgnoringCase(Integer id);
|
||||
|
||||
List<User> findByCreatedAtAfter(@Temporal(TemporalType.TIMESTAMP) @Param("refDate") Date refDate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ import org.springframework.data.jpa.repository.sample.UserRepository;
|
||||
import org.springframework.data.jpa.repository.support.DefaultJpaEntityMetadata;
|
||||
import org.springframework.data.jpa.repository.support.JpaEntityMetadata;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
|
||||
/**
|
||||
* Unit test for {@link SimpleJpaQuery}.
|
||||
@@ -90,7 +89,7 @@ public class SimpleJpaQueryUnitTests {
|
||||
method = mock(JpaQueryMethod.class);
|
||||
when(method.getCountQuery()).thenReturn("foo");
|
||||
when(method.getParameters()).thenReturn(
|
||||
new Parameters(SimpleJpaQueryUnitTests.class.getMethod("prefersDeclaredCountQueryOverCreatingOne")));
|
||||
new JpaParameters(SimpleJpaQueryUnitTests.class.getMethod("prefersDeclaredCountQueryOverCreatingOne")));
|
||||
when(method.getEntityInformation()).thenReturn((JpaEntityMetadata) new DefaultJpaEntityMetadata<User>(User.class));
|
||||
when(em.createQuery("foo", Long.class)).thenReturn(query);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user