在Hibernate中用程序化条件创建查询的最佳方法。

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

比方说,我有一个方法负责根据传递给它的参数建立一个查询。

    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个回答
1
投票

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

我建议你看看 Blaze-持久性实体-视图 所提供的。

我创建这个库的目的是为了让JPA模型和自定义接口定义的模型之间能够轻松地进行映射,就像Spring Data Projections一样。这个想法是,你以你喜欢的方式定义你的目标结构,并通过JPQL表达式将属性(getters)映射到实体模型。由于属性名被用作默认映射,你大多不需要显式映射,因为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);
}
© www.soinside.com 2019 - 2024. All rights reserved.