到目前为止,我的偏好是始终使用EntityManager的 merge() 来处理插入和更新 . 但我也注意到merge在update / insert之前执行了额外的select查询,以确保数据库中不存在记录 .
merge()
现在我正在开发一个需要对数据库进行大量(批量)插入的项目 . 从性能的角度来看,在我绝对知道我总是在创建一个要保留的对象的新实例的场景中使用persist而不是merge是有意义的吗?
当一个 persist 足够 merge 做了相当多的工作时,使用 merge 并不是一个好主意 . 之前的主题已经是discussed on StackOverflow,并且this article详细解释了这些差异,并提供了一些很好的流程图来说明问题 .
persist
merge
如果像你说的那样,我肯定会坚持 persist()
persist()
(...)我完全知道我总是创建一个新的对象实例来保持(...)
这就是这个方法的全部意义 - 它将在实体已经存在的情况下保护您(并将回滚您的事务) .
如果您使用的是指定的生成器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 (a.k.a Persistence Context )关联且未映射到任何数据库表行被视为处于新(暂停)状态 .
Session
Persistence Context
要成为持久化,我们需要显式调用 EntityManager#persist 方法或使用传递持久性机制 .
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:
3 回答
当一个
persist
足够merge
做了相当多的工作时,使用merge
并不是一个好主意 . 之前的主题已经是discussed on StackOverflow,并且this article详细解释了这些差异,并提供了一些很好的流程图来说明问题 .如果像你说的那样,我肯定会坚持
persist()
这就是这个方法的全部意义 - 它将在实体已经存在的情况下保护您(并将回滚您的事务) .
如果您使用的是指定的生成器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
(a.k.aPersistence 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: