Hibernate @Filter 不适用于 Spring JpaRepository.findById 方法

问题描述 投票:0回答:3

为了创建行级授权,我想结合使用

@Filter
@FilterDef
hibernate 注解以及
JpaRepository<T, ID>
spring-data 接口。假设,我们有以下实体:

@Entity
public class User {
   @Id
   private Long id;
   private String name;
    
   @ManyToOne
   private Pharmacy pharmacy;
}
    
@Entity
public class Pharmacy {
   @Id
   private Long id;
   private String name;
}

我想根据谁向服务器发送请求来创建授权。为此,我在

@Filter
实体顶部添加了
@FilterDef
Pharmacy
注释。所以,药房应该是这样的:

@Entity
@FilterDef(name = "pharmacyFilter", parameters = {@ParamDef(name = "userId", type = "long")})
@Filters({
   @Filter(name = "pharmacyFilter", condition = "id in (select user.pharmacy_id from user where user.id = :userId)")
})
public class Pharmacy {
   //...
}

我创建的用于访问数据库的存储库如下所示:

@Repository
public interface PharmacyRepository extends JpaRepository<Pharmacy, Long> {
    
}

当我启用

pharmacyFilter
时,一切正常,并且过滤器应用于所有查询。您可以看到为
repository.findAll()
生成的查询,如下所示:

select pharmacy0_.id as id1_0_, pharmacy0_.name as name2_0_ from pharmacy pharmacy0_ where pharmacy0_.id in (select user.pharmacy_id from user where user.id = ?)

但是,当我想尝试使用

repository.findById(ID id)
时,问题出现了。当我使用上述方法时,过滤器不会应用于最终查询,我们将在终端中看到以下sql:

select pharmacy0_.id as id1_0_0_, pharmacy0_.name as name2_0_0_ from pharmacy pharmacy0_ where pharmacy0_.id=?

我猜问题是由于多次使用 id 造成的。一个处于

findById
状态,另一个处于过滤状态。但是当我尝试使用
session
对象创建查询时,这个问题没有发生并且输出是理想的:

select pharmacy0_.id as id1_0_, pharmacy0_.name as name2_0_ from pharmacy pharmacy0_ where pharmacy0_.id in (select user.pharmacy_id from user where user.id = ?) and pharmacy0_.id=2

使用以下方法解决了问题,但是当我们使用 JpaRepository#findById 默认实现时会发生什么?

@Query(value = "from Pharmacy where id = :id")
Optional<Pharmacy> findById(Long id);

提前致谢。

java hibernate spring-data
3个回答
8
投票

正如 hibernate 文档中所述:

过滤器适用于实体查询,但不适用于直接获取。

但在幕后

repository.findById(ID id)
方法调用
EntityManager.find
。 所以,这是预期的行为。


0
投票

您好,尝试这将绕过默认的 springboot findById 应用任何过滤器


@Repository
public interface PharmacyRepository extends JpaRepository<Pharmacy, Long> {
    

   @Override
    default Optional<Pharmacy> findById(Long id) {
        return findOne(Example.of(Pharmacy.builder().id(id).build()));
        // or with predicate
    }
}

0
投票

正如其他人提到的,有一些解决方案,

使用@Query

@Query(value = "from Pharmacy where id = :id")
Optional<Pharmacy> findById(Long id);

实施起来可能花费最少的一个是

Optional<Pharmacy> findOneById(Long pharmacuId);

由于 findOneById 不是 Jpa 的一部分,因此它不会调用 EntityManager.find 并且正确应用了过滤器。

© www.soinside.com 2019 - 2024. All rights reserved.