2017-02-01 19 views
0

Я использую API-интерфейс JPA в приложении JPA для данных Spring. Мой класс сервиса использует статические методы для извлечения Specifications, которые затем могут быть объединены вместе для формирования конкретного запроса. Например.Как правильно применить выбор подключения в 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() + "%") 
     ); 
} 

Это прекрасно работает. Я хочу, чтобы добавить

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

таким образом, что все запросы, которые построены с использованием любой комбинации статических методов Спецификации, использовать FETCH JOIN.

Если я добавлю это утверждение к статическим методам, тогда INNER JOIN применяется дважды, что не кажется правильным. В идеале, я думаю, у меня должен быть другой статический метод, который применяет только FETCH JOIN и возвращает спецификации, но я не могу понять, как вернуть Predicate без использования каких-либо методов criteriaBuilder. Чтобы уточнить, так должен выглядеть мой метод:

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

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

Любая помощь будет оценена.

ответ

0

Одно из решений, которое я использовал в прошлом, состояло в том, чтобы ввести класс CriteriaQueryHelper, который позволил мне предоставить его несколькими классами JPA, и это определит, следует ли создавать или повторно использовать новое соединение или выборку.

С использованием следующих, ваши Specification реализации будет просто использовать вспомогательный класс, вызвав #getOrCreateJoin(...) и он будет возвращать либо (а) существующее объединение без создания нового или (б) вновь созданного экземпляра, если один Didn не существует.

Это позволяет легко устранить проблему, которую вы описали несколькими соединениями.

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; 
    } 
}