Hibernate 如何在JPA标准API中正确应用连接获取

Hibernate 如何在JPA标准API中正确应用连接获取,hibernate,jpa,spring-data,spring-data-jpa,jpa-criteria,Hibernate,Jpa,Spring Data,Spring Data Jpa,Jpa Criteria,我在基于Spring数据JPA的应用程序中使用JPA标准API。我的服务类使用静态方法来检索规范,然后可以将这些规范组合在一起形成特定的查询。例如 repository.findAll(where(matchById(str)).or(matchByName(str))) 这里,我使用两种方法返回规范,并应用适当的标准。这就是这两种方法的样子 public static Specification<SomeEntity> matchById(String str) { re

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

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() + "%")
        );
}

任何帮助都将不胜感激

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

通过使用以下内容,您的
规范
实现只需通过调用
#getOrCreateJoin(…)
来使用helper类,它将返回(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;
  }
}
公共类标准查询帮助{
//对于列表属性,获取或创建联接
//其他容器类型可能需要其他实现。
公共静态列表连接getOrCreateJoin(
从根本上说,
ListAttribute属性,
接合类型(接合类型){
ListJoin=(ListJoin)getJoin(根、属性、joinType);
return join!=null?join:root.join(属性,joinType);
}
//获取联接,首先查看联接获取,然后是联接
私有静态连接getJoin(
从根本上说,
属性获取:root.getFetches()){
最终类attributeClass=fetch.getAttribute().getClass();
如果(attributeClass.isAssignableFrom(attribute.getClass()){
最终字符串名称=attribute.getName();
if(name.equals(fetch.getAttribute().getName())){
返回(加入)获取;
}
}
}
返回null;
}      
//从联接中获取联接
私有静态连接getJoinFromJoins(
从根本上说,
属性获取:root.getJoins(){
最后一个字符串joinName=join.getAttribute().getName();
if(joinName.equals(attribute.getName())){
if(join.getJoinType().equals(joinType)){
返回(加入)加入;
}
}
}
返回null;
}
}

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

通过使用以下内容,您的
规范
实现只需通过调用
#getOrCreateJoin(…)
来使用helper类,它将返回(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;
  }
}
公共类标准查询帮助{
//对于列表属性,获取或创建联接
//其他容器类型可能需要其他实现。
公共静态列表连接getOrCreateJoin(
从根本上说,
ListAttribute属性,
接合类型(接合类型){
ListJoin=(ListJoin)getJoin(根、属性、joinType);
return join!=null?join:root.join(属性,joinType);
}
//获取联接,首先查看联接获取,然后是联接
私有静态连接getJoin(
从根本上说,
属性获取:root.getFetches()){
最终类attributeClass=fetch.getAttribute().getClass();
如果(attributeClass.isAssignableFrom(attribute.getClass()){
最终字符串名称=attribute.getName();
if(name.equals(fetch.getAttribute().getName())){
返回(加入)获取;
}
}
}
返回null;
}      
//从联接中获取联接
私有静态连接getJoinFromJoins(
从根本上说,
属性获取:root.getJoins(){
最后一个字符串joinName=join.getAttribute().getName();
if(joinName.equals(attribute.getName())){
if(join.getJoinType().equals(joinType)){
返回(加入)加入;
}
}
}
返回null;
}
}