2013-04-23 109 views
2

我們必須從數據庫中查詢數據,我們需要查找與鍵值對列表匹配的實體。我們認爲使用Spring Data JPA是一個不錯的主意,因爲我們還需要分頁。以列表爲參數的彈簧數據查詢方法

我們創建的表是象下面這樣:

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

是否有可能定義一個查詢方法以提取與包含給定的鍵/值對的屬性的所有終端?

事情是這樣的:名單<終端> findByPropertiesKeyAndValue(名單<物業>);

回答

1

我沒有執行代碼,但給了正確的導入語句,至少編譯。根據您的實體定義,某些屬性可能需要進行調整,無論如何,您應該瞭解如何處理此問題。

我的條件查詢是基於以下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)); 
    } 
} 

可以很明顯的替代Map<String, String>Iterable<TerminalProperty>,但因爲他們似乎被綁定到特定的Terminal會感到奇怪。

+0

我希望避免規範和編程建立查詢,但顯然這是不可能的。非常感謝您的詳細解答。 – Ismail 2013-04-24 07:03:37

+0

不客氣。如果您希望避免使用'Specification',但您想使用Spring Data JPA,則您可以直接使用JPA標準API? – skirsch 2013-04-24 07:27:14