到目前为止,我的偏好是始终使用EntityManager的merge()
来处理插入和更新。但我也注意到merge在update / insert之前执行了额外的select查询,以确保数据库中不存在记录。
现在我正在开发一个需要对数据库进行大量(批量)插入的项目。从性能的角度来看,在我绝对知道我总是创建一个要保留的对象的新实例的场景中使用persist而不是merge是有意义的吗?
答案 0 :(得分:69)
当merge
足够 - persist
完成更多工作时,使用merge
并不是一个好主意。该主题之前已经discussed on StackOverflow,并且this article详细解释了这些差异,并提供了一些很好的流程图以使事情变得清晰。
答案 1 :(得分:9)
如果像你说的那样,我肯定会坚持persist()
:
(...)我完全知道我总是创建一个新的对象实例来保持(...)
这就是这个方法的全部意义 - 它将在实体已经存在的情况下保护您(并将回滚您的交易)。
答案 2 :(得分:2)
如果您使用的是指定的生成器using merge
instead of persist
can cause a redundant SQL statement,则会影响性能。
此外,calling merge for managed entities也是一个错误,因为管理实体由Hibernate自动管理,并且dirty checking mechanism在flushing the Persistence Context上状态与数据库记录同步。
要了解所有这些是如何工作的,首先应该知道Hibernate将开发人员的思维方式从SQL语句转移到entity state transitions。
一旦Hibernate主动管理实体,所有更改都将自动传播到数据库。
Hibernate监视当前附加的实体。但是对于要管理的实体,它必须处于正确的实体状态。
首先,我们必须定义所有实体状态:
新(瞬态)
新创建的对象尚未与Hibernate Session
(又名Persistence Context
)关联且未映射到任何数据库表行被视为处于新(暂停)状态。
要成为持久化,我们需要显式调用EntityManager#persist
方法或使用传递持久性机制。
持久(管理)
持久性实体已与数据库表行关联,并且由当前运行的持久性上下文管理。对此类实体所做的任何更改都将被检测并传播到数据库(在会话刷新时间内)。
使用Hibernate,我们不再需要执行INSERT / UPDATE / DELETE语句。 Hibernate使用transactional write-behind工作方式,并且在当前Session
刷新时间的最后一个负责时刻同步更改。
独立式
当前运行的持久性上下文关闭后,所有先前管理的实体都将分离。将不再跟踪连续更改,也不会发生自动数据库同步。
要将分离的实体与活动的Hibernate会话相关联,您可以选择以下选项之一:
再附
Hibernate(但不是JPA 2.1)支持通过Session#update方法重新连接。 Hibernate会话只能为给定的数据库行关联一个Entity对象。这是因为持久性上下文充当内存缓存(第一级缓存),并且只有一个值(实体)与给定密钥(实体类型和数据库标识符)相关联。 只有在没有与当前Hibernate会话关联的其他JVM对象(匹配相同的数据库行)时,才能重新附加实体。
合并
合并将将分离的实体状态(源)复制到托管实体实例(目标)。如果合并实体在当前会话中没有等效项,则将从数据库中获取一个。 即使在合并操作之后,分离的对象实例仍将继续保持分离状态。
删除
虽然JPA要求仅允许删除托管实体,但Hibernate还可以删除分离的实体(但只能通过Session#delete方法调用)。 删除的实体仅计划删除,实际的数据库DELETE语句将在会话刷新时执行。
为了更好地理解JPA状态转换,您可以可视化下图:
或者如果您使用Hibernate特定的API: