2013-04-23 3 views
2

키 값 쌍 목록과 일치하는 엔터티를 찾을 필요가있는 데이터베이스에서 데이터를 쿼리해야합니다. 우리는 페이지 매김이 필요할 때 스프링 데이터 JPA를 사용하는 것이 좋은 생각이라고 생각했습니다. 우리가 생성목록을 인수로 사용하는 스프링 데이터 쿼리 메서드

테이블과 같은 다음과 같다

terminal(ID,NUMBER,NAME); 
terminal_properties(ID,KEY,VALUE,TERMINAL_FK); 

가 주어진 키/값 쌍을 포함하는 특성을 갖는 모든 단자를 가져 질의 방법을 정의 할 수있다? 이 같은

뭔가 : 목록 < 터미널 > findByPropertiesKeyAndValue (목록 < 재산권 >);

답변

1

나는 코드를 실행하지 않았지만, 정확한 import 문이 주어지면 적어도 이것을 컴파일한다. 엔티티 정의에 따라 일부 속성을 적용해야 할 수도 있으며 어떤 경우에도이 속성에 접근하는 방법에 대한 아이디어를 얻으십시오.

내 기준 쿼리는 다음과 같은 SQL을 기반으로합니다

SELECT * FROM TERMINAL 
    WHERE ID IN (
    SELECT TERMINAL_FK FROM TERMINAL_PROPERTIES 
     WHERE (KEY = 'key1' AND VALUE = 'value1') 
     OR (KEY = 'key2' AND VALUE = 'value2') 
     ... 
     GROUP BY TERMINAL_FK 
     HAVING COUNT(*) = 42 
) 
각 이름/값 쌍을 나열

42은 단순히 이름/값 쌍의 수를 나타냅니다.

그래서 나는이 같은 저장소를 정의 가정 :

public interface TerminalRepository extends CrudRepository<Terminal, Long>, JpaSpecificationExecutor { 
} 

그것은 기준 API의 사용을 만들기 위해 JpaSpecificationExecutor을 확장하는 것이 중요합니다.

그런 다음 당신은이 같은 기준에 쿼리를 구축 할 수 있습니다 :

public class TerminalService { 

    private static Specification<Terminal> hasProperties(final Map<String, String> properties) { 
    return new Specification<Terminal>() { 
     @Override 
     public Predicate toPredicate(Root<Terminal> root, CriteriaQuery<?> query, CriteriaBuilder builder) { 
     // SELECT TERMINAL_FK FROM TERMINAL_PROPERTIES 
     Subquery<TerminalProperty> subQuery = query.subquery(TerminalProperty.class); 
     Root propertyRoot = subQuery.from(TerminalProperty.class); 
     subQuery.select(propertyRoot.get("terminal.id")); 
     Predicate whereClause = null; 
     for (Map.Entry<String, String> entry : properties.entrySet()) { 
      // (KEY = 'key1' AND VALUE = 'value1') 
      Predicate predicate = builder.and(builder.equal(propertyRoot.get("key"), 
       entry.getKey()), builder.equal(propertyRoot.get("value"), entry.getValue())); 
      if (whereClause == null) { 
      whereClause = predicate; 
      } else { 
      // (...) OR (...) 
      whereClause = builder.or(whereClause, predicate); 
      } 
     } 
     subQuery.where(whereClause); 
     // GROUP BY TERMINAL_FK 
     subQuery.groupBy(propertyRoot.get("terminal.id")); 
     // HAVING COUNT(*) = 42 
     subQuery.having(builder.equal(builder.count(propertyRoot), properties.size())); 

     // WHERE ID IN (...) 
     return query.where(builder.in(root.get("id")).value(subQuery)).getRestriction(); 
     } 
    }; 
    } 

    @Autowired 
    private TerminalRepository terminalRepository; 

    public Iterable<Terminal> findTerminalsWith(Map<String, String> properties) { 
    // this works only because our repository implements JpaSpecificationExecutor 
    return terminalRepository.findAll(hasProperties(properties)); 
    } 
} 

당신은 분명히 Iterable<TerminalProperty>Map<String, String>을 대체 할 수 있습니다, 그들은 특정 Terminal에 바인딩하는 것 때문에 그 이상한 느낌을 받겠지만.

+0

사양을 피하고 프로그래밍 방식으로 쿼리를 작성하기를 바랬지 만 분명히 불가능합니다. 자세한 답변을 주셔서 감사합니다. – Ismail

+0

반갑습니다. 그리고 'Specification'을 사용하지 않으려면 JPA criteria API를 직접 사용할 수 있지만 Spring Data JPA를 사용하고 싶습니까? – skirsch

관련 문제