领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多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 参数绑定保持一致。特殊类型(如Sort
和Pageable
)的参数以其简单类名作为变量公开。
虽然高级参数绑定是一个非常有用的功能,但 SpEL 的真正强大之处在于表达式可以引用框架抽象或其他应用程序组件。SpEL 的一个非常常见的场景是安全约束的定义。因此,如果我们可以将查询限制为仅返回与当前已认证用户相关的结果,那将是很棒的。
@Query("select u from User u where u.emailAddress = ?#{principal.emailAddress}")
List<User> findCurrentUserWithCustomQuery();
如您所见,我们引用了 Spring Security 的principal
的属性。那么 Spring Data SpEL 支持如何与 Spring Security 集成呢?
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 函数的全部功能。想象一个存储库查询方法,该方法应返回当前用户拥有者或如果当前用户是管理员则返回所有BusinessObject
的BusinessObject
。查询方法定义如下所示。
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存储库中找到此处显示的代码片段的工作示例。
通常,新功能能够以以前认为不可能的方式执行操作 - 本地查询中的分页就是一个例子。由于此机制也公开了像Sort
或Pageable
这样的特殊参数类型,因此我们现在能够在本地查询中使用分页。此处可以找到一个示例UserRepository。
目前,我们正在研究 Spring Security 与 Spring Data 的更紧密集成。我们还在努力为其他 Spring Data 模块添加对 SpEL 功能的支持。
但现在轮到你了 - 请在下面的评论中告诉我们您的想法,或在我们的JIRA中提交功能请求。
SpringOne 2GX 2014 即将到来。
如果您想了解有关 Spring Data 的更多信息,请务必注册参加今年的 SpringOne 大会。该日程安排包含许多与数据相关的演讲,以向您介绍我们将随 Evans 一起发布的最新功能。