【问题标题】:Make hibernate save() and update() as a single insert optimization将休眠 save() 和 update() 作为单个插入优化
【发布时间】:2013-06-05 13:08:44
【问题描述】:

我想知道如何使用我的 Hibernate (4) 和 Oracle 设置减少 SQL 更新。

一个简单的例子可能是这样的:

打开一个会话/事务,使用 session.save() 创建一个新实体 Xyz,处理一些业务逻辑,对 Xyz 进行一些更改并调用 session.update(),然后让会话正常关闭并通过提交休眠到数据库。

提交时,Hibernate 会先插入,然后再进行更新——但它真正需要做的只是一次插入,但在本例中使用 Xyz 的 latest 属性。

有没有人对做这种事情有任何想法/模式?或者有什么办法可以改变 Hibernate 的行为吗?或者你对此有什么看法——它是一个完整的反模式吗?

我知道 Hibernate 足够聪明,可以忽略多个更新,当一个更新覆盖另一个更新时——那么为什么插入不相似呢?

这个小sn-p可以重现“问题”:

MyEntity e = new MyEntity("xxx"); 
Session session = sessionFactory.openSession(); 
session.beginTransaction();
session.save(e); e.setName("yyy"); 
session.getTransaction().commit(); 
session.close();

顺便说一句,我不用担心触发因素。

这个例子非常简单,性能问题可能看起来微不足道,但实际上我正在处理一个更复杂的对象(即多次插入和更新)和大量数据,因此避免更新会很好。

【问题讨论】:

  • 您不能将保存移动到事务的末尾并在将更改保存到数据库之前将更改应用于实体吗?如果没有,请告诉我们是什么阻止了这种方法,以便我们制定解决方案。因为我不认为 Hibernate 内置了一个简单的解决方案(虽然不确定)。
  • 这是一个解决方案,但问题是我正在使用各种其他有线服务,这些服务期望对象在会话中(他们从会话中读取对象)。而且它们在其他地方使用,因此重新分解它们比听起来要多。有没有办法可以将对象添加到会话中,并将其标记为持久性,但直到最后才调用 save() (即不会生成 INSERT)?
  • 你是如何从其他服务的会话中获取实体的?
  • 我想您将不得不扩展您的示例,以便我掌握它。 ^^" 但是如果你使用不同的'事务'和'session.get'之类的东西,就没有办法解决这个问题,因为几乎所有的Hibernate实体获取方法都旨在获取持久实体。如果是这样的话,我担心会有这里没有快速获胜。唯一的机会可能是以某种方式滥用缓存(使用内置方法或可能使用反射)并防止刷新。但只考虑这一点似乎是错误的。xD
  • 您好,感谢您的意见。根据传递给实体的 id 在各个点重新读取实体——这是在实体实例化时创建的!实际上,实体刚刚被“保存”并且将在缓存中,而不是在数据库中,因为事务还没有结束。这都是一笔大交易。将外部包装器视为创建实体的事物,它为某些可能更改实体属性的业务逻辑调用其他服务。我同意这里也可能没有速赢。

标签: hibernate session optimization insert save


【解决方案1】:

有没有办法可以将对象添加到会话中,并将其标记为持久,但直到最后才调用 save()(即不会生成 INSERT)?

如果您想将INSERT 的执行推迟到会话刷新和/或事务关闭,请考虑使用Session.persist(Object) 而不是Session.save(Object) 进行重构。

来自JBoss Hibernate Community documentation 的第 10.2 节:

您还可以使用persist() 代替save(),语义在EJB3 早期草案中定义。

  • persist() 使瞬态实例持久化。但是,它不保证标识符值将立即分配给持久实例,分配可能发生在刷新时间。 persist() 还保证如果在事务边界之外调用INSERT 语句,它不会执行它。这在具有扩展会话/持久性上下文的长时间运行的对话中很有用。
  • save() 保证返回一个标识符。如果必须执行INSERT 以获取标识符(例如“身份”生成器,而不是“序列”),则无论您是在事务内部还是外部,都会立即发生INSERT。这在具有扩展会话/持久性上下文的长期对话中是有问题的。

另一个有用的参考可以在此blog article 的“全部放在一起”部分顶部的方法到场景网格的左上角单元格中找到,它描述了persist() 在从未持久化的对象上的行为如下:

  1. 对象作为新实体添加到持久性上下文中
  2. 新实体插入数据库flush()/commit()

【讨论】:

  • 我认为,尽管信息量很大,但它可能无法完全回答问题。我使用标准 JPA 实体管理器和 em.persist(e) 发现了相同的情况,随后进行了一些更新,最终导致插入,然后在休眠操作队列中进行更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-12
  • 2011-01-08
  • 2011-12-09
  • 1970-01-01
  • 2012-02-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多