JPQL In 子句错误 - 语句太复杂

JPQL In clause error - Statement too complex

如果传递给 "IN" 子句的列表有多个值,则以下代码将被破坏。在我的例子中,计数是 1400 个值。此外,客户 table 中有数千条(约 100,000 条)记录。正在对 DERBY 数据库执行查询。

    public List<Customer> getCustomersNotIn(String custType, List<Int> customersIDs) {

    TypedQuery<Customer> query = em.createQuery("from Customer where type=:custType and customerId not in (:customersIDs)", Customer.class);

    query.setParameter("custType", custType);
    query.setParameter("customersIDs", customersIDs);   
    List<Customer> customerList  = query.getResultList();


    return customerList;
}

如果列表的值较少(可能小于 1000),则上述方法完美执行,如果列表 customersIDs 的值较多,因为 in 子句是基于它执行的,它会抛出一个错误 "Statement too complex"

因为我是 JPA 的新手,谁能告诉我如何按照下面描述的方式编写上面提到的函数。* 请阅读代码中的注释 *

    public List<Customer> getCustomersNotIn(String custType, List<Int> customersIDs) {

    // CREATE A IN-MEMORY TEMP TABLE HERE...

    // INSERT ALL VALUES FROM customerIDs collection into temp table

    // Change following query to get all customers EXCEPT THOSE IN TEMP TABLE
    TypedQuery<Customer> query = em.createQuery("from Customer where type=:custType and customerId not in (:customersIDs)", Customer.class);

    query.setParameter("custType", custType);
    query.setParameter("customersIDs", customersIDs);   
    List<Customer> customerList  = query.getResultList();

    // REMOVE THE TEMP TABLE FROM MEMORY

    return customerList;
}

JPA 和底层 Hibernate 只是将其转换为正常的 JDBC-理解的查询。您不会手动在 IN 子句中编写包含 1400 个元素的查询,对吗?没有魔法。在正常 SQL 中不起作用的任何东西,在 JPQL 中都不会。

我不确定在调用该方法之前您是如何获得该列表的(很可能来自另一个查询)。您最好的选择是根据用于获取这些 ID 的标准加入这些表。通常,您希望在一个 query/transaction 中执行相关过滤器,这意味着一种方法而不是传递长列表。

我还注意到您的 customerId 是双倍的 - 对于 PK 来说是一个糟糕的选择。通常人们使用 long(autoincremented/sequenced 等)而我不明白 "temp table" 逻辑。

Derby IN 子句支持确实对 IN 子句中可以提供的值的数量有限制。

该限制与 Java 字节码格式中单个函数大小的潜在限制有关; Derby 目前通过生成 Java 字节码来评估 IN 子句来执行 IN 子句,如果生成的字节码超出 JVM 的基本限制,Derby 会抛出 "statement too complex" 错误。

已就解决此问题的方法进行了讨论,例如,请参阅:

但目前,您最好的方法可能是找到一种方式来表达您的查询,而无需生成如此庞大而复杂的 IN 子句。

好的,这是适合我的解决方案。我无法更改生成 customerList 的部分,因为这对我来说是不可能的,因此解决方案必须来自此方法。布莱恩,你的解释是最好的,我仍然混淆 "in" 子句如何与 table 完美配合。请参阅以下解决方案。

public List<Customer> getCustomersNotIn(String custType, List<Int> customersIDs) {

// INSERT customerIds INTO TEMP TABLE 
storeCustomerIdsIntoTempTable(customersIDs)


// I AM NOT SURE HOW BUT,  "not in" CLAUSE WORKED INCASE OF TABLE BUT DID'T WORK WHILE PASSING LIST VALUES.
TypedQuery<Customer> query = em.createQuery("select c from Customer c where c.customerType=:custType and  c.customerId not in (select customerId from TempCustomer)");
query.setParameter("custType", custType);
List<Customer> customerList  = query.getResultList();

// REMOVE THE DATA FROM TEMP TABLE 
deleteCustomerIdsFromTempTable()
return customerList;
}


private void storeCustomerIdsIntoTempTable(List<Int> customersIDs){

// I ENDED UP CREATING TEMP PHYSICAL TABLE, INSTEAD OF JUST IN MEMORY TABLE
    TempCustomer tempCustomer = null;
    try{
        tempCustomerDao.deleteAll();
        for (Int customerId : customersIDs) {
            tempCustomer = new TempCustomer();
            tempCustomer.customerId=customerId;         
            tempCustomerDao.save(tempCustomer);
        }
    }catch(Exception e){
        // Do logging here
    }   

}

private void deleteCustomerIdsFromTempTable(){

    try{
        // Delete all data from TempCustomer table to start fresh
        int deletedCount= tempCustomerDao.deleteAll();
        LOGGER.debug("{} customers deleted from temp table", deletedCount);

    }catch(Exception e){
        // Do logging here
    }       


}