抢占先机
VMware 提供培训和认证,助您加速前进。
了解更多Spring Data JPA 允许使用 @Query
注解手动定义由 Repository 方法执行的查询。遗憾的是,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 函数的全部功能。想象一个 Repository 查询方法,它应返回当前用户拥有的 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 Repository 中找到此处所示代码片段的实际示例。
新特性常常能实现以前认为不可能的事情——例如在原生查询中进行分页。由于该机制也暴露了 Sort
或 Pageable
等特殊参数类型,我们现在可以在原生查询中使用分页。相关示例可以在这里找到 UserRepository。
目前我们正在研究将 Spring Security 更紧密地集成到 Spring Data 中。我们还在努力为其他 Spring Data 模块添加 SpEL 功能支持。
现在轮到您了——请在下方评论中告诉我们您的想法,或者在我们的 JIRA 中提交功能请求。
SpringOne 2GX 2014 临近了。
如果您想了解更多关于 Spring Data 的信息,请务必注册参加今年的 SpringOne 大会。日程表中包含许多与数据相关的讲座,将向您介绍 Evans 版本即将发布的新特性。