JPA 处理关系的 merge()
JPA handle merge() of relationship
我有一个单向关系 Project -> ProjectType
:
@Entity
public class Project extends NamedEntity
{
@ManyToOne(optional = false)
@JoinColumn(name = "TYPE_ID")
private ProjectType type;
}
@Entity
public class ProjectType extends Lookup
{
@Min(0)
private int progressive = 1;
}
请注意,没有级联。
现在,当我插入一个新项目时,我需要递增类型。
这是我在 EJB 中所做的,但我不确定这是最好的方法:
public void create(Project project)
{
em.persist(project);
/* is necessary to merge the type? */
ProjectType type = em.merge(project.getType());
/* is necessary to set the type again? */
project.setType(type);
int progressive = type.getProgressive();
type.setProgressive(progressive + 1);
project.setCode(type.getPrefix() + progressive);
}
我正在使用 eclipselink 2.6.0,但我想知道是否有独立于实现的最佳实践and/or如果持久性提供程序之间存在行为差异,关于这个特定场景。
更新
在进入 EJB 创建方法时阐明上下文(它由 JSF @ManagedBean
调用):
project.projectType
是 DETACHED
project
是 NEW
- 没有交易(我正在使用 JTA/CMT)处于活动状态
我不是在问 persist()
和 merge()
之间的区别,我是在问
- if
em.persist(project)
自动 "reattach" project.projectType
(我想不会)
- 如果 调用顺序 合法:首先
em.persist(project)
然后 em.merge(projectType)
或者如果它应该反转
- 因为
em.merge(projectType)
returns不同的实例,如果需要调用project.setType(managedProjectType)
对 "why" 这在某种程度上起作用而不是另一种方式的解释也很受欢迎。
在此代码中。合并基本上将记录存储在不同的对象中,比方说
一个帐户 pojo 在那里
帐号帐号=null;
账户 = entityManager.merge(账户);
然后你可以存储这个结果。
但是在您的代码中,您使用的是合并不同的条件,例如
public无效创建(项目项目)
{
em.persist(项目);
/* is necessary to merge the type? */
ProjectType type = em.merge(project.getType());
}
这里
Project 和 ProjectType 是两个不同的 pojo,您可以对同一个 pojo 使用合并。
或者在你的pojo中有什么关系那么你也可以使用它。
需要合并吗?这要看情况。根据 merge() javadoc:
Merge the state of the given entity into the current persistence
context
您是如何获得附加到项目的 ProjectType 实例的?如果该实例已经被管理,那么您需要做的只是
type.setProgessive(type.getProgressive() + 1)
并且 JPA 将在下一次上下文刷新时自动发布更新。
否则,如果该类型不受管理,则需要先将其合并。
虽然没有直接关系,但这个问题对持久化与合并有一些很好的见解:JPA EntityManager: Why use persist() over merge()?
有了 em.persist(project)
vs em.merge(projectType)
的调用顺序,你可能应该问问自己,如果类型在数据库中消失了会发生什么?如果您首先合并类型,它将被重新插入,如果您首先保留项目并且您有 FK 约束,插入将失败(因为它不是级联的)。
您只需要 merge(...)
来创建由您的实体管理器管理的临时实体。根据 JPA 的实现(不确定 EclipseLink),merge
调用的 returned 实例可能是原始对象的不同副本。
MyEntity unmanaged = new MyEntity();
MyEntity managed = entityManager.merge(unmanaged);
assert(entityManager.contains(managed)); // true if everything worked out
assert(managed != unmanaged); // probably true, depending on JPA impl.
如果您调用 manage(entity)
,其中 entity
已被管理,则不会发生任何事情。
调用 persist(entity)
也将使您的实体受到管理,但它 return 没有副本。相反,它合并了原始对象,它也可能调用 ID 生成器(例如序列),使用 merge
.
时情况并非如此
有关 persist
和 merge
之间差异的更多详细信息,请参阅 this answer。
这是我的建议:
public void create(Project project) {
ProjectType type = project.getType(); // maybe check if null
if (!entityManager.contains(type)) { // type is transient
type = entityManager.merge(type); // or load the type
project.setType(type); // update the reference
}
int progressive = type.getProgressive();
type.setProgressive(progressive + 1); // mark as dirty, update on flush
// set "code" before persisting "project" ...
project.setCode(type.getPrefix() + progressive);
entityManager.persist(project);
// ... now no additional UPDATE is required after the
// INSERT on "project".
}
更新
if em.persist(project) automatically "reattach" project.projectType (I suppose not)
没有。你可能会得到一个异常(Hibernate 无论如何)说明你正在尝试与瞬态引用合并。
更正: 我用 Hibernate 测试了它,没有异常。该项目是使用非托管项目类型创建的(在保留项目之前先进行托管然后分离)。 但是项目类型的progression
没有增加,正如预期的那样,因为它没有被管理。所以是的,在坚持项目之前管理它。
if it is legal the call order: first em.persist(project) then em.merge(projectType) or if it should be inverted
最好这样做。但是当这两个语句在同一批中执行时(在实体管理器被刷新之前)它甚至可能工作(在持久化项目之后合并类型)。在我的测试中它仍然有效。但正如我所说,最好在保留新实体之前合并实体。
since em.merge(projectType) returns a different instance, if it is required to call project.setType(managedProjectType)
是的。请参见上面的示例。持久性提供程序 可以 return 相同的引用,但这不是必需的。所以为了确定,调用 project.setType(mergedType)
.
我有一个单向关系 Project -> ProjectType
:
@Entity
public class Project extends NamedEntity
{
@ManyToOne(optional = false)
@JoinColumn(name = "TYPE_ID")
private ProjectType type;
}
@Entity
public class ProjectType extends Lookup
{
@Min(0)
private int progressive = 1;
}
请注意,没有级联。
现在,当我插入一个新项目时,我需要递增类型。
这是我在 EJB 中所做的,但我不确定这是最好的方法:
public void create(Project project)
{
em.persist(project);
/* is necessary to merge the type? */
ProjectType type = em.merge(project.getType());
/* is necessary to set the type again? */
project.setType(type);
int progressive = type.getProgressive();
type.setProgressive(progressive + 1);
project.setCode(type.getPrefix() + progressive);
}
我正在使用 eclipselink 2.6.0,但我想知道是否有独立于实现的最佳实践and/or如果持久性提供程序之间存在行为差异,关于这个特定场景。
更新
在进入 EJB 创建方法时阐明上下文(它由 JSF @ManagedBean
调用):
project.projectType
是 DETACHEDproject
是 NEW- 没有交易(我正在使用 JTA/CMT)处于活动状态
我不是在问 persist()
和 merge()
之间的区别,我是在问
- if
em.persist(project)
自动 "reattach"project.projectType
(我想不会) - 如果 调用顺序 合法:首先
em.persist(project)
然后em.merge(projectType)
或者如果它应该反转 - 因为
em.merge(projectType)
returns不同的实例,如果需要调用project.setType(managedProjectType)
对 "why" 这在某种程度上起作用而不是另一种方式的解释也很受欢迎。
在此代码中。合并基本上将记录存储在不同的对象中,比方说 一个帐户 pojo 在那里 帐号帐号=null; 账户 = entityManager.merge(账户); 然后你可以存储这个结果。
但是在您的代码中,您使用的是合并不同的条件,例如 public无效创建(项目项目) { em.persist(项目);
/* is necessary to merge the type? */
ProjectType type = em.merge(project.getType());
} 这里 Project 和 ProjectType 是两个不同的 pojo,您可以对同一个 pojo 使用合并。 或者在你的pojo中有什么关系那么你也可以使用它。
需要合并吗?这要看情况。根据 merge() javadoc:
Merge the state of the given entity into the current persistence context
您是如何获得附加到项目的 ProjectType 实例的?如果该实例已经被管理,那么您需要做的只是
type.setProgessive(type.getProgressive() + 1)
并且 JPA 将在下一次上下文刷新时自动发布更新。
否则,如果该类型不受管理,则需要先将其合并。
虽然没有直接关系,但这个问题对持久化与合并有一些很好的见解:JPA EntityManager: Why use persist() over merge()?
有了 em.persist(project)
vs em.merge(projectType)
的调用顺序,你可能应该问问自己,如果类型在数据库中消失了会发生什么?如果您首先合并类型,它将被重新插入,如果您首先保留项目并且您有 FK 约束,插入将失败(因为它不是级联的)。
您只需要 merge(...)
来创建由您的实体管理器管理的临时实体。根据 JPA 的实现(不确定 EclipseLink),merge
调用的 returned 实例可能是原始对象的不同副本。
MyEntity unmanaged = new MyEntity();
MyEntity managed = entityManager.merge(unmanaged);
assert(entityManager.contains(managed)); // true if everything worked out
assert(managed != unmanaged); // probably true, depending on JPA impl.
如果您调用 manage(entity)
,其中 entity
已被管理,则不会发生任何事情。
调用 persist(entity)
也将使您的实体受到管理,但它 return 没有副本。相反,它合并了原始对象,它也可能调用 ID 生成器(例如序列),使用 merge
.
有关 persist
和 merge
之间差异的更多详细信息,请参阅 this answer。
这是我的建议:
public void create(Project project) {
ProjectType type = project.getType(); // maybe check if null
if (!entityManager.contains(type)) { // type is transient
type = entityManager.merge(type); // or load the type
project.setType(type); // update the reference
}
int progressive = type.getProgressive();
type.setProgressive(progressive + 1); // mark as dirty, update on flush
// set "code" before persisting "project" ...
project.setCode(type.getPrefix() + progressive);
entityManager.persist(project);
// ... now no additional UPDATE is required after the
// INSERT on "project".
}
更新
if em.persist(project) automatically "reattach" project.projectType (I suppose not)
没有。你可能会得到一个异常(Hibernate 无论如何)说明你正在尝试与瞬态引用合并。
更正: 我用 Hibernate 测试了它,没有异常。该项目是使用非托管项目类型创建的(在保留项目之前先进行托管然后分离)。 但是项目类型的progression
没有增加,正如预期的那样,因为它没有被管理。所以是的,在坚持项目之前管理它。
if it is legal the call order: first em.persist(project) then em.merge(projectType) or if it should be inverted
最好这样做。但是当这两个语句在同一批中执行时(在实体管理器被刷新之前)它甚至可能工作(在持久化项目之后合并类型)。在我的测试中它仍然有效。但正如我所说,最好在保留新实体之前合并实体。
since em.merge(projectType) returns a different instance, if it is required to call project.setType(managedProjectType)
是的。请参见上面的示例。持久性提供程序 可以 return 相同的引用,但这不是必需的。所以为了确定,调用 project.setType(mergedType)
.