7

我正在使用休眠更新数据库中的 20K 产品。

到目前为止,我正在提取 20K 产品,遍历它们并修改一些属性,然后更新数据库。

所以:

load products

foreach products
   session begintransaction
   productDao.MakePersistant(p);
   session commit();

到目前为止,与您的标准 jdbc 相比,事情进展得很慢,我能做些什么来加快速度?

我确定我在这里做错了什么。

4

4 回答 4

10

在文档中查看这种处理的正确位置是整个第 13 章。批处理

在这里,您当前的方法有几个明显的错误:

  • 您不应为每次更新启动/提交事务。
  • 您应该启用 JDBC 批处理并将其设置为合理的数字(10-50):

    hibernate.jdbc.batch_size 20
    
  • 您应该flush()然后clear()定期进行会话(每 n 条记录,其中 n 等于hibernate.jdbc.batch_size参数),否则它将继续增长并可能OutOfMemoryException在某个时候爆炸(带有 )。

下面,13.2 节给出的例子。说明这一点的批量更新:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

ScrollableResults customers = session.getNamedQuery("GetCustomers")
    .setCacheMode(CacheMode.IGNORE)
    .scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while ( customers.next() ) {
    Customer customer = (Customer) customers.get(0);
    customer.updateStuff(...);
    if ( ++count % 20 == 0 ) {
        //flush a batch of updates and release memory:
        session.flush();
        session.clear();
    }
}

tx.commit();
session.close();

您也可以考虑使用StatelessSession

另一种选择是使用DML 样式的操作(在 HQL 中!):UPDATE FROM? EntityName (WHERE where_conditions)?. 这是 HQL 更新示例:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
        .setString( "newName", newName )
        .setString( "oldName", oldName )
        .executeUpdate();
tx.commit();
session.close();

同样,请参阅文档以获取详细信息(尤其是如何使用关键字处理versionortimestamp属性值)。VERSIONED

于 2010-03-07T22:53:08.097 回答
5

如果这是伪代码,我建议将事务移出循环,或者如果在单个事务中包含所有 20K 产品太多,则至少有一个双循环:

load products
foreach (batch)
{
   try
   {
      session beginTransaction()
      foreach (product in batch)
      {
          product.saveOrUpdate()
      }
      session commit()
   }
   catch (Exception e)
   {
       e.printStackTrace()
       session.rollback()
   }
}

另外,我建议您批量更新,而不是将每个更新单独发送到数据库。那样的话网络流量太大了。将每个块捆绑成一个批次并一次发送它们。

于 2010-03-07T22:44:19.107 回答
1

我同意上面关于查看批处理章节的答案。

我还想补充一点,您应该确保只加载对产品进行更改所需的内容。

我的意思是,如果产品急切地加载大量对该事务不重要的其他对象,您应该考虑不加载连接的对象 - 它会加快产品的加载速度,并且根据它们的持久性策略,也可能在使产品再次持久化时节省您的时间。

于 2010-03-08T13:49:47.673 回答
0

进行批量更新的最快方法是将其转换为单个 SQL 语句并在会话上作为原始 sql 执行。就像是

update TABLE set (x=y) where w=z;

如果失败,您可以尝试减少事务并批量更新:

start session
start transaction

products = session.getNamedQuery("GetProducs")
    .setCacheMode(CacheMode.IGNORE)
    .scroll(ScrollMode.FORWARD_ONLY);
count=0;
foreach product
    update product
    if ( ++count % 20 == 0 ) {
        session.flush();
        session.clear();
    }
}

commit transaction
close session

有关更多信息,请查看Hibernate 社区文档

于 2010-03-07T22:49:58.650 回答