以下是正在传递给" 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;
}
答案 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;逻辑。