如何将Hibernate Criteria API替换为JPA CriteriaBuilder?
从Hibernate Criteria迁移到JPA CriteriaBuilder:表达式添加逻辑改写
你需要升级Hibernate 3到5.3,将旧的Hibernate Criteria代码迁移到JPA CriteriaBuilder,核心问题是原代码中通过.add()添加表达式的逻辑在JPA CriteriaBuilder中没有直接对应方法,以下是具体的改写方案:
原代码与迁移痛点
原Hibernate 3核心逻辑
public List<ENTITY> findByCriteria(final Map<String, Object> criteriaMap, final List<String> fields, final Class<ENTITY> entityClass) { Session session = this.sessionFactory.getSessionFactory().getCurrentSession(); final Criteria criteria = session.createCriteria(entityClass); final Set<String> keys = criteriaMap.keySet(); Object object; CriteriaValue criteriaValue; CriteriaValue.Operator operator; for (String key:keys) { object = criteriaMap.get(key); if (object instanceof SimpleExpression) { criteria.add((SimpleExpression)object); } else if (object instanceof LogicalExpression) { criteria.add((LogicalExpression)object); } else if (object instanceof Criterion) { criteria.add((Criterion)object); } else if (!isDefaultCriteria(key)) { if (!(object instanceof CriteriaValue)) { if (object instanceof String && ((String)object).contains(SYSTEMWILDCARD)) { criteriaMap.put(key, new CriteriaValue(object, CriteriaValue.Operator.iLIKE)); } else { criteriaMap.put(key, new CriteriaValue(object, CriteriaValue.Operator.EQ)); } object = criteriaMap.get(key); } operator = ((CriteriaValue)object).getOperator(); criteriaValue = (CriteriaValue)object; if (criteriaValue != null) { Object value = criteriaValue.getValue(); switch (operator) { case EQ: criteria.add(value == null ? Restrictions.isNull(key) : Restrictions.eq(key, value)); break; case iLIKE: criteria.add(Restrictions.ilike(key, cleanWildcards(value))); break; case LIKE: criteria.add(Restrictions.like(key, cleanWildcards(value))); break; case LE: criteria.add(Restrictions.le(key, value)); break; case GE: criteria.add(Restrictions.ge(key, value)); break; case LT: criteria.add(Restrictions.lt(key, value)); break; case GT: criteria.add(Restrictions.gt(key, value)); break; case OR: criteria.add(getOrRestrictions(key, value)); break; case AND: criteria.add(getAndRestrictions(key, value)); break; case IN: criteria.add(Restrictions.in(key, (Object[])value)); break; case NE: criteria.add(value == null ? Restrictions.isNotNull(key): Restrictions.ne(key, value)); break; } } } } // ... 省略后续逻辑 }
你的初步迁移代码
CriteriaBuilder cb = sessionFactory.getCriteriaBuilder(); CriteriaQuery<?> cq = null; try { cq = cb.createQuery(Class.forName(entityClass.getName())); cq.from(Class.forName(entityClass.getName())); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } List<?> books = sessionFactory.getCurrentSession().createQuery(cq).getResultList();
核心改写方案
JPA CriteriaBuilder的核心差异是:没有直接的.add()方法,而是通过收集Predicate条件,最后统一通过CriteriaQuery.where()绑定,同时需要先获取实体的Root对象来定位属性。
1. 修正初始化逻辑
去掉不必要的反射,直接用泛型初始化CriteriaQuery和Root:
CriteriaBuilder cb = sessionFactory.getCriteriaBuilder(); CriteriaQuery<ENTITY> cq = cb.createQuery(entityClass); Root<ENTITY> root = cq.from(entityClass); List<Predicate> predicates = new ArrayList<>(); // 用于存储所有查询条件
2. 兼容旧的Criterion表达式
Hibernate 5.3提供了HibernateCriteriaBuilder来包装旧的Criterion,转换为JPA的Predicate:
if (object instanceof Criterion) { Predicate predicate = cb.unwrap(org.hibernate.query.criteria.HibernateCriteriaBuilder.class) .wrap((Criterion) object); predicates.add(predicate); }
注:
SimpleExpression和LogicalExpression都属于Criterion子类,无需单独判断
3. 替换Restrictions为CriteriaBuilder方法
针对原代码中的CriteriaValue操作符,对应JPA CriteriaBuilder的实现如下:
operator = ((CriteriaValue)object).getOperator(); criteriaValue = (CriteriaValue)object; if (criteriaValue != null) { Object value = criteriaValue.getValue(); Path<?> path = root.get(key); // 获取实体属性路径 switch (operator) { case EQ: predicates.add(value == null ? cb.isNull(path) : cb.equal(path, value)); break; case iLIKE: String ilikeVal = cleanWildcards(value).toString().toLowerCase(); predicates.add(cb.like(cb.lower(path.as(String.class)), ilikeVal)); break; case LIKE: String likeVal = cleanWildcards(value).toString(); predicates.add(cb.like(path.as(String.class), likeVal)); break; case LE: predicates.add(cb.lessThanOrEqualTo(path.as(Comparable.class), (Comparable) value)); break; case GE: predicates.add(cb.greaterThanOrEqualTo(path.as(Comparable.class), (Comparable) value)); break; case LT: predicates.add(cb.lessThan(path.as(Comparable.class), (Comparable) value)); break; case GT: predicates.add(cb.greaterThan(path.as(Comparable.class), (Comparable) value)); break; case OR: // 转换原OR逻辑返回的Criterion数组为Predicate,再用cb.or组合 Criterion[] orCriterions = getOrRestrictions(key, value); Predicate[] orPredicates = Arrays.stream(orCriterions) .map(c -> cb.unwrap(org.hibernate.query.criteria.HibernateCriteriaBuilder.class).wrap(c)) .toArray(Predicate[]::new); predicates.add(cb.or(orPredicates)); break; case AND: Criterion[] andCriterions = getAndRestrictions(key, value); Predicate[] andPredicates = Arrays.stream(andCriterions) .map(c -> cb.unwrap(org.hibernate.query.criteria.HibernateCriteriaBuilder.class).wrap(c)) .toArray(Predicate[]::new); predicates.add(cb.and(andPredicates)); break; case IN: if (value instanceof Object[]) { predicates.add(path.in((Object[]) value)); } break; case NE: predicates.add(value == null ? cb.isNotNull(path) : cb.notEqual(path, value)); break; } }
4. 绑定所有条件并执行查询
收集完所有Predicate后,统一绑定到CriteriaQuery,再执行查询:
// 绑定条件,多个条件默认用AND组合 if (!predicates.isEmpty()) { cq.where(cb.and(predicates.toArray(new Predicate[0]))); } // 处理原代码中的fields参数(指定返回字段) if (fields != null && !fields.isEmpty()) { List<Selection<?>> selections = fields.stream() .map(root::get) .collect(Collectors.toList()); cq.multiselect(selections); } // 执行查询 return sessionFactory.getCurrentSession().createQuery(cq).getResultList();
完整改写后的代码
public List<ENTITY> findByCriteria(final Map<String, Object> criteriaMap, final List<String> fields, final Class<ENTITY> entityClass) { CriteriaBuilder cb = sessionFactory.getCriteriaBuilder(); CriteriaQuery<ENTITY> cq = cb.createQuery(entityClass); Root<ENTITY> root = cq.from(entityClass); List<Predicate> predicates = new ArrayList<>(); final Set<String> keys = criteriaMap.keySet(); Object object; CriteriaValue criteriaValue; CriteriaValue.Operator operator; for (String key : keys) { object = criteriaMap.get(key); // 兼容旧的Criterion表达式 if (object instanceof Criterion) { Predicate predicate = cb.unwrap(org.hibernate.query.criteria.HibernateCriteriaBuilder.class) .wrap((Criterion) object); predicates.add(predicate); } else if (!isDefaultCriteria(key)) { // 自动转换普通值为CriteriaValue if (!(object instanceof CriteriaValue)) { if (object instanceof String && ((String) object).contains(SYSTEMWILDCARD)) { criteriaMap.put(key, new CriteriaValue(object, CriteriaValue.Operator.iLIKE)); } else { criteriaMap.put(key, new CriteriaValue(object, CriteriaValue.Operator.EQ)); } object = criteriaMap.get(key); } operator = ((CriteriaValue) object).getOperator(); criteriaValue = (CriteriaValue) object; if (criteriaValue != null) { Object value = criteriaValue.getValue(); Path<?> path = root.get(key); switch (operator) { case EQ: predicates.add(value == null ? cb.isNull(path) : cb.equal(path, value)); break; case iLIKE: String ilikeVal = cleanWildcards(value).toString().toLowerCase(); predicates.add(cb.like(cb.lower(path.as(String.class)), ilikeVal)); break; case LIKE: String likeVal = cleanWildcards(value).toString(); predicates.add(cb.like(path.as(String.class), likeVal)); break; case LE: predicates.add(cb.lessThanOrEqualTo(path.as(Comparable.class), (Comparable) value)); break; case GE: predicates.add(cb.greaterThanOrEqualTo(path.as(Comparable.class), (Comparable) value)); break; case LT: predicates.add(cb.lessThan(path.as(Comparable.class), (Comparable) value)); break; case GT: predicates.add(cb.greaterThan(path.as(Comparable.class), (Comparable) value)); break; case OR: Criterion[] orCriterions = getOrRestrictions(key, value); Predicate[] orPredicates = Arrays.stream(orCriterions) .map(c -> cb.unwrap(org.hibernate.query.criteria.HibernateCriteriaBuilder.class).wrap(c)) .toArray(Predicate[]::new); predicates.add(cb.or(orPredicates)); break; case AND: Criterion[] andCriterions = getAndRestrictions(key, value); Predicate[] andPredicates = Arrays.stream(andCriterions) .map(c -> cb.unwrap(org.hibernate.query.criteria.HibernateCriteriaBuilder.class).wrap(c)) .toArray(Predicate[]::new); predicates.add(cb.and(andPredicates)); break; case IN: if (value instanceof Object[]) { predicates.add(path.in((Object[]) value)); } break; case NE: predicates.add(value == null ? cb.isNotNull(path) : cb.notEqual(path, value)); break; } } } } // 绑定所有查询条件 if (!predicates.isEmpty()) { cq.where(cb.and(predicates.toArray(new Predicate[0]))); } // 处理指定返回字段 if (fields != null && !fields.isEmpty()) { List<Selection<?>> selections = fields.stream() .map(root::get) .collect(Collectors.toList()); cq.multiselect(selections); } return sessionFactory.getCurrentSession().createQuery(cq).getResultList(); }
内容的提问来源于stack exchange,提问作者d00d
相关产品推荐
相关产品推荐

