重写Hibernate查询没有巨大的列表参数

时间:2012-12-10 10:14:38

标签: java sql hibernate postgresql

在我的数据库中,我有一个zip表,其中包含code列。用户可以上传Zip代码列表,我需要确定哪些已经存在于数据库中。目前,我使用以下Hibernate查询(HQL)执行此操作:

select zip.code from Zip zip
where zip.code in (:zipCodes)

:zipCodes参数的值是用户上传的代码列表。但是,在Hibernate的版本中我使用a bug限制了这些列表参数的大小,有时候我们超过了这个限制。

因此,我需要找到另一种方法来确定哪些(可能非常长的)邮政编码列表已经存在于数据库中。以下是我考虑过的几个选项

选项A

使用SQL而不是HQL重写查询。虽然这将避免Hibernate错误,但我怀疑如果需要检查30,000个Zip代码,性能将会很糟糕。

选项B

将Zip代码列表拆分为一系列子列表,并为每个子列表执行单独的查询。同样,这将避免Hibernate错误,但性能可能仍然很糟糕

选项C

使用临时表,即将要检查的Zip代码插入临时表,然后将其加入zip表。看起来这个解决方案的查询部分应该运行得相当好,但是创建临时表并插入多达30,000行不会。但也许我没有以正确的方式去做,这就是我在伪Java代码中的想法

/**
 * Indicates which of the Zip codes are already in the database
 *
 * @param zipCodes the zip codes to check
 * @return the codes that already exist in the database
 * @throws IllegalArgumentException if the list is null or empty
 */
List<Zip> validateZipCodes(List<String> zipCodes) {

  try {
    // start transaction

    // execute the following SQL
    CREATE TEMPORARY TABLE zip_tmp
    (code VARCHAR(255) NOT NULL) 
    ON COMMIT DELETE ROWS;

    // create SQL string that will insert data into zip_tmp
    StringBuilder insertSql = new StringBuilder()

    for (String code : zipCodes) {
      insertSql.append("INSERT INTO zip_tmp (code) VALUES (" + code + ");")
    }     

    // execute insertSql to insert data into zip_tmp

    // now run the following query and return the result   
    SELECT z.*
    FROM zip z
    JOIN zip_tmp zt ON z.code = zt.code

  } finally {
    // rollback transaction so that temporary table is removed to ensure
    // that concurrent invocations of this method operate do not interfere
    // with each other
  }    
}

有没有比上面的伪代码更有效的方法来实现它,还是有其他解决方案,我还没有想到?我正在使用Postgres数据库。

5 个答案:

答案 0 :(得分:1)

将数据库中的所有Zip代码加载到List。在用户输入的邮政编码列表中,执行removeAll(databaseList)

问题解决了!

答案 1 :(得分:0)

选项D:
从数据库加载所有现有的邮政编码(分页?)并在您的应用程序中进行比较。

关于您的选项A:
我记得SQL查询版本的限制但是在DB2上,我不知道PostgreSQL是否有限制。

答案 2 :(得分:0)

您是否尝试使用子查询IN?

http://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/criterion/Subqueries.html

会是这样的

DetachedCriteria dc = DetachedCriteria.forClass(Zip.class, "zz");
//add restrictions for the previous dc

Criteria c = session.createCriteria(Zip.class, "z");
c.add(Subqueries.in("z.code" dc));
如果我弄错了代码,那已经有一段时间了,因为我不使用Hibernate

答案 3 :(得分:0)

假设您根据100000条记录的表“验证”1000个代码,其中代码是主键并且具有聚簇索引。

  • 选项A不是改进,Hibernate将构建相同的SELECT ... IN ...你可以自己编写。
  • 选项B以及您当前的查询可能无法使用索引。
  • 如果您确定邮政编码在任意时间不会发生变化,那么选项D可能会很好,这是不太可能的,或者如果您可以从尝试处理现有代码中恢复。
  • 选项C(创建一个临时表,发出1000个INSERT语句并在一个SELECT中连接1000个对100000行)并不具备竞争力,只需为每个单个新代码发出1000个简单且索引友好的查询:

    SELECT COUNT(*)FROM Zip WHERE Zip.code =:newCode

答案 4 :(得分:0)

在美国大约有45'000个邮政编码,而且似乎是根据需要进行更新。如果这是一个年轻的工作,不要用java写。创建一个sql脚本,将zip代码加载到新表中,并使用

编写一个insert语句

insert XXX into zip where zip.code not in (select code from ziptemp)

让你的操作人员每年运行一次这两行SQL脚本,不要在java代码中自行购买。另外,如果你把它保留在java之外,你基本上可以采取任何方法,因为没有人关心在非高峰时间运行30分钟。

divide et impera