Spring Data JPA 中 @Query 定义的 SpEL 支持

工程 | Thomas Darimont | 2014年7月15日 | ...

Spring Data JPA 允许使用@Query注解手动定义存储库方法要执行的查询。不幸的是,JPQL 中的参数绑定非常有限,只能允许您设置值并提供一些类型转换。Evans 发布列车的最新Spring Data JPA M1版本通过添加对使用 SpEL 表达式在@Query注解中的语句中使用动态绑定参数的支持来缓解了这个问题,这在手动定义查询时提供了额外的灵活性。在这篇博文中,我将向您介绍此功能的功能。

方法参数表达式

SpEL 支持提供对查询方法参数的访问。这允许您简单地绑定参数原样,或在绑定之前执行其他操作。

@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);

参数可用于索引访问(第一个方法中的[0])或通过使用@Param声明的名称访问。实际的 SpEL 表达式绑定由?#:#触发。我们支持这两种类型,以允许您与查询定义中也可能出现的标准 JPQL 参数绑定保持一致。特殊类型(如SortPageable)的参数以其简单类名作为变量公开。

高级 SpEL 表达式

虽然高级参数绑定是一个非常有用的功能,但 SpEL 的真正强大之处在于表达式可以引用框架抽象或其他应用程序组件。SpEL 的一个非常常见的场景是安全约束的定义。因此,如果我们可以将查询限制为仅返回与当前已认证用户相关的结果,那将是很棒的。

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

如您所见,我们引用了 Spring Security 的principal的属性。那么 Spring Data SpEL 支持如何与 Spring Security 集成呢?

SpEL EvaluationContext 扩展模型

Spring Data 公开了扩展点EvaluationContextExtension。该接口允许实现者以非常详细的方式自定义EvaluationContext,但为了方便起见,我们提供了一个EvaluationContextExtensionSupport基类,方便您只实现您感兴趣的部分。

class SecurityEvaluationContextExtension extends EvaluationContextExtensionSupport {

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

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

对于我们的 Spring Security 扩展,我们扩展了EvaluationContextExtensionSupport并覆盖了getRootObject()方法,并返回一个新的SecurityExpressionRoot实例,该实例公开了您在@PreAuthorize中使用的所有安全属性和表达式。此步骤还使它们在我们的@Query注解中的 SpEL 表达式中可用。

我们需要采取的最后一步是将安全扩展注册为一个 Bean。

@Configuration
@EnableJpaRepositories
class SecurityConfiguration {

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

Spring Data JPA 将获取所有类型为EvaluationContextExtension的 Bean,并使用它们来准备用于评估在@Query中定义的 SpEL 表达式的EvaluationContext

扩展到位后,现在可以让您利用 Spring Security SpEL 函数的全部功能。想象一个存储库查询方法,该方法应返回当前用户拥有者或如果当前用户是管理员则返回所有BusinessObjectBusinessObject。查询方法定义如下所示。

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();
}

您可以在Spring-Data-Examples存储库中找到此处显示的代码片段的工作示例。

复杂的用例

通常,新功能能够以以前认为不可能的方式执行操作 - 本地查询中的分页就是一个例子。由于此机制也公开了像SortPageable这样的特殊参数类型,因此我们现在能够在本地查询中使用分页。此处可以找到一个示例UserRepository

接下来是什么?

目前,我们正在研究 Spring Security 与 Spring Data 的更紧密集成。我们还在努力为其他 Spring Data 模块添加对 SpEL 功能的支持。

但现在轮到你了 - 请在下面的评论中告诉我们您的想法,或在我们的JIRA中提交功能请求。

SpringOne 2GX 2014

SpringOne 2GX 2014 即将到来。

如果您想了解有关 Spring Data 的更多信息,请务必注册参加今年的 SpringOne 大会。该日程安排包含许多与数据相关的演讲,以向您介绍我们将随 Evans 一起发布的最新功能。

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部