【问题标题】:How to properly apply join fetch in JPA Criteria API如何在 JPA Criteria API 中正确应用连接提取
【发布时间】:2017-02-01 20:25:27
【问题描述】:

我在基于 Spring 数据 JPA 的应用程序中使用 JPA Criteria API。我的服务类使用静态方法来检索Specifications,然后可以将它们组合在一起以形成特定的查询。例如

repository.findAll(where(matchById(str)).or(matchByName(str)))

在这里,我使用了两种方法,它们返回具有适用于它的适当标准的规范。这就是这两种方法的样子

public static Specification<SomeEntity> matchById(String str) {
    return (root, criteriaQuery, cb) -> 
        cb.like(root.get(SomeEntity_.id).as(String.class), str + "%");
}

public static Specification<SomeEntity> matchByName(String str) {
    return (root, criteriaQuery, cb) -> {
        cb.or(cb.like(cb.lower(root.get(SomeEntity_.firstName)), str.toLowerCase() + "%"),
              cb.like(cb.lower(root.get(SomeEntity_.lastName)), str.toLowerCase() + "%")
        );
}

这很好用。我想添加一个

root.fetch(SomeEntity_.employee, JoinType.INNER);

使用静态规范方法的任意组合构建的所有查询都使用FETCH JOIN

如果我将此语句添加到两个静态方法中,则 INNER JOIN 会应用两次,这似乎不正确。理想情况下,我认为我应该有另一个仅应用 FETCH JOIN 并返回规范的静态方法,但我似乎无法弄清楚如何在不使用任何 criteriaBuilder 方法的情况下返回 Predicate。澄清一下,我的方法应该是这样的:

public static Specification<SomeEntity> start() {
    return (root, criteriaQuery, criteriaBuilder) -> {
        root.fetch(SomeEntity_.employee, JoinType.INNER);

        // NOW RETURN WHAT ???
        return null;
    };
}

任何帮助将不胜感激。

【问题讨论】:

  • return null 替换为 criteriaBuilder.conjunction() 。这意味着创建一个连词(零连词)。零连词的连词为真。` 并返回谓词。

标签: hibernate jpa spring-data spring-data-jpa jpa-criteria


【解决方案1】:

我过去使用的一个解决方案是引入一个CriteriaQueryHelper 类,它允许我为它提供几个 JPA 类,它可以确定是应该构建新的连接或提取还是重用现有的。

通过使用以下内容,您的 Specification 实现将简单地通过调用 #getOrCreateJoin(...) 来使用帮助程序类,它将返回 (a) 现有连接而不创建新连接或 (b) 新创建的实例如果一个不存在。

这很容易避免您描述的多个连接问题。

public class CriteriaQueryHelper {

  // for List<> attributes, get or create a join
  // other implementations would be needed for other container types likely.
  public static <X, Y, Z> ListJoin<X, Y> getOrCreateJoin(
                From<Z, X> root, 
                ListAttribute<X, Y> attribute,
                JoiNType joinType) {
    ListJoin<X, Y> join = (ListJoin<X, Y>) getJoin( root, attribute, joinType );
    return join != null ? join : root.join( attribute, joinType );
  }

  // gets the join, looking at join-fetch first, followed by joins
  private static <X, Y, Z> Join<X, Y> getJoin(
                From<Z,X> root, 
                Attribute<?, Y> attribute, 
                JoinType joinType) {
    Join<X, Y> fetchJoin = getJoinFromFetches( root, attribute );
    if ( fetchJoin != null ) {
      return fetchJoin; 
    }
    Join<X, Y> join = getJoinFromJoins( root, attribute, joinType );
    return join;
  }

  // gets a join from fetch
  private static <X, Y, Z> Join<X, Y> getJoinFromFetches(
                 From<Z, X> root, 
                 Attribute<?, Y> attribute) {
    for ( Fetch<X, ?> fetch : root.getFetches() ) {
      final Class<?> attributeClass = fetch.getAttribute().getClass();
      if ( attributeClass.isAssignableFrom( attribute.getClass() ) ) {       
        final String name = attribute.getName();
        if ( name.equals( fetch.getAttribute().getName() ) ) {
          return (Join<X, Y>) fetch;
        }
      }
    }
    return null;
  }      

  // gets a join from joins
  private static <X, Y, Z> Join<X, Y> getJoinFromJoins(
                 From<Z, X> root, 
                 Attribute<?, Y> attribute,
                 JoinType joinType) {
    for ( Join<?, ?> fetch : root.getJoins() ) {
      final String joinName = join.getAttribute().getName();
      if ( joinName.equals( attribute.getName() ) ) {
        if ( join.getJoinType().equals( joinType ) ) {
          return (Join<X, Y>) join;
        }
      }
    }
    return null;
  }
}

【讨论】:

    猜你喜欢
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    • 2012-03-08
    • 2011-03-17
    • 2016-11-09
    • 2017-04-13
    相关资源
    最近更新 更多