Java 拦截Spring数据中的存储库方法调用,动态优化查询

Java 拦截Spring数据中的存储库方法调用,动态优化查询,java,spring,spring-data,query-by-example,Java,Spring,Spring Data,Query By Example,假设我有几个接口扩展了crudepositor。其中有一些方法,如findByField。其中一些方法应该只返回属于用户有权访问的一组实体的实体(组是数据库中的一列,因此它是为大多数实体定义的字段)。我希望通过允许在存储库方法上使用注释(如@Protected)来实现这一点,然后在调用这些方法而不是调用findByField时,在幕后调用方法findByFieldAndGroup。通过使用AOP(截取用@Protected标记注释的方法),可以在有效执行方法之前分配组 public interf

假设我有几个接口扩展了
crudepositor
。其中有一些方法,如
findByField
。其中一些方法应该只返回属于用户有权访问的一组实体的实体(组是数据库中的一列,因此它是为大多数实体定义的字段)。我希望通过允许在存储库方法上使用注释(如@Protected)来实现这一点,然后在调用这些方法而不是调用
findByField
时,在幕后调用方法
findByFieldAndGroup
。通过使用AOP(截取用@Protected标记注释的方法),可以在有效执行方法之前分配组

public interface MyRepository extends CRUDRepository<MyEntity,long> {

    @Protected
    Optional<MyEntity> findById(Long id); // Should become findByIdAndGroup(Long id, String group) behind the scenes

    @Protected
    Collection<MyEntity> findAll();

}
公共接口MyRepository扩展了Crudepository{
@保护
可选的findById(Long id);//应该在幕后成为findByIdAndGroup(Long id,String group)
@保护
集合findAll();
}
有没有办法做到这一点?在最坏的情况下,我要么手动添加所有方法,要么完全切换到按示例查询的方法(您可以更轻松地动态添加组),要么使用ASM(操作字节码)使用Java代理生成方法。。。但这些都是需要大量重构的不太实用的方法

编辑:找到这些相关问题 其他相关参考包括(无进展,只有一种使用QueryDSL的解决方案,它排除了基于方法名称的查询)和。

您可以使用特定的Hibernate功能来解决此问题

想法如下

首先,您需要使用要应用的不同过滤器对实体进行注释,例如:

@实体
//...
@过滤器({
@过滤器(name=“filterByGroup”,condition=“group\u id=:group\u id”)
})
公共类MyEntity实现可序列化{
// ...
}
然后,您需要访问底层的
EntityManager
,因为您需要与相关的Hibernate
会话进行交互。你有几种方法可以做到这一点。例如,可以为任务定义自定义事务管理器,如:

公共类筛选器WAREJPATransactionManager扩展了JpaTransactionManager{
@凌驾
受保护的EntityManager CreateEntityManager端口传输(){
final EntityManager EntityManager=super.createEntityManagerForTransaction();
//获取对基础会话对象的访问权限
最终会话Session=entityManager.unwrap(Session.class);
//启用过滤器
试一试{
此.enableFilterByGroup(会话);
}捕获(可丢弃的t){
//按您认为适当的方式处理异常
t、 printStackTrace();
}
返回实体管理器;
}
private void enableFilterByGroup(最终会话){
最后一个字符串组=this.getGroup();
如果(组==null){
/考虑记录问题
返回;
}
一场
.enableFilter(“filterByGroup”)
.setParameter(“组id”,组)
;
}
私有字符串getGroup(){
//您需要访问用户信息。例如,在Spring Security的情况下,您可以尝试:
最终身份验证=SecurityContextHolder.getContext().getAuthentication();
if(身份验证==null){
返回null;
}
//您的用户类型
MyUser=(MyUser)身份验证。getPrincipal();
String group=user.getGroup();
返回组;
}
}
然后,在数据库配置中注册此
transactionmanager
,而不是默认的
JpaTransactionManager

@Bean
公共平台transactionManager transactionManager(){
JpaTransactionManager transactionManager=新的FilterAwareJpaTransactionManager();
setEntityManagerFactory(entityManagerFactory());
返回事务管理器;
}

您还可以通过创建自定义的
JpaRepository
或在bean中注入
@PersistenceContext
来访问
EntityManager
和相关的
会话
,但我认为上述方法是更简单的方法,尽管它有总是被应用的缺点。

Bruno,您是否正在使用Hibernate under-the-hood作为持久性提供程序?是的,就是这样。我会尝试一下,如果它正常工作,我会接受它作为最佳答案,非常感谢您的回答。欢迎@BrunoVandekerkhove。我希望它能正常工作,你能解决你的问题。