如何将嵌套列表的列表传递到 JPA 本机查询中

Posted

技术标签:

【中文标题】如何将嵌套列表的列表传递到 JPA 本机查询中【英文标题】:How to pass list of nested lists into JPA native query 【发布时间】:2021-08-26 11:27:26 【问题描述】:

我需要创建一个与以下示例相关的 JPA 原生查询:

select * from inventory where (server, product) in (('server1','product1'), ('server2','product2'), ('server1','product3'));

我创建了一个原生查询:

@Query(
            value = "select server, product from inventory where (server, product) in (:myList)",
            nativeQuery = true
    )
    List<Product> getProducts(List<List<String>> myList);

但是当我将列表列表作为参数传递时:

List<List<String>> myList= Arrays.asList(
    Arrays.asList("server1","product1"),
    Arrays.asList("server2","product2"),
    Arrays.asList("server1","product3")
);
List<Product> products = myRepository.getProducts(myList);

我收到一个错误

java.sql.SQLSyntaxErrorException: ORA-00920: invalid relational operator

我使用 Oracle 数据库。请告诉我,我做错了什么?

这个问题不是关于将平面列表传递到本机查询中以按 1 列过滤数据库结果(这很好),而是如何传递嵌套列表列表 进入查询按 2 列过滤数据库结果

【问题讨论】:

你好@Ula,在继续之前你必须将你的通用列表解析为一个字符串:) 您好,谢谢您的回答。但是,当我尝试将它作为扁平字符串传递时,它也不起作用。 【参考方案1】:

已知缺少括号或逗号会引发此错误。

java.sql.SQLSyntaxErrorException: ORA-00920: invalid relational operator

所以我认为 将您的通用列表解析为字符串

请注意将您的数据解析为 String ,我建议您使用 String 作为 getProducts 方法的参数,否则您应该先解析该函数​​中的列表,然后再将其添加到查询中:

这是一个如何解析通用列表的示例:

 String myListToStr = "";
    for(List<String> list : myList)
        myListToStr += "(\"" + String.join("\",\"", list) + "\")";
    
    myListToStr = myListToStr.replace(")(", "),(");
    System.out.println(myListToStr);

结果: ("server1","product1"),("server2","product2"),("server1","product3")

正是您在这种情况下所需要的。

希望您对这个解决方案感到满意。如果它确实解决了您的问题,请务必将其标记为答案!

【讨论】:

您好,谢谢您的回答。但是,当我尝试将它作为扁平字符串传递时,它也不起作用。【参考方案2】:

我找到了一个可行的解决方案。我不得不对 Specification 和 CriteriaBuilder 使用动态查询。

我创建了一个模型类:

public class SrvProdTuple 
    String server;
    String product;
    // + getters

规格:

public class mySpecification 

public static Specification<Product> getProducts(
        List<SrvProdTouple> myList
) 
    return (root, query, cb) -> 
        List<Predicate> predicates = new ArrayList<>();

        myList.forEach(srvProdTouple -> 
            List<Predicate> srvProdPredicates = new ArrayList<>();

            srvProdPredicates.add(
                cb.equal(root.get("server"), srvProdTouple.getServer())
            );
            srvProdPredicates.add(
                cb.equal(root.get("product"), srvProdTouple.getProduct())
            );

            predicates.add(cb.and(srvProdPredicates.toArray(new Predicate[])));
        );

        return cb.or(predicates.toArray(new Predicate[]));
    ;

及用法(我用了一个JPA方法findAll(Specification specification)):

List<SrvProdTouple> myList= Arrays.asList(
    new SrvProdTouple("server1","product1"),
    new SrvProdTouple("server2","product2"),
    new SrvProdTouple("server1","product3")
);
List<Product> products = myRepository.findAll(mySpecification.getProducts(myList));

【讨论】:

以上是关于如何将嵌套列表的列表传递到 JPA 本机查询中的主要内容,如果未能解决你的问题,请参考以下文章

JPA 中的传递列表命名本机查询

如何在本机查询中使用 NVL 或 COALESCE 获取 Spring Data JPA 中的值列表

如何使用本机查询在 JPA 中执行嵌套联接

如何通过在spring数据本机查询或存储库中传递userIds列表来获取用户的最大日期时间戳

将数组传递给 PostgreSQL PL/pgSQL 函数

JPA:如何将原生查询结果集转换为 POJO 类集合