SpEL support in Spring Data JPA @Query definitions

Spring Data JPA 最新版本通过引入 SpEL 表达式增强了 @Query 注解的功能,允许开发者使用动态参数绑定。这不仅提高了查询定义的灵活性,还能够集成 Spring Security 的权限控制,实现更复杂的查询逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 @Queryannotations 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 BusinessObjects which the current user is owner of or all BusinessObjects 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.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值