2013-04-23 113 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