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

mm9b1k5b  于 2021-07-24  发布在  Java
关注(0)|答案(1)|浏览(305)

假设我有几个接口 CRUDRepositor . 里面有很多方法,比如 findByField . 其中一些方法应该只返回属于用户有权访问的一组实体的实体 group 是数据库中的一列,因此它是为大多数实体定义的字段)。我希望通过允许在存储库方法上使用注解(比如@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();

}

有办法做到这一点吗?在最坏的情况下,我要么手动添加所有方法,要么完全切换到“按示例查询”方法(在这种方法中,您可以更轻松地动态添加组),要么使用asm使用java代理生成方法(操作字节码)。。。但是这些方法不太实用,需要大量的重构。
编辑:找到了这些相关问题spring data jpa-在执行之前修改查询spring data jpa和spring security:在数据库级别进行筛选(特别是分页)其他相关参考包括github上的这张票据(没有进展,只有一种带有querydsl的解决方案,它排除了使用基于方法名的查询)和这个线程。

hvvq6cgz

hvvq6cgz1#

对于这个问题,您可以使用过滤器,一种特定的hibernate特性。
想法如下。
首先,您需要使用要应用的不同过滤器对实体进行注解,例如:

@Entity
//...
@Filters({
  @Filter(name="filterByGroup", condition="group_id = :group_id")
})
public class MyEntity implements Serializable { 
  // ...
}

然后,您需要访问底层 EntityManager 因为您需要与相关的hibernate进行交互 Session . 你有几种方法可以做到这一点。例如,可以为任务定义自定义事务管理器,例如:

public class FilterAwareJpaTransactionManager extends JpaTransactionManager {

  @Override
  protected EntityManager createEntityManagerForTransaction() {
    final EntityManager entityManager = super.createEntityManagerForTransaction();
    // Get access to the underlying Session object
    final Session session = entityManager.unwrap(Session.class);

    // Enable filter
    try{
      this.enableFilterByGroup(session);
    }catch (Throwable t){
      // Handle exception as you consider appropriate
      t.printStackTrace();
    }

    return entityManager;
  }

  private void enableFilterByGroup(final Session session){
    final String group = this.getGroup();

    if (group == null) {
      // Consider logging the problem
      return;
    }

    session
      .enableFilter("filterByGroup")
      .setParameter("group_id", group)
    ;
  }

  private String getGroup() {
    // You need access to the user information. For instance, in the case of Spring Security you can try:
    final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

    if (authentication == null) {
      return null;
    }

    // Your user type
    MyUser user = (MyUser)authentication.getPrincipal();
    String group = user.getGroup();
    return group;
  }
}

然后,注册这个 TransationManager 在数据库配置中,而不是默认的 JpaTransactionManager :

@Bean
public PlatformTransactionManager transactionManager() {
  JpaTransactionManager transactionManager = new FilterAwareJpaTransactionManager();
  transactionManager.setEntityManagerFactory(entityManagerFactory());
  return transactionManager;
}

您还可以访问 EntityManager 和相关的 Session 通过创建自定义 JpaRepository 或者通过注射 @PersistenceContext 但是我认为上面提到的方法是比较简单的方法,尽管它有一个缺点就是总是被应用。

相关问题