JPA 中的@OneToMany 关系无法正常工作
@OneToMany Relationship in JPA doesn't work properly
你好我有以下关系:
@Entity
public class Athlete implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer athleteId;
@ManyToOne
@JoinColumn(name="closetWaiting")
private Closet closetWaiting;
....
@Entity
public class Closet implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer closetId;
@OneToMany(mappedBy="closetWaiting")
private List<Athlete> waitingList;
....
在我的 Bean 中 class 我有这个代码:
public void addAthleteIntoWaitingList(String closetId, Athlete singleAthlete ) {
Closet singleCloset=entityManager.find(Closet.class,Integer.parseInt(closetId));
List<Athlete> waitingList=singleCloset.getWaitingList();
waitingList.add(singleAthlete);
singleCloset.setWaitingList(waitingList);
singleAthlete.setClosetWaiting(singleCloset);
}
这不起作用,因为在我调用方法 addAthleteIntoWaitingList 后,table 运动员中的 CLOSETWAITING 列始终为空。
我是不是做错了什么?对解决方案有什么建议吗?
编辑:
为了理解代码背后发生的事情,我在 persistence.xml 中激活了 eclipse 的日志记录属性。所以这就是我调用该方法时得到的结果:
2019-03-26T10:37:16.862+0100|Fein: SELECT ATHLETEID, FIRSTNAME, GENDER, LASTNAME, PHONENUMBER, closetWaiting FROM ATHLETE WHERE (ATHLETEID = ?)
bind => [12]
2019-03-26T10:37:16.871+0100|Fein: SELECT CLOSETID, CLOSETRANK, GENDER, ISBROKEN, ISOCCUPIED, RESERVATIONENDED, RESERVATIONSTARTED, athleteId FROM CLOSET WHERE (CLOSETID = ?)
bind => [5]
2019-03-26T10:37:16.876+0100|Fein: SELECT CLOSETID, CLOSETRANK, GENDER, ISBROKEN, ISOCCUPIED, RESERVATIONENDED, RESERVATIONSTARTED, athleteId FROM CLOSET
这意味着 JPA 正在获取 Id=12 的 Athlete,然后找到 Id=5 的壁橱。但是随后它完全忽略了将 Athlete 添加到 waitingList 中的其余代码,并且不会将其保存到数据库中。
现在真正的问题是为什么会这样?
解决方案:
除了@Peter 的完美解决方案外,我做了以下更改,没有添加任何级联类型,更重要的是没有调用合并方法:
我改变了我的方法获取对象 Athlete 的方式。我没有将它作为参数提供,而是通过我的 EJB Bean 中的 EntityManager.find() 获取它。外观如下:
public void addAthleteIntoWaitingList(String closetId, String athleteId ) {
Closet singleCloset=entityManager.find(Closet.class, Integer.parseInt(closetId));
Athlete singleAthlete=entityManager.find(Athlete.class, Integer.parseInt(athleteId));
List<Athlete> waitingList=singleCloset.getWaitingList();
waitingList.add(singleAthlete);
singleCloset.setWaitingList(waitingList);
singleAthlete.setClosetWaiting(singleCloset);
}
任何人都可以向我解释为什么这个有效吗?我猜是因为我在交易中操作了一个 Ahtlete 对象?
没有@OneToMany(cascade=CascadeType.ALL)
如果保存父实体则不会保存集合。
如果您在事务中更改实体 属性,则无需显式调用合并。如果实体在事务之外,它会被分离。分离的实体是 singleAthlete
。需要合并。 事务由退出最后一个 EJB 方法的容器自动提交。
显式调用 entityManager.merge(壁橱)。
我认为在将 "singleAthlete" 添加到列表之前,您必须将其保存到数据库中,因此在 waitingList.add(singleAthlete) 之前尝试:
entityManager.persist(单身运动员);
当然不要忘记合并 singleCloset 并提交结果:所以你最终调用:
entityManager.merge(单壁橱);
entityManager.getTransaction().commit();
你好我有以下关系:
@Entity
public class Athlete implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer athleteId;
@ManyToOne
@JoinColumn(name="closetWaiting")
private Closet closetWaiting;
....
@Entity
public class Closet implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer closetId;
@OneToMany(mappedBy="closetWaiting")
private List<Athlete> waitingList;
....
在我的 Bean 中 class 我有这个代码:
public void addAthleteIntoWaitingList(String closetId, Athlete singleAthlete ) {
Closet singleCloset=entityManager.find(Closet.class,Integer.parseInt(closetId));
List<Athlete> waitingList=singleCloset.getWaitingList();
waitingList.add(singleAthlete);
singleCloset.setWaitingList(waitingList);
singleAthlete.setClosetWaiting(singleCloset);
}
这不起作用,因为在我调用方法 addAthleteIntoWaitingList 后,table 运动员中的 CLOSETWAITING 列始终为空。
我是不是做错了什么?对解决方案有什么建议吗?
编辑:
为了理解代码背后发生的事情,我在 persistence.xml 中激活了 eclipse 的日志记录属性。所以这就是我调用该方法时得到的结果:
2019-03-26T10:37:16.862+0100|Fein: SELECT ATHLETEID, FIRSTNAME, GENDER, LASTNAME, PHONENUMBER, closetWaiting FROM ATHLETE WHERE (ATHLETEID = ?)
bind => [12]
2019-03-26T10:37:16.871+0100|Fein: SELECT CLOSETID, CLOSETRANK, GENDER, ISBROKEN, ISOCCUPIED, RESERVATIONENDED, RESERVATIONSTARTED, athleteId FROM CLOSET WHERE (CLOSETID = ?)
bind => [5]
2019-03-26T10:37:16.876+0100|Fein: SELECT CLOSETID, CLOSETRANK, GENDER, ISBROKEN, ISOCCUPIED, RESERVATIONENDED, RESERVATIONSTARTED, athleteId FROM CLOSET
这意味着 JPA 正在获取 Id=12 的 Athlete,然后找到 Id=5 的壁橱。但是随后它完全忽略了将 Athlete 添加到 waitingList 中的其余代码,并且不会将其保存到数据库中。
现在真正的问题是为什么会这样?
解决方案:
除了@Peter 的完美解决方案外,我做了以下更改,没有添加任何级联类型,更重要的是没有调用合并方法:
我改变了我的方法获取对象 Athlete 的方式。我没有将它作为参数提供,而是通过我的 EJB Bean 中的 EntityManager.find() 获取它。外观如下:
public void addAthleteIntoWaitingList(String closetId, String athleteId ) {
Closet singleCloset=entityManager.find(Closet.class, Integer.parseInt(closetId));
Athlete singleAthlete=entityManager.find(Athlete.class, Integer.parseInt(athleteId));
List<Athlete> waitingList=singleCloset.getWaitingList();
waitingList.add(singleAthlete);
singleCloset.setWaitingList(waitingList);
singleAthlete.setClosetWaiting(singleCloset);
}
任何人都可以向我解释为什么这个有效吗?我猜是因为我在交易中操作了一个 Ahtlete 对象?
没有@OneToMany(cascade=CascadeType.ALL)
如果保存父实体则不会保存集合。
如果您在事务中更改实体 属性,则无需显式调用合并。如果实体在事务之外,它会被分离。分离的实体是 singleAthlete
。需要合并。 事务由退出最后一个 EJB 方法的容器自动提交。
显式调用 entityManager.merge(壁橱)。
我认为在将 "singleAthlete" 添加到列表之前,您必须将其保存到数据库中,因此在 waitingList.add(singleAthlete) 之前尝试: entityManager.persist(单身运动员); 当然不要忘记合并 singleCloset 并提交结果:所以你最终调用: entityManager.merge(单壁橱); entityManager.getTransaction().commit();