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
}
}
如果传递给 "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
}
}