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:
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.domain.sample;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sample domain class representing roles. Mapped with XML.
|
||||
*
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package org.springframework.data.jpa.domain.sample;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Entity
|
||||
public class SpecialUser extends User {
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user