【问题标题】:Best way to create a query in Hibernate with programmatical conditions使用编程条件在 Hibernate 中创建查询的最佳方法
【发布时间】:2020-08-22 00:51:33
【问题描述】:

假设我有一个方法负责根据传递给它的参数构建查询:

    public Query<User> buildQuery(String name, String city) {
        Session session = HibernateUtil.getSessionFactory().openSession();

        String queryString = "from Users where 1 = 1 ";
        if (name != null) queryString += "and name = :name ";
        if (city != null) queryString += "and city = :city ";

        Query<User> query = session.createQuery(queryString, User.class);

        if (name != null) query.setParameter("name", name);
        if (city != null) query.setParameter("city", city);

        return query;
    }

我不喜欢这样,因为逻辑重复了两次(用于添加条件和设置参数)。

我想到的唯一解决方案是使用条件查询:

    public Query<User> buildQuery2(String name, String city) {
        Session session = HibernateUtil.getSessionFactory().openSession();

        CriteriaBuilder builder = session.getCriteriaBuilder();         
        CriteriaQuery<User> criteriaQuery = builder.createQuery(User.class);
        Root<User> root = criteriaQuery.from(User.class);
        Predicate predicate = builder.conjunction();
        if (name != null) {
            predicate = builder.and(predicate, builder.equal(root.get("name"), name));
        }
        if (city != null) {
            predicate = builder.and(predicate, builder.equal(root.get("city"), city));
        }
        criteriaQuery.select(root).where(predicate);            
        Query<User> query = session.createQuery(criteriaQuery);
        return query;
    }

有没有更好的解决方案?

【问题讨论】:

    标签: hibernate criteria named-parameters


    【解决方案1】:

    JPA Criteria API 方法是一种方法,但添加新过滤器将需要更改您的业务代码,这是不太理想的。此外,在某些时候,将所有可能的过滤器值作为方法的参数列出是非常痛苦的。

    我建议您看看Blaze-Persistence Entity-Views 提供的服务。

    我创建了该库以允许在 JPA 模型和自定义接口定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您以您喜欢的方式定义您的目标结构,并通过 JPQL 表达式将属性(getter)映射到实体模型。由于属性名称用作默认映射,因此您通常不需要显式映射,因为 80% 的用例是拥有作为实体模型子集的 DTO。最好的一点是,您可以为您的投影定义过滤器。

    您的模型的映射可能如下所示简单

    @EntityView(User.class)
    interface UserView {
      @IdMapping
      Long getId();
      @AttributeFilter(EqualFilter.class)
      String getName();
      @AttributeFilter(EqualFilter.class)
      String getCity();
    }
    

    在您的表示层中,您构建一个 EntityViewSetting 对象,您可以将其传递给您的服务层。

    EntityViewSetting<UserView, CriteriaBuilder<UserView>> setting = EntityViewSetting.create(UserView.class);
    if (nameFilter != null) setting.addAttributeFilter("name", nameFilter);
    if (cityFilter != null) setting.addAttributeFilter("city", cityFilter);
    

    你的服务可以是纯粹的,只是关于业务逻辑,像这样

    <T> T findAll(EntityViewSetting<T, CriteriaBuilder<T>> setting) {
        CriteriaBuilder<User> cb = criteriaBuilderFactory.create(entityManager, User.class);
        // Your business logic
        return entityViewManager.applySetting(setting, cb);
    }
    

    【讨论】:

    • 感谢您的回答,我肯定会研究 Blaze,但目前我必须坚持使用纯 Hibernate。你能扩展一下这个话题吗?
    • 那么您对 ​​JPA Criteria API 的使用就可以了。你有什么特别不工作的地方吗?
    • 我的解决方案完美运行,我只是徘徊,如果其他方法可能更好。谢谢。
    猜你喜欢
    • 2016-08-29
    • 1970-01-01
    • 2014-04-15
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-07
    相关资源
    最近更新 更多