JPA-插入到联接表的正确方式(带有额外列)

JPA-插入到联接表的正确方式(带有额外列),jpa,eclipselink,jpa-2.0,entitymanager,Jpa,Eclipselink,Jpa 2.0,Entitymanager,我成功地为我的电影加入了剧组,现在我想用正确的方式。实体(缩写): 问题1:我想在持久化MoviesHasCrew实体(即dropm.addMoviesHasCrew(MoviesHasCrew);em.merge(m);)时更新剧组和电影实体的字段MoviesHasCrew),但我的级联注释似乎无法做到这一点。我应该反过来做吗?这是添加到电影中的moviesHasCrews,并合并/perist电影,并更新MoviesHasCrew——我需要hibernate,但我使用通用JPA——在香草JP

我成功地为我的电影加入了剧组,现在我想用正确的方式。实体(缩写):

问题1:我想在持久化
MoviesHasCrew
实体(即drop
m.addMoviesHasCrew(MoviesHasCrew);em.merge(m);
)时更新剧组和电影实体的字段
MoviesHasCrew
),但我的级联注释似乎无法做到这一点。我应该反过来做吗?这是添加到电影中的
moviesHasCrews
,并合并/perist电影,并更新
MoviesHasCrew
——我需要hibernate,但我使用通用JPA——在香草JPA中仍然不可行吗

问题2:请详细说明应该如何做(例如,我是否应该将
fetch=Lazy
(在
Movie
Crew
)添加到
moviesHasCrews
字段中?)。联接表实体中是否需要
@MapsId(“moviesidmoine”)
etc?这是最简单/优雅的方式吗

模式:

参考资料:


我认为你不应该追求正确的道路,它不存在。就我个人而言,我不喜欢太多的级联操作。有什么好处?在这种情况下,我会使用如下内容:

服务:

public void addCrew(long movieId, long crewId) {
     Movie m = em.getReference(Movie.class, movieId);
     Crew w = em.getReference(Crew.class, crewId);
     MoviesHasCrew moviesHasCrew = new MoviesHasCrew();
     moviesHasCrew.setCrewAndMovie(w,m);
     moviesHasCrew.setRole(Role.DEFAUT_ROLE);
     em.persist(moviesHasCrew);
}
电影剧组:

public void setCrewAndMovie(Crew c, Movie m){
    this.crew = c;
    this.movie = m;
    m.addMoviesHasCrew(this);
    c.addMoviesHasCrew(this);
}
它保持可读性,级联操作有时像魔术一样工作


关于
@MapsId
:您需要它们是因为嵌入了id
MoviesHasCrewPK
。通过这种方式,嵌入id的属性被映射到
@MapsId
注释的相应值。另见。如果没有必要,我不会使用这个嵌入的id。生成的id在我看来更干净,但在联接表中有一个额外的列。

问题是JPA不能为您维护双向关系的两侧。当您使用具有二级缓存的JPA提供程序时,这一点更加明显。显而易见的原因是,当您设置关系的拥有方时(在本例中,调用moviesHasCrew.setCrew(w),然后调用em.flush()),这会导致更新数据库FK。但是,如果立即检查对象模型,您将看到引用的工作组成员在其集合中没有相应的moviesHasCrew实例。JPA不会管理您的引用并为您设置它们,因此它与数据库中的内容不同步

这应该在同一个EntityManager中实现。但是,当涉及第二级缓存时,每次查询该Crew实例时,它都会返回现在已过时的缓存副本

更新集合的唯一方法是从数据库中重新加载Crew实例。这可以通过清除缓存或强制刷新来完成

更好的选择是保持双向关系的双方,并保持它们彼此同步。在您的代码案例中,这意味着调用:

public void addCrew(Movie m, Crew w) {
    MoviesHasCrew moviesHasCrew = new MoviesHasCrew();
    moviesHasCrew.setCrew(w);
    w.addMoviesHasCrew(moviesHasCrew); 
    moviesHasCrew.setMovy(m);
    m.addMoviesHasCrew(moviesHasCrew); // (1)
    moviesHasCrew.setRole(Role.DEFAUT_ROLE);
    em.persist(moviesHasCrew);
    em.merge(m); // noop unless it is detached
    em.merge(w); // noop unless it is detached
}
如果它们是分离实例,则需要合并,因为对集合的更改需要放入EntityManager,以便可以合并到缓存中


如果这些合并是您想要避免的,您可以依靠moviesHasCrew->Movies和moviesHasCrew->Crew关系来处理它,方法是在这些关系上设置CascadeType.MERGE选项,然后使用em.MERGE(moviesHasCrew);而不是3个em呼叫。合并moviesHasCrew将使其像持久化一样插入到数据库中,但合并将级联到所有被引用实体上,关系标记为CascadeType.merge-因此被引用的工作组和电影也将被合并

如果persist()失败,第(1)行仍在执行,这是一个问题,或者它正在发生?如果persist失败,它应该抛出PersistenceException不是吗?@Koitoer:是的,但它是异步的-它发生了可能不是正确的方式,但我对这方面的一些基本代码感兴趣,就像我提供的一样,它至少包含了建立这种关系的绝对最小值和可能的扩展点。为什么要使用
em.getReference
?这样不会重新加载整个实体,只需获取其引用(id)。比较
em.find
。但我之所以不传递整个实体,只是传递id,是因为这样更安全。想象一下,如果有人修改了
剧组
电影
,而您进行了
合并
。显然,您不想更新它们,只想更新它们的关系。编辑了包含您的一些建议的问题-在某个时候,我遇到了“无法刷新非托管对象”问题。仍然对这方面的一些基本代码感兴趣,如我提供的但已注释/修改的代码。顺便说一句,一个剧组成员可以有很多角色。无法刷新是因为我认为
merge(m)
就足够了-而我需要
m=merge(m)
-将检查这个。在你的回答中-你认为
crew.addMoviesHasCrews(这个);movie.addMoviesHasCrews(这个)是不可避免的(一种方式还是另一种方式)?因为我调用了
em.merge(moviesHasCrew)应该有一种方法告诉JPA相关(加入的)实体应该更新他们的
电影列表。这就是
org.hibernate.annotations.CascadeType.SAVE\u UPDATE
所做的(请参阅)?在普通JPA中没有办法(可能在实体上添加其他注释,或者在
m,w
上执行
merge
)?您的回答还假设
w,m
实例是否由JPA管理?因为我从UI获取它们,并且据我所知JPA没有对它们进行管理(因此我的
无法刷新未管理对象
),答案中显示的映射设置了级联。在MoviesHasCrew->crew和Movie关系上进行合并,因此如果在MoviesHasCrew实例上使用合并,它级联到工作人员和电影,使他们成为管理。
public void addCrew(long movieId, long crewId) {
     Movie m = em.getReference(Movie.class, movieId);
     Crew w = em.getReference(Crew.class, crewId);
     MoviesHasCrew moviesHasCrew = new MoviesHasCrew();
     moviesHasCrew.setCrewAndMovie(w,m);
     moviesHasCrew.setRole(Role.DEFAUT_ROLE);
     em.persist(moviesHasCrew);
}
public void setCrewAndMovie(Crew c, Movie m){
    this.crew = c;
    this.movie = m;
    m.addMoviesHasCrew(this);
    c.addMoviesHasCrew(this);
}
public void addCrew(Movie m, Crew w) {
    MoviesHasCrew moviesHasCrew = new MoviesHasCrew();
    moviesHasCrew.setCrew(w);
    w.addMoviesHasCrew(moviesHasCrew); 
    moviesHasCrew.setMovy(m);
    m.addMoviesHasCrew(moviesHasCrew); // (1)
    moviesHasCrew.setRole(Role.DEFAUT_ROLE);
    em.persist(moviesHasCrew);
    em.merge(m); // noop unless it is detached
    em.merge(w); // noop unless it is detached
}