如何使用 Hibernate 为 Spring data JPA 的所有查找方法添加全局 where 子句?

Posted

技术标签:

【中文标题】如何使用 Hibernate 为 Spring data JPA 的所有查找方法添加全局 where 子句?【英文标题】:How to add global where clause for all find methods of Spring data JPA with Hibernate? 【发布时间】:2018-02-07 17:24:55 【问题描述】:

我们正在开发使用带有休眠功能的 Spring data JPA 的 Web 应用程序。

在应用程序中,每个实体都有一个compid字段。 这意味着在每个 DB 调用(Spring Data 方法)中都必须使用 compid 检查。

我需要一种方法,即“where compid = ?”检查是否为每个查找方法自动注入。 这样我们就不必专门为 compid 检查而烦恼。

这可以通过 Spring Data JPA 框架实现吗?

【问题讨论】:

【参考方案1】:

也许Hibernate的注解@Where会对你有所帮助。它将传递的条件添加到与实体相关的任何 JPA 查询中。例如

@Entity
@Where(clause = "isDeleted='false'")
public class Customer 
    //...
    @Column
    private Boolean isDeleted;

更多信息:1、2

【讨论】:

感谢您的回复,但在@Where 注释中,我必须在每个实体类上添加它。我想避免这种机制。有可能这样做吗?哪里 repo.findAll(); 您有所有此类实体的基超类吗?您可以将字段 compid@Where 注释移到那里... “通用查询增强”存在一个未解决的问题,这是您所追求的功能类型,但进展不大jira.spring.io/browse/DATACMNS-293。我认为添加到超类的 @Where 注释是最好的选择 - 因为它是特定于 Hibernate 的注释,它不适用于 Spring Data 存储库,因此它需要在实体上运行。 @Zeeshan 不要忘记接受/支持答案))【参考方案2】:

就像其他人说的那样,没有设置方法

一种选择是通过示例查看查询 - 来自 spring 数据文档 -

Person person = new Person();
person.setFirstname("Dave");
Example<Person> example = Example.of(person); 

因此您可以在对象或父 JPA 对象中默认为 compid

另一个选项是自定义存储库

【讨论】:

【参考方案3】:

同意 Abhijit Sarkar。

您可以通过休眠侦听器和方面来实现您的目标。我可以提出以下建议: 创建一个注解@Compable(或任何你称之为的)来标记服务方法 创建应该是 bean 和 @Aspect 的 CompAspect。它应该有这样的东西

@Around("@annotation(compable)")`
    public Object enableClientFilter(ProceedingJoinPoint pjp, Compable compable) throws Throwable 
        Session session = (Session) em.getDelegate();
        try 
            if (session.isOpen()) 
                session.enableFilter("compid_filter_name")
                        .setParameter("comp_id", your_comp_id);
            
            return pjp.proceed();
         finally 
            if (session.isOpen()) 
                session.disableFilter("filter_name");
            
        
    

em  - EntityManager

3)您还需要提供休眠过滤器。如果你使用注解,它可能看起来像这样:

@FilterDef(name="compid_filter_name", parameters=@ParamDef(name="comp_id", type="java.util.Long"))
@Filters(@Filter(name="compid_filter_name", condition="comp_id=:comp_id"))

所以你的条件where compid = ? 将是下面的@Service 方法

   @Compable
    someServicweMethod()
     List<YourEntity> l = someRepository.findAllWithNamesLike("test");
     

Selects 基本上就是这样, 对于更新/删除,此方案需要 EntityListener

【讨论】:

【参考方案4】:

我可以贡献 50% 的解决方案。 50% 因为似乎不容易包装Query Methods。自定义 JPA 查询也是这种全局方法的一个问题。如果标准查找器足够,则可以扩展自己的SimpleJpaRepository

public class CustomJpaRepositoryIml<T, ID extends Serializable> extends
    SimpleJpaRepository<T, ID> 

private JpaEntityInformation<T, ?> entityInformation;

@Autowired
public CustomJpaRepositoryIml(JpaEntityInformation<T, ?> entityInformation,
                              EntityManager entityManager) 
    super(entityInformation, entityManager);
    this.entityInformation = entityInformation;




private Sort applyDefaultOrder(Sort sort) 
    if (sort == null) 
        return null;
    
    if (sort.isUnsorted()) 
        return Sort.by("insert whatever is a default").ascending();
    
    return sort;


private Pageable applyDefaultOrder(Pageable pageable) 
    if (pageable.getSort().isUnsorted()) 
        Sort defaultSort = Sort.by("insert whatever is a default").ascending();
        pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), defaultSort);
    
    return pageable;



@Override
public Optional<T> findById(ID id) 
    Specification<T> filterSpec = filterOperatorUserAccess();
    if (filterSpec == null) 
        return super.findById(id);
    
    return findOne(filterSpec.and((Specification<T>) (root, query, criteriaBuilder) -> 
        Path<?> path = root.get(entityInformation.getIdAttribute());
        return criteriaBuilder.equal(path, id);
    ));


@Override
protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort) 
    sort = applyDefaultOrder(sort);
    Specification<T> filterSpec = filterOperatorUserAccess();
    if (filterSpec != null) 
        spec = (Specification<S>) filterSpec.and((Specification<T>) spec);
    
    return super.getQuery(spec, domainClass, sort);



这个实现被拾取,例如通过将其添加到 Spring Boot:

@SpringBootApplication
@EnableJpaRepositories(repositoryBaseClass = CustomJpaRepositoryIml.class)
public class ServerStart 
...

如果您也需要对 Querydsl 进行这种过滤,也可以实现并注册 QuerydslPredicateExecutor

【讨论】:

以上是关于如何使用 Hibernate 为 Spring data JPA 的所有查找方法添加全局 where 子句?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring 和 Hibernate 为 Web 应用程序和批处理作业设置事务

如何使用 Hibernate 为 Spring data JPA 的所有查找方法添加全局 where 子句?

使用 spring 和 hibernate 时,如何处理会话/事务?

如何使用 JPQL、Spring Data Repositories 和 Hibernate 为 TimescaleDB `time_bucket` 函数参数化 Postgresql 间隔

Spring with Hibernate-如何动态传递模式名称

Spring Hibernate 关系映射