EntityManager.merge()可以插入新对象并更新现有对象。
为什么要使用persist()(它只能创建新对象)?
EntityManager.merge()可以插入新对象并更新现有对象。
为什么要使用persist()(它只能创建新对象)?
当前回答
Merge不会更新传递的实体,除非该实体被管理。即使实体ID被设置为一个现有的DB记录,一个新的记录将在数据库中创建。
其他回答
如果您正在使用分配的生成器,使用merge而不是持久化可能会导致冗余SQL语句,从而影响性能。
另外,对托管实体调用merge也是一个错误,因为托管实体是由Hibernate自动管理的,并且它们的状态在刷新持久性上下文时通过脏检查机制与数据库记录同步。
要理解这一切是如何工作的,首先应该知道Hibernate将开发人员的思维模式从SQL语句转换为实体状态转换。
一旦一个实体被Hibernate主动管理,所有的更改都将自动传播到数据库中。
Hibernate监视当前附加的实体。但是要使实体受到管理,它必须处于正确的实体状态。
为了更好地理解JPA状态转换,您可以将下面的图可视化:
或者如果你使用Hibernate特定的API:
如上图所示,一个实体可以处于以下四种状态之一:
新(瞬态)
如果一个新创建的对象从未与Hibernate会话(也就是持久化上下文)关联过,也没有映射到任何数据库表行,则认为该对象处于New (Transient)状态。
要成为持久化,我们需要显式地调用EntityManager#persist方法,或者使用传递持久化机制。
Persistent (Managed) A persistent entity has been associated with a database table row and it’s being managed by the currently running Persistence Context. Any change made to such an entity is going to be detected and propagated to the database (during the Session flush-time). With Hibernate, we no longer have to execute INSERT/UPDATE/DELETE statements. Hibernate employs a transactional write-behind working style and changes are synchronized at the very last responsible moment, during the current Session flush-time. Detached
一旦当前运行的持久性上下文被关闭,所有以前管理的实体将被分离。连续的更改将不再被跟踪,也不会发生自动的数据库同步。
要将分离实体关联到活动Hibernate会话,您可以选择以下选项之一:
Reattaching Hibernate (but not JPA 2.1) supports reattaching through the Session#update method. A Hibernate Session can only associate one Entity object for a given database row. This is because the Persistence Context acts as an in-memory cache (first level cache) and only one value (entity) is associated with a given key (entity type and database identifier). An entity can be reattached only if there is no other JVM object (matching the same database row) already associated with the current Hibernate Session. Merging The merge is going to copy the detached entity state (source) to a managed entity instance (destination). If the merging entity has no equivalent in the current Session, one will be fetched from the database. The detached object instance will continue to remain detached even after the merge operation. Remove Although JPA demands that managed entities only are allowed to be removed, Hibernate can also delete detached entities (but only through a Session#delete method call). A removed entity is only scheduled for deletion and the actual database DELETE statement will be executed during Session flush-time.
您可能来这里寻求关于何时使用持久化和何时使用合并的建议。我认为这取决于具体情况:您需要创建新记录的可能性有多大,以及检索持久数据的难度有多大。
让我们假设您可以使用自然键/标识符。
Data needs to be persisted, but once in a while a record exists and an update is called for. In this case you could try a persist and if it throws an EntityExistsException, you look it up and combine the data: try { entityManager.persist(entity) } catch(EntityExistsException exception) { /* retrieve and merge */ } Persisted data needs to be updated, but once in a while there is no record for the data yet. In this case you look it up, and do a persist if the entity is missing: entity = entityManager.find(key); if (entity == null) { entityManager.persist(entity); } else { /* merge */ }
如果你没有自然的键/标识符,你将很难弄清楚实体是否存在,或者如何查找它。
合并也可以用两种方式处理:
如果更改通常很小,请将其应用到托管实体。 如果更改很常见,则从持久化实体以及未更改的数据复制ID。然后调用EntityManager::merge()替换旧的内容。
JPA规范说明了关于persist()的以下内容。
如果X是一个分离对象,则在持久化时可能会抛出EntityExistsException 操作被调用,或者EntityExistsException或另一个PersistenceException可能在刷新或提交时抛出。
因此,当对象不应该是分离对象时,使用persist()是合适的。您可能更喜欢让代码抛出PersistenceException,以便快速失败。
尽管规范不清楚,persist()可能会为对象设置@GeneratedValue @Id。但是merge()必须有一个已经生成的@Id对象。
我发现Hibernate文档中的解释很有启发性,因为它们包含了一个用例:
The usage and semantics of merge() seems to be confusing for new users. Firstly, as long as you are not trying to use object state loaded in one entity manager in another new entity manager, you should not need to use merge() at all. Some whole applications will never use this method. Usually merge() is used in the following scenario: The application loads an object in the first entity manager the object is passed up to the presentation layer some modifications are made to the object the object is passed back down to the business logic layer the application persists these modifications by calling merge() in a second entity manager Here is the exact semantic of merge(): if there is a managed instance with the same identifier currently associated with the persistence context, copy the state of the given object onto the managed instance if there is no managed instance currently associated with the persistence context, try to load it from the database, or create a new managed instance the managed instance is returned the given instance does not become associated with the persistence context, it remains detached and is usually discarded
来自:http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html
Merge不会更新传递的实体,除非该实体被管理。即使实体ID被设置为一个现有的DB记录,一个新的记录将在数据库中创建。