您的位置:首页 > 编程语言 > Java开发

SpEL support in Spring Data JPA @Query definitions

2017-07-12 09:37 483 查看
https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions

Spring Data JPA allows manually defining the query to be executed by a repository method using the 
@Query
 annotation.
Unfortunately parameter binding in JPQL is quite limited only allowing you to set a value and providing some type conversion. The latest Spring
Data JPA M1release of the Evans release train eases this pain by adding support

for using SpEL expressions to use dynamically bound parameters within statements in 
@Query
annotations
which provides additional flexibility when defining queries manually. In this blog post, I am going to introduce you to the capabilities of this feature.

Method
parameter expressions

SpEL support provides access to the query method arguments. This allows you to either simply bind the parameter as is or perform additional operations before binding.

@Query("select u from User u where u.age = ?#{[0]}")
List<User> findUsersByAge(int age);

@Query("select u from User u where u.firstname = :#{#customer.firstname}")
List<User> findUsersByCustomersFirstname(@Param("customer") Customer customer);

Parameters are exposed for indexed access (
[0]
 in
the first method) or via the name declared using 
@Param
.
The actual SpEL expression binding is either triggered by 
?#
 or 
:#
.
We support both types to allow you to be consistent to standard JPQL parameter bindings that also might occur in the query definition.

Parameters of special types like 
Sort
 and
`
Pageable
 are exposed with their
simple class names as variables.

Advanced
SpEL expressions

While advanced parameter binding is a very useful feature, the real power of SpEL stems from the fact, that the expressions can refer to framework abstractions or other application components. A very common scenario for SpEL is the definition of
security constraints. So it would be cool if we could restrict a query to only return results related to the currently authenticated user:

@Query("select u from User u where u.emailAddress = ?#{principal.emailAddress}")
List<User> findCurrentUserWithCustomQuery();

As you can see we refer to a property of Spring Security’s 
principal
.
So how does the Spring Data SpEL support integrate with Spring Security.

SpEL
EvaluationContext extension model

Spring Data exposes an extension point 
EvaluationContextExtension
.
The interface allows implementors to customize the 
EvaluationContext
 in
a very detailed way but for convenience, we provide a 
EvaluationContextExtensionSupport
 base
class to conveniently let you only implement the parts you’re interested in:

class SecurityEvaluationContextExtension extends EvaluationContextExtensionSupport {

@Override
public String getExtensionId() {
return "security";
}

@Override
public SecurityExpressionRoot getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return new SecurityExpressionRoot(authentication) {};
}
}

For our Spring Security extension we extend 
EvaluationContextExtensionSupport
 and
override the 
getRootObject()


method and return a new 
SecurityExpressionRoot
 instance
which exposes all the security properties and expressions you already know from usage in 
@PreAuthorize
.
This step also makes them available in SpEL expressions in our 
@Query
 annotation.

The final step we need to take is to register the security extension as a bean:

@Configuration
@EnableJpaRepositories
class SecurityConfiguration {

@Bean
EvaluationContextExtension securityExtension() {
return new SecurityEvaluationContextExtension();
}
}

Spring Data JPA will pick up all beans of type 
EvaluationContextExtension
 and
use those to prepare the 
EvaluationContext
 to
be used to evaluate the SpEL expression defined in 
@Query
.

The extension in place will now let you leverage the full power of the Spring Security SpEL functions. Imagine a repository query method that shall return the 
BusinessObject
s
which the current user is owner of or all 
BusinessObject
s
if the current user is admin. The query method definition would look like this:

interface SecureBusinessObjectRepository extends Repository<BusinessObject,Long>{

@Query("select o from BusinessObject o where o.owner.emailAddress like "+
"?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
List<BusinessObject> findBusinessObjectsForCurrentUser();
}

You can find the working examples of the snippets seen here in the Spring-Data-Examplesrepository.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: