比方说,我有一个方法负责根据传递给它的参数建立一个查询。
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;
}
有没有更好的解决方案?
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);
}