DATAJPA-64 - Refactored query execution to use ParameterExpression.

The JpaQueryCreator now creates a CriteriaQuery using ParameterExpressions that have to be bound later on. Refactored the RepositoryQuery implementation hierarchy and JpaQueryExecution as binding has to be done by the query classes now. This required the introduction of a special CriteraQueryParameterBinder as well. It uses the ParameterExpressions of the CriteriaQuery to bind the actual query values later on.

We have to convert arrays passed into query method into collections as none of the major persistence providers support binding arrays to IN parameters currently.
This commit is contained in:
Oliver Gierke
2011-06-15 21:28:03 +02:00
parent 762e57ca2f
commit 405fccb04f
16 changed files with 514 additions and 307 deletions

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.jpa.domain.sample;
/**
* Sample domain class representing roles. Mapped with XML.
*

View File

@@ -1,8 +1,12 @@
package org.springframework.data.jpa.domain.sample;
import javax.persistence.Entity;
/**
* @author Oliver Gierke
*/
@Entity
public class SpecialUser extends User {
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2011 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;
import org.junit.Ignore;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
/**
* Ignores some test cases using IN queries as long as we wait for fix for
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477.
*
* @author Oliver Gierke
*/
@Ignore
@DirtiesContext
@ContextConfiguration(value = "classpath:eclipselink.xml", inheritLocations = true)
public class EclipseLinkUserRepositoryFinderTests extends
UserRepositoryFinderTests {
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2011 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;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Root;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:application-context.xml"
// , "classpath:eclipselink.xml"
// , "classpath:openjpa.xml"
})
@Transactional
public class SimpleJpaParameterBindingTests {
@PersistenceContext
EntityManager em;
@Test
@Ignore
public void bindArray() {
User user = new User("Dave", "Matthews", "foo@bar.de");
em.persist(user);
em.flush();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> criteria = builder.createQuery(User.class);
Root<User> root = criteria.from(User.class);
ParameterExpression<String[]> parameter =
builder.parameter(String[].class);
criteria.where(root.get("firstname").in(parameter));
TypedQuery<User> query = em.createQuery(criteria);
query.setParameter(parameter, new String[] { "Dave", "Carter" });
List<User> result = query.getResultList();
assertThat(result.isEmpty(), is(false));
}
@Test
@SuppressWarnings("rawtypes")
public void bindCollection() {
User user = new User("Dave", "Matthews", "foo@bar.de");
em.persist(user);
em.flush();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> criteria = builder.createQuery(User.class);
Root<User> root = criteria.from(User.class);
ParameterExpression<Collection> parameter =
builder.parameter(Collection.class);
criteria.where(root.get("firstname").in(parameter));
TypedQuery<User> query = em.createQuery(criteria);
query.setParameter(parameter, Arrays.asList("Dave"));
List<User> result = query.getResultList();
assertThat(result.isEmpty(), is(false));
assertThat(result.get(0), is(user));
}
}

View File

@@ -17,16 +17,15 @@ package org.springframework.data.jpa.repository.query;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution;
@@ -44,8 +43,6 @@ public class JpaQueryExecutionUnitTests {
@Mock
AbstractStringBasedJpaQuery jpaQuery;
@Mock
ParameterBinder binder;
@Mock
Query query;
@Mock
JpaQueryMethod method;
@@ -54,7 +51,7 @@ public class JpaQueryExecutionUnitTests {
@Test(expected = IllegalArgumentException.class)
public void rejectsNullQuery() {
new StubQueryExecution().execute(null, binder);
new StubQueryExecution().execute(null, new Object[] {});
}
@@ -71,29 +68,11 @@ public class JpaQueryExecutionUnitTests {
assertThat(new JpaQueryExecution() {
@Override
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) {
protected Object doExecute(AbstractJpaQuery query, Object[] values) {
return null;
}
}.execute(jpaQuery, binder), is(nullValue()));
}.execute(jpaQuery, new Object[] {}), is(nullValue()));
}
@@ -101,13 +80,13 @@ public class JpaQueryExecutionUnitTests {
@SuppressWarnings({ "unchecked", "rawtypes" })
public void modifyingExecutionClearsEntityManagerIfSet() {
Query param = any();
when(binder.bind(param)).thenReturn(query);
when(query.executeUpdate()).thenReturn(0);
when(method.getReturnType()).thenReturn((Class) void.class);
when(jpaQuery.createQuery(Mockito.any(Object[].class))).thenReturn(
query);
ModifyingExecution execution = new ModifyingExecution(method, em);
execution.execute(jpaQuery, binder);
execution.execute(jpaQuery, new Object[] {});
verify(em, times(1)).clear();
}
@@ -138,15 +117,7 @@ public class JpaQueryExecutionUnitTests {
static class StubQueryExecution extends JpaQueryExecution {
@Override
protected Object doExecute(AbstractStringBasedJpaQuery query,
ParameterBinder binder) {
return null;
}
@Override
protected Object doExecute(PartTreeJpaQuery query, Object[] parameters) {
protected Object doExecute(AbstractJpaQuery query, Object[] values) {
return null;
}

View File

@@ -34,6 +34,7 @@ import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.data.jpa.repository.sample.UserRepository;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.Parameters;
/**
@@ -72,8 +73,7 @@ public class SimpleJpaQueryUnitTests {
public void appliesHintsCorrectly() throws Exception {
SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "foobar");
jpaQuery.createQuery(new ParameterBinder(method.getParameters(),
new Object[] { "gierke" }));
jpaQuery.createQuery(new Object[] { "gierke" });
verify(query).setHint("foo", "bar");
}
@@ -84,11 +84,16 @@ public class SimpleJpaQueryUnitTests {
method = mock(JpaQueryMethod.class);
when(method.getCountQuery()).thenReturn("foo");
when(method.getParameters())
.thenReturn(
new Parameters(
SimpleJpaQueryUnitTests.class
.getMethod("prefersDeclaredCountQueryOverCreatingOne")));
when(em.createQuery("foo")).thenReturn(query);
SimpleJpaQuery jpaQuery =
new SimpleJpaQuery(method, em, "select u from User u");
assertThat(jpaQuery.createCountQuery(null), is(query));
assertThat(jpaQuery.createCountQuery(new Object[] {}), is(query));
}
}