Migrated query-from-methodname algorithm to PartTree infrastructure.

Query creation is not String based anymore but rather leverages the PartTree infrastructure and builds a JPA criteria API query instead. Changed query lookup and execution accordingly.
This commit is contained in:
Oliver Gierke
2010-12-07 20:43:54 +01:00
parent 488e6979ab
commit 4d1188b751
28 changed files with 691 additions and 687 deletions

View File

@@ -25,7 +25,6 @@ import org.springframework.test.context.ContextConfiguration;
* @author Oliver Gierke
*/
@ContextConfiguration(value = "classpath:openjpa.xml", inheritLocations = true)
public class OpenJpaNamespaceUserDaoTests extends
NamespaceUserRepositoryTests {
public class OpenJpaNamespaceUserDaoTests extends NamespaceUserRepositoryTests {
}

View File

@@ -255,8 +255,9 @@ public class UserRepositoryTests {
flushTestUsers();
repository.renameAllUsersTo("newLastname");
assertEquals(repository.count().intValue(),
repository.findByLastname("newLastname").size());
Integer expected = repository.count().intValue();
assertThat(repository.findByLastname("newLastname").size(),
is(expected));
}
@@ -546,8 +547,10 @@ public class UserRepositoryTests {
firstUser = repository.save(firstUser);
secondUser = repository.save(secondUser);
assertTrue(repository.findByFirstnameOrLastname("Oliver", "Arrasz")
.containsAll(Arrays.asList(firstUser, secondUser)));
List<User> result =
repository.findByFirstnameOrLastname("Oliver", "Arrasz");
assertThat(result.size(), is(2));
assertThat(result, hasItems(firstUser, secondUser));
}

View File

@@ -25,6 +25,5 @@ import org.springframework.test.context.ContextConfiguration;
* @author Oliver Gierke
*/
@ContextConfiguration(locations = "classpath:config/namespace-autoconfig-context.xml")
public class RepositoryAutoConfigTests extends
AbstractRepositoryConfigTests {
public class RepositoryAutoConfigTests extends AbstractRepositoryConfigTests {
}

View File

@@ -28,8 +28,7 @@ import org.springframework.test.context.ContextConfiguration;
* @author Oliver Gierke
*/
@ContextConfiguration(locations = "classpath:config/namespace-autoconfig-typefilter-context.xml")
public class TypeFilterConfigTest extends
AbstractRepositoryConfigTests {
public class TypeFilterConfigTest extends AbstractRepositoryConfigTests {
/*
* (non-Javadoc)

View File

@@ -25,13 +25,12 @@ import org.springframework.data.repository.support.RepositorySupport;
/**
* Sample implementation of a custom {@link JpaRepositoryFactory} to use
* a custom repository base class.
* Sample implementation of a custom {@link JpaRepositoryFactory} to use a
* custom repository base class.
*
* @author Oliver Gierke
*/
public class CustomGenericJpaRepositoryFactory extends
JpaRepositoryFactory {
public class CustomGenericJpaRepositoryFactory extends JpaRepositoryFactory {
/**
* @param entityManager

View File

@@ -24,10 +24,10 @@ import org.springframework.data.repository.NoRepositoryBean;
/**
* Extension of {@link Repository} to be added on a custom repository
* base class. This tests the facility to implement custom base class
* functionality for all repository instances derived from this interface and
* implementation base class.
* Extension of {@link Repository} to be added on a custom repository base
* class. This tests the facility to implement custom base class functionality
* for all repository instances derived from this interface and implementation
* base class.
*
* @author Oliver Gierke
*/

View File

@@ -19,13 +19,13 @@ package org.springframework.data.jpa.repository.custom;
import org.springframework.data.jpa.domain.sample.User;
/**
* Custom Extended DAO interface for a {@code User}. This relies on the custom
* intermediate DAO interface {@link CustomGenericRepository}.
*
* @author Oliver Gierke
*/
public interface UserCustomExtendedRepository extends CustomGenericRepository<User, Integer> {
public interface UserCustomExtendedRepository extends
CustomGenericRepository<User, Integer> {
}

View File

@@ -45,7 +45,7 @@ public class JpaQueryExecutionUnitTests {
@Mock
EntityManager em;
@Mock
AbstractJpaQuery jpaQuery;
AbstractStringBasedJpaQuery jpaQuery;
@Mock
ParameterBinder binder;
@Mock
@@ -81,11 +81,28 @@ public class JpaQueryExecutionUnitTests {
assertThat(new JpaQueryExecution() {
@Override
protected Object doExecute(AbstractJpaQuery query,
protected Object doExecute(AbstractStringBasedJpaQuery query,
ParameterBinder binder) {
throw new NoResultException();
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.jpa.repository.query.JpaQueryExecution
* #doExecute
* (org.springframework.data.jpa.repository.query.PartTreeJpaQuery,
* java.lang.Object[])
*/
@Override
protected Object doExecute(PartTreeJpaQuery query,
Object[] parameters) {
return null;
}
}.execute(jpaQuery, binder), is(nullValue()));
}
@@ -123,11 +140,18 @@ public class JpaQueryExecutionUnitTests {
static class StubQueryExecution extends JpaQueryExecution {
@Override
protected Object doExecute(AbstractJpaQuery query,
protected Object doExecute(AbstractStringBasedJpaQuery query,
ParameterBinder binder) {
return null;
}
@Override
protected Object doExecute(PartTreeJpaQuery query, Object[] parameters) {
return null;
}
}
static interface Dummy {

View File

@@ -57,7 +57,7 @@ public class JpaQueryMethodUnitTests {
EntityManager em;
Method daoMethod, invalidReturnType, pageableAndSort, pageableTwice,
sortableTwice, modifyingMethod;
sortableTwice, modifyingMethod;
/**
@@ -67,24 +67,24 @@ public class JpaQueryMethodUnitTests {
public void setUp() throws Exception {
daoMethod =
UserRepository.class.getMethod("findByLastname", String.class);
UserRepository.class.getMethod("findByLastname", String.class);
invalidReturnType =
InvalidDao.class.getMethod(METHOD_NAME, String.class,
Pageable.class);
InvalidDao.class.getMethod(METHOD_NAME, String.class,
Pageable.class);
pageableAndSort =
InvalidDao.class.getMethod(METHOD_NAME, String.class,
Pageable.class, Sort.class);
InvalidDao.class.getMethod(METHOD_NAME, String.class,
Pageable.class, Sort.class);
pageableTwice =
InvalidDao.class.getMethod(METHOD_NAME, String.class,
Pageable.class, Pageable.class);
InvalidDao.class.getMethod(METHOD_NAME, String.class,
Pageable.class, Pageable.class);
sortableTwice =
InvalidDao.class.getMethod(METHOD_NAME, String.class,
Sort.class, Sort.class);
InvalidDao.class.getMethod(METHOD_NAME, String.class,
Sort.class, Sort.class);
modifyingMethod =
UserRepository.class
.getMethod("renameAllUsersTo", String.class);
UserRepository.class
.getMethod("renameAllUsersTo", String.class);
}
@@ -95,8 +95,6 @@ public class JpaQueryMethodUnitTests {
assertEquals("User.findByLastname", method.getNamedQueryName());
assertThat(method.getExecution(), is(CollectionExecution.class));
assertEquals("select x from User x where x.lastname = ?1",
new QueryCreator(method).constructQuery());
}
@@ -137,11 +135,11 @@ public class JpaQueryMethodUnitTests {
assertNull(method.getAnnotatedQuery());
Method daoMethod =
UserRepository.class
.getMethod("findByHadesQuery", String.class);
UserRepository.class
.getMethod("findByHadesQuery", String.class);
assertNotNull(new JpaQueryMethod(daoMethod, extractor, em)
.getAnnotatedQuery());
.getAnnotatedQuery());
}
@@ -192,11 +190,11 @@ public class JpaQueryMethodUnitTests {
@Test(expected = IllegalArgumentException.class)
public void rejectsPageablesOnPersistenceProvidersNotExtractingQueries()
throws Exception {
throws Exception {
Method method =
UserRepository.class.getMethod("findByFirstname",
Pageable.class, String.class);
UserRepository.class.getMethod("findByFirstname",
Pageable.class, String.class);
when(extractor.canExtractQuery()).thenReturn(false);
@@ -208,7 +206,7 @@ public class JpaQueryMethodUnitTests {
public void recognizesModifyingMethod() {
JpaQueryMethod method =
new JpaQueryMethod(modifyingMethod, extractor, em);
new JpaQueryMethod(modifyingMethod, extractor, em);
assertTrue(method.isModifyingQuery());
}
@@ -217,8 +215,8 @@ public class JpaQueryMethodUnitTests {
public void rejectsModifyingMethodWithPageable() throws Exception {
Method method =
InvalidDao.class.getMethod("updateMethod", String.class,
Pageable.class);
InvalidDao.class.getMethod("updateMethod", String.class,
Pageable.class);
new JpaQueryMethod(method, extractor, em);
}
@@ -228,8 +226,8 @@ public class JpaQueryMethodUnitTests {
public void rejectsModifyingMethodWithSort() throws Exception {
Method method =
InvalidDao.class.getMethod("updateMethod", String.class,
Sort.class);
InvalidDao.class.getMethod("updateMethod", String.class,
Sort.class);
new JpaQueryMethod(method, extractor, em);
}

View File

@@ -22,6 +22,7 @@ import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
import javax.persistence.Embeddable;
import javax.persistence.Query;
import org.junit.Before;
@@ -31,7 +32,6 @@ 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.query.QueryCreatorUnitTests.SampleEmbeddable;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.query.Parameters;
@@ -183,8 +183,8 @@ public class ParameterBinderUnitTests {
public void bindsEmbeddableCorrectly() throws Exception {
Method method =
QueryCreatorUnitTests.class.getMethod("findByEmbeddable",
SampleEmbeddable.class);
getClass()
.getMethod("findByEmbeddable", SampleEmbeddable.class);
Parameters parameters = new Parameters(method);
SampleEmbeddable embeddable = new SampleEmbeddable();
@@ -204,4 +204,24 @@ public class ParameterBinderUnitTests {
new Object[] { "name", sort });
assertThat(binder.getSort(), is(sort));
}
public SampleEntity findByEmbeddable(SampleEmbeddable embeddable) {
return null;
}
@SuppressWarnings("unused")
static class SampleEntity {
private SampleEmbeddable embeddable;
}
@Embeddable
@SuppressWarnings("unused")
static class SampleEmbeddable {
private String foo;
private String bar;
}
}

View File

@@ -1,327 +0,0 @@
/*
* Copyright 2008-2010 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 org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import java.util.Date;
import javax.persistence.Embeddable;
import javax.persistence.EntityManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.repository.query.JpaQueryMethodUnitTests.InvalidDao;
import org.springframework.data.repository.query.QueryCreationException;
/**
* Unit test for {@link QueryCreator}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class QueryCreatorUnitTests {
private Method method;
@Mock
QueryExtractor extractor;
@Mock
EntityManager em;
@Before
public void setup() throws SecurityException, NoSuchMethodException {
method =
QueryCreatorUnitTests.class.getMethod(
"findByFirstnameAndMethod", String.class);
}
@Test(expected = QueryCreationException.class)
public void rejectsInvalidProperty() throws Exception {
JpaQueryMethod finderMethod = new JpaQueryMethod(method, extractor, em);
new QueryCreator(finderMethod).constructQuery();
}
@Test
public void splitsKeywordsCorrectly() throws SecurityException,
NoSuchMethodException {
method =
QueryCreatorUnitTests.class.getMethod(
"findByNameOrOrganization", String.class, String.class);
assertCreatesQueryForMethod(
"where x.name = :name or x.organization = :organization",
method);
}
/**
* @throws NoSuchMethodException
* @throws SecurityException
* @see #265
* @throws Exception
*/
@Test
public void createsQueryWithEmbeddableCorrectly() throws SecurityException,
NoSuchMethodException {
method =
getClass()
.getMethod("findByEmbeddable", SampleEmbeddable.class);
assertCreatesQueryForMethod("where x.embeddable = :embeddable", method);
}
@Test
public void createsQueryWithBetweenKeywordCorrectly() throws Exception {
method =
getClass().getMethod("findByStartDateBetweenAndName",
Date.class, Date.class, String.class);
assertCreatesQueryForMethod(
"where x.startDate between :first and :second and x.name = :name",
method);
}
@Test
public void createsQueryWithLessThanKeywordCorrectly() throws Exception {
method = getClass().getMethod("findByAgeLessThan", int.class);
assertCreatesQueryForMethod("where x.age < :age", method);
}
@Test
public void createsQueryWithGreaterThanKeywordCorrectly() throws Exception {
method = getClass().getMethod("findByAgeGreaterThan", int.class);
assertCreatesQueryForMethod("where x.age > :age", method);
}
@Test(expected = IllegalArgumentException.class)
public void rejectsModifyingMethodWithoutBacking()
throws SecurityException, NoSuchMethodException {
Method invalidModifyingMethod =
InvalidDao.class.getMethod("updateMethod", String.class);
JpaQueryMethod method =
new JpaQueryMethod(invalidModifyingMethod, extractor, em);
new QueryCreator(method);
}
@Test
public void parsesLikeOperatorCorrectly() throws Exception {
method = getClass().getMethod("findByNameLike", String.class);
assertCreatesQueryForMethod("where x.name like :name", method);
}
@Test
public void parsesNotOperatorCorrectly() throws Exception {
method = getClass().getMethod("findByNameNot", String.class);
assertCreatesQueryForMethod("where x.name <> :name", method);
}
@Test
public void parsesNotNullOperatorCorrectly() throws Exception {
method = getClass().getMethod("findByNameNotNull");
assertCreatesQueryForMethod("where x.name is not null", method);
}
@Test
public void parsesLikeCorrectly() throws Exception {
method = getClass().getMethod("findByNameLike", String.class);
assertCreatesQueryForMethod("where x.name like :name", method);
}
@Test
public void parsesNotLikeCorrectly() throws Exception {
method = getClass().getMethod("findByNameNotLike", String.class);
assertCreatesQueryForMethod("where x.name not like :name", method);
}
@Test
public void parsesOrderByClauseCorrectly() throws Exception {
method =
getClass().getMethod("findByNameOrderByOrganizationDesc",
String.class);
assertCreatesQueryForMethod(
"where x.name = :name order by x.organization desc", method);
}
/**
* Asserts that the query created for the given {@link Method} results in a
* query ending with the given {@link String}.
*
* @param queryEnd
* @param method
*/
private void assertCreatesQueryForMethod(String queryEnd, Method method) {
JpaQueryMethod queryMethod = new JpaQueryMethod(method, extractor, em);
String result = new QueryCreator(queryMethod).constructQuery();
assertThat(result, endsWith(queryEnd));
}
/**
* Sample method to test failing query creation.
*
* @param firstname
* @return
*/
public User findByFirstnameAndMethod(String firstname) {
return null;
}
/**
* A method to check that query keyowrds are considered correctly. The
* {@link QueryCreator} must not detect the {@code Or} in
* {@code Organization} as keyword.
*
* @param name
* @param organization
* @return
*/
public SampleEntity findByNameOrOrganization(String name,
String organization) {
return null;
}
/**
* Sample method to create a finder query for that references an
* {@link Embeddable}.
*
* @see #265
* @param embeddable
* @return
*/
public SampleEntity findByEmbeddable(SampleEmbeddable embeddable) {
return null;
}
public SampleEntity findByStartDateBetweenAndName(Date first, Date second,
String name) {
return null;
}
public SampleEntity findByAgeLessThan(int age) {
return null;
}
public SampleEntity findByAgeGreaterThan(int age) {
return null;
}
public SampleEntity findByNameLike(String name) {
return null;
}
public SampleEntity findByNameNotLike(String name) {
return null;
}
public SampleEntity findByNameNot(String name) {
return null;
}
public SampleEntity findByNameNotNull() {
return null;
}
public SampleEntity findByNameOrderByOrganizationDesc(String name) {
return null;
}
/**
* Sample class for keyword split check.
*
* @author Oliver Gierke
*/
@SuppressWarnings("unused")
static class SampleEntity {
private String organization;
private String name;
private Date startDate;
private int age;
private SampleEmbeddable embeddable;
}
@Embeddable
@SuppressWarnings("unused")
static class SampleEmbeddable {
private String foo;
private String bar;
}
}

View File

@@ -69,7 +69,7 @@ public class SimpleJpaQueryUnitTests {
public void appliesHintsCorrectly() throws Exception {
SimpleJpaQuery hadesQuery = new SimpleJpaQuery(method, em, "foobar");
hadesQuery.createQuery(em, new ParameterBinder(method.getParameters(),
hadesQuery.createQuery(new ParameterBinder(method.getParameters(),
new Object[] { "gierke" }));
verify(query).setHint("foo", "bar");
@@ -86,6 +86,6 @@ public class SimpleJpaQueryUnitTests {
SimpleJpaQuery hadesQuery =
new SimpleJpaQuery(method, em, "select u from User u");
assertThat(hadesQuery.createCountQuery(em), is(query));
assertThat(hadesQuery.createCountQuery(null), is(query));
}
}

View File

@@ -72,8 +72,8 @@ public class JpaRepositoryFactoryBeanUnitTests {
// Setup standard factory configuration
factory =
JpaRepositoryFactoryBean.create(
SimpleSampleRepository.class, entityManager);
JpaRepositoryFactoryBean.create(SimpleSampleRepository.class,
entityManager);
factory.setEntityManager(entityManager);
}
@@ -139,8 +139,7 @@ public class JpaRepositoryFactoryBeanUnitTests {
JpaRepositoryFactoryBean<SampleRepository> factory =
JpaRepositoryFactoryBean.create(SampleRepository.class,
entityManager);
JpaRepositoryFactoryBean.create(SampleRepository.class, entityManager);
try {
factory.afterPropertiesSet();
@@ -166,8 +165,8 @@ public class JpaRepositoryFactoryBeanUnitTests {
void someSampleMethod();
}
private interface SampleRepository extends
JpaRepository<User, Integer>, SampleCustomDao {
private interface SampleRepository extends JpaRepository<User, Integer>,
SampleCustomDao {
}
}

View File

@@ -131,8 +131,7 @@ public class JpaRepositoryFactoryUnitTests {
dao.customMethod(1);
}
private interface SimpleSampleDao extends
JpaRepository<User, Integer> {
private interface SimpleSampleDao extends JpaRepository<User, Integer> {
@Transactional
User readByPrimaryKey(Integer primaryKey);