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

时间:2015-06-18 15:22:18

标签: jpa derby jpql

以下是正在传递给" IN"的列表的代码。子句有几个值。在我的情况下,计数是1400的值。此外,客户表中还有数千个(大约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子句基于它而执行的值更多,则会抛出错误说“#34;语句过于复杂& #34;

由于我是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;
}

3 个答案:

答案 0 :(得分:2)

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

该限制与Java字节码格式中单个函数大小的基本限制有关; Derby当前通过生成Java字节码来评估IN子句来实现IN子句执行,如果生成的字节码超出JVM的基本限制,则Derby会抛出“语句过于复杂”的错误。

已经讨论过如何解决这个问题,例如见:

但是现在,您最好的方法可能是找到一种方法来表达您的查询,而不会生成如此庞大而复杂的IN子句。

答案 1 :(得分:1)

好的,这是我的解决方案对我有用。我无法更改生成customerList的部分,因为我不可能,因此解决方案必须来自此方法。布莱恩你的探索是最好的,我仍然混淆“in”条款如何与表完美配合。请参阅下面的解决方案。

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
    }       


}   

答案 2 :(得分:0)

JPA和底层的Hibernate只是将其转换为普通的JDBC理解查询。你不会手动在IN子句中用1400个元素写一个查询,不是吗?没有魔力。无论在普通SQL中不起作用,都不会在JPQL中使用。

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

我也注意到你的customerId是双倍的 - 一个糟糕的PK选择。通常人们使用很长时间(自动增量/测序等)并且我没有得到临时表&#34;逻辑。