JPA - 插入连接表的正确方法(带有额外的列)

Mr_*_*s_D 7 jpa entitymanager eclipselink jpa-2.0

我设法为我的电影插入工作人员 - 现在我想以正确的方式做到这一点.实体(缩写):

@Entity
@Table(name = "movies")
public class Movie implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int idmovie;
    // bi-directional many-to-one association to MoviesHasCrew
    @OneToMany(mappedBy = "movy", cascade = CascadeType.PERSIST)
    private List<MoviesHasCrew> moviesHasCrews;
}

@Entity
@Table(name = "movies_has_crew")
public class MoviesHasCrew implements Serializable {
    @EmbeddedId
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private MoviesHasCrewPK id;
    // bi-directional many-to-one association to Crew
    @ManyToOne
    @JoinColumn(name = "crew_idcrew", columnDefinition = "idcrew")
    @MapsId("crewIdcrew")
    private Crew crew;
    // bi-directional many-to-one association to Movy
    @ManyToOne
    @JoinColumn(name = "movies_idmovie")
    @MapsId("moviesIdmovie")
    private Movie movy;
    // bi-directional many-to-one association to Role
    @ManyToOne
    @JoinColumn(name = "roles_idrole")
    @MapsId("rolesIdrole")
    private Role role;
}

@Entity
@Table(name = "crew")
public class Crew implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int idcrew;
    // bi-directional many-to-one association to MoviesHasCrew
    @OneToMany(mappedBy = "crew", cascade = CascadeType.PERSIST)
    private List<MoviesHasCrew> moviesHasCrews;
}
Run Code Online (Sandbox Code Playgroud)

抱歉'movy'和'crews'是工具(并且有资格获得错误报告)

控制器和表格:

@ManagedBean
@ViewScoped
public class MovieController implements Serializable {
    @EJB
    private MovieService service;
    private Crew crewMember;
    private Movie movie;

    public String addCrewMember() {
        if (movie.getIdmovie() == 0) {
            movie = (Movie) FacesContext.getCurrentInstance()
                .getExternalContext()
                .getSessionMap().get("movie");
        }
        service.addCrew(movie, crewMember);
        return null;
    }
}

<h:form id="movie_add_crew_form" rendered="#{sessionScope.movie != null}">
<h:panelGrid columns="2">
    <h:selectOneListbox id="crewMember" redisplay="true" size="8"
        value="#{movieController.crewMember}"
        converter="#{movieController$CrewConverter}">
        <f:selectItems value="#{movieController.allCrew}" var="entry"
            itemValue="#{entry}" itemLabel="#{entry.name}" />
        <f:ajax event="blur" render="crewMemberMessage" />
    </h:selectOneListbox>
    <h:message id="crewMemberMessage" for="crewMember" />
</h:panelGrid>
<h:commandButton value="Add" action="#{movieController.addCrewMember}">
    <f:ajax execute="@form" render="@form :movie_crew" />
</h:commandButton></h:form>
Run Code Online (Sandbox Code Playgroud)

最后服务:

@Stateless
public class MovieService {

    @PersistenceContext
    private EntityManager em;

    public void addCrew(Movie m, Crew w) {
        MoviesHasCrew moviesHasCrew = new MoviesHasCrew();
        moviesHasCrew.setCrew(w);
        moviesHasCrew.setMovy(m);
        moviesHasCrew.setRole(Role.DEFAUT_ROLE);
        em.persist(moviesHasCrew);
        m.addMoviesHasCrew(moviesHasCrew); // (1)
        em.merge(m); // noop
    }
}
Run Code Online (Sandbox Code Playgroud)

问题1:我希望moviesHasCrews更新Crew和Movie实体的字段以保持MoviesHasCrew 实体(即删除m.addMoviesHasCrew(moviesHasCrew); em.merge(m);),但我的级联注释似乎没有这样做.我应该反过来做吗?这是添加到moviesHasCrews电影和合并/ perist电影和MoviesHasCrew更新 - 这我读取需要休眠但我使用通用JPA - 它仍然不能在香草JPA?

问题2:应该理解应该如何做到这一点(例如我应该在字段中添加fetch=Lazy(in MovieCrew)?).连接表实体是否需要等?这是最简单/最优雅的方式吗?@TransientmoviesHasCrews@MapsId("moviesIdmovie")

架构:

在此输入图像描述

参考文献:

Chr*_*ris 3

问题是 JPA 不会为您维护双向关系的双方。当您使用具有二级缓存的 JPA 提供程序时,这一点更加明显。显而易见的原因是,当您设置关系的拥有方时(在本例中调用 movieHasCrew.setCrew(w),然后调用 em.flush()),这会导致数据库 FK 被更新。但是,如果您立即检查对象模型,您将看到引用的 Crew 成员在其集合中没有相应的 movieHasCrew 实例。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
}
Run Code Online (Sandbox Code Playgroud)

如果它们是分离的实例,则需要合并,因为对集合的更改需要放入 EntityManager 中,以便可以将其合并到缓存中。

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