到目前为止,我的偏好是始终使用 EntityManagermerge()
来处理插入和更新。但我也注意到,merge 在更新/插入之前执行了一个额外的选择查询,以确保数据库中不存在记录。
现在我正在处理一个需要大量(批量)插入数据库的项目。从性能的角度来看,在我绝对知道我总是在创建要持久化的对象的新实例的情况下使用持久化而不是合并是否有意义?
到目前为止,我的偏好是始终使用 EntityManagermerge()
来处理插入和更新。但我也注意到,merge 在更新/插入之前执行了一个额外的选择查询,以确保数据库中不存在记录。
现在我正在处理一个需要大量(批量)插入数据库的项目。从性能的角度来看,在我绝对知道我总是在创建要持久化的对象的新实例的情况下使用持久化而不是合并是否有意义?
merge
当 apersist
足够时使用它不是一个好主意-merge
做更多的工作。这个话题之前已经在 StackOverflow 上讨论过,这篇文章详细解释了它们之间的区别,并用一些漂亮的流程图来说明问题。
persist()
如果如您所说,我肯定会坚持下去:
(...) 我绝对知道我总是在创建要持久化的对象的新实例 (...)
这就是这个方法的全部意义——它会在实体已经存在的情况下保护你(并且会回滚你的事务)。
如果您使用分配的生成器,使用merge
而不是persist
可能会导致冗余 SQL 语句,从而影响性能。
此外,为托管实体调用合并也是一个错误,因为托管实体由 Hibernate 自动管理,并且它们的状态在刷新 Persistence Context时通过脏检查机制与数据库记录同步。
要了解这一切是如何工作的,您首先应该知道 Hibernate 将开发人员的思维方式从 SQL 语句转变为实体状态转换。
一旦实体由 Hibernate 主动管理,所有更改都将自动传播到数据库。
Hibernate 监视当前附加的实体。但是要使实体成为受管实体,它必须处于正确的实体状态。
首先,我们必须定义所有实体状态:
新(瞬态)
从未与 Hibernate Session
(aka Persistence Context
) 关联并且未映射到任何数据库表行的新创建的对象被视为处于 New (Transient) 状态。
要持久化,我们需要显式调用该EntityManager#persist
方法或使用传递持久性机制。
持久(托管)
持久性实体已与数据库表行相关联,并由当前运行的持久性上下文管理。对此类实体所做的任何更改都将被检测到并传播到数据库(在会话刷新期间)。使用 Hibernate,我们不再需要执行 INSERT/UPDATE/DELETE 语句。Hibernate 采用事务性后写Session
工作方式,并且在当前刷新时间期间的最后一个负责时刻同步更改。
分离式
一旦当前运行的持久性上下文关闭,所有以前管理的实体都将被分离。将不再跟踪连续的更改,也不会发生自动数据库同步。
要将分离的实体关联到活动的 Hibernate Session,您可以选择以下选项之一:
重新连接
Hibernate(但不是 JPA 2.1)支持通过 Session#update 方法重新附加。Hibernate Session 只能为给定的数据库行关联一个实体对象。这是因为持久性上下文充当内存缓存(一级缓存),并且只有一个值(实体)与给定的键(实体类型和数据库标识符)相关联。仅当没有其他 JVM 对象(匹配相同的数据库行)与当前 Hibernate Session 关联时,才能重新附加实体。
合并
合并会将分离的实体状态(源)复制到托管实体实例(目标)。如果合并实体在当前会话中没有等价物,则将从数据库中获取一个。即使在合并操作之后,分离的对象实例仍将继续保持分离状态。
已移除
尽管 JPA 要求只允许删除托管实体,但 Hibernate 也可以删除分离的实体(但只能通过 Session#delete 方法调用)。删除的实体仅计划删除,实际的数据库 DELETE 语句将在会话刷新期间执行。
为了更好地理解 JPA 状态转换,您可以可视化下图:
或者,如果您使用 Hibernate 特定的 API: