DATAJPA-564 - Support for SpEL based parameter expressions in String based queries.

We now support the discovery and dynamic registration of SpEL expression parameters in String based queries. Introduced an ExpressionEvaluationContextProvider that provides access to
a potentially shared SpEL EvaluationContext that is defined in the application context. This allow shared spring beans to be used within query expressions.
The SpEL expressions are evaluated in org.springframework.data.jpa.repository.query.SpelExpressionStringQueryParameterBinder.potentiallyBindSyntheticParameters(T) by using a hierarchal EvaluationContext with the RootObject set to the current method arguments.
We enhanced the parsing of ParameterBindings in ParameterBindingParser to support "synthetic" Parameters like SpEL expressions that should be evaluated at query time.

This feature works with Hibernate, EclipseLink as well as OpenJPA.

We currently support those variants:
Indexed parameter:
@Query("select c from Customer c where c.firstname = ?1 and c.attribute1 like ?#{[0] + ' ' + [1]})
To determine the index for the expression parameter we determine the max parameter index present and use that as an offset to generate appropriate parameter indices.

Named parameter:
@Query("select c from Customer c where c.firstname = :firstname and c.attribute1 like :#{[0] + ' ' + [1]})
whereby we generate a name like __$synthetic$__0 for SpEL parameter expression.
This commit is contained in:
Thomas Darimont
2014-06-25 01:49:18 +02:00
committed by Oliver Gierke
parent 6260e8209c
commit c4f245b11e
18 changed files with 568 additions and 67 deletions

View File

@@ -1588,6 +1588,32 @@ public class UserRepositoryTests {
assertThat(result.isPresent(), is(true));
assertThat(result.get(), is(firstUser));
}
/**
* @see DATAJPA-XXX
*/
@Test
public void shouldFindUserByFirstnameAndLastnameWithSpelExpressionInStringBasedQuery() {
flushTestUsers();
List<User> users = repository.findByFirstnameAndLastnameWithSpelExpression("Oliver", "ierk");
assertThat(users, hasSize(1));
assertThat(users.get(0), is(firstUser));
}
/**
* @see DATAJPA-XXX
*/
@Test
public void shouldFindUserByLastnameWithSpelExpressionInStringBasedQuery() {
flushTestUsers();
List<User> users = repository.findByLastnameWithSpelExpression("ierk");
assertThat(users, hasSize(1));
assertThat(users.get(0), is(firstUser));
}
private Page<User> executeSpecWithSort(Sort sort) {

View File

@@ -36,6 +36,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.support.StandardExpressionEvaluationContextProvider;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
@@ -72,7 +73,8 @@ public class JpaQueryLookupStrategyUnitTests {
@Test
public void invalidAnnotatedQueryCausesException() throws Exception {
QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, Key.CREATE_IF_NOT_FOUND, extractor);
QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, Key.CREATE_IF_NOT_FOUND, extractor,
StandardExpressionEvaluationContextProvider.INSTANCE);
Method method = UserRepository.class.getMethod("findByFoo", String.class);
RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class);
@@ -93,7 +95,8 @@ public class JpaQueryLookupStrategyUnitTests {
@Test
public void sholdThrowMorePreciseExceptionIfTryingToUsePaginationInNativeQueries() throws Exception {
QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, Key.CREATE_IF_NOT_FOUND, extractor);
QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, Key.CREATE_IF_NOT_FOUND, extractor,
StandardExpressionEvaluationContextProvider.INSTANCE);
Method method = UserRepository.class.getMethod("findByInvalidNativeQuery", String.class, Pageable.class);
RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class);

View File

@@ -44,6 +44,7 @@ import org.springframework.data.jpa.repository.Query;
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.jpa.repository.support.StandardExpressionEvaluationContextProvider;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.RepositoryQuery;
@@ -95,7 +96,8 @@ public class SimpleJpaQueryUnitTests {
when(method.getEntityInformation()).thenReturn((JpaEntityMetadata) new DefaultJpaEntityMetadata<User>(User.class));
when(em.createQuery("foo", Long.class)).thenReturn(query);
SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u");
SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u",
StandardExpressionEvaluationContextProvider.INSTANCE);
assertThat(jpaQuery.createCountQuery(new Object[] {}), is(query));
}
@@ -111,7 +113,8 @@ public class SimpleJpaQueryUnitTests {
Method method = UserRepository.class.getMethod("findAllPaged", Pageable.class);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, extractor);
AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u");
AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u",
StandardExpressionEvaluationContextProvider.INSTANCE);
jpaQuery.createCountQuery(new Object[] { new PageRequest(1, 10) });
verify(query, times(0)).setFirstResult(anyInt());
@@ -124,7 +127,8 @@ public class SimpleJpaQueryUnitTests {
Method method = SampleRepository.class.getMethod("findNativeByLastname", String.class);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, extractor);
AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromQueryAnnotation(queryMethod, em);
AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromQueryAnnotation(queryMethod, em,
StandardExpressionEvaluationContextProvider.INSTANCE);
assertThat(jpaQuery instanceof NativeJpaQuery, is(true));
@@ -205,7 +209,8 @@ public class SimpleJpaQueryUnitTests {
private RepositoryQuery createJpaQuery(Method method) {
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, extractor);
return JpaQueryFactory.INSTANCE.fromQueryAnnotation(queryMethod, em);
return JpaQueryFactory.INSTANCE.fromQueryAnnotation(queryMethod, em,
StandardExpressionEvaluationContextProvider.INSTANCE);
}
interface SampleRepository {

View File

@@ -447,7 +447,7 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
* @see DATAJPA-551
*/
Slice<User> findTop2UsersBy(Pageable page);
/**
* @see DATAJPA-506
*/
@@ -459,4 +459,16 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
*/
@Query("select u from User u where u.emailAddress = ?1")
Optional<User> findOptionalByEmailAddress(String emailAddress);
/**
* @see DATAJPA-XXX
*/
@Query("select u from User u where u.firstname = ?#{[0]} and u.firstname = ?1 and u.lastname like %?#{[1]}% and u.lastname like %?2%")
List<User> findByFirstnameAndLastnameWithSpelExpression(String firstname, String lastname);
/**
* @see DATAJPA-XXX
*/
@Query("select u from User u where u.lastname like %:#{[0]}% and u.lastname like %:lastname%")
List<User> findByLastnameWithSpelExpression(@Param("lastname") String lastname);
}