You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 11:31:07