如何使用JPA持久化两个实体

我在我的webapp中使用JPA,我无法弄清楚如何持久保存两个彼此相关的新实体。 这是一个例子:

这是两个实体

 + ----------------- + + -------------------- +
 | 消费者|  |  ProfilePicture |
 + ----------------- + + -------------------- +
 |  id(PK)| --- |  consumerId(PPK + FK)|
 |  userName |  | url|
 + ----------------- + + -------------------- +

消费者有一个id和一些其他值。 ProfilePicture使用Consumerid作为它自己的主键和外键。 (因为如果没有消费者,ProfilePicture将不存在,并且不是每个消费者都有ProfilePicture)

我使用NetBeans生成实体类和会话bean(外观)。

简而言之,这就是它们的样子

Consumer.java

@Entity @Table(name = "Consumer") @NamedQueries({...}) public class Consumer implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "id") private Integer id; @Basic(optional = false) @NotNull @Size(min = 1, max = 50) @Column(name = "userName") private String userName; @OneToOne(cascade = CascadeType.ALL, mappedBy = "consumer") private ProfilePicture profilePicture; /* and all the basic getters and setters */ (...) } 

ProfilePicture.java

 @Entity @Table(name = "ProfilePicture") @XmlRootElement @NamedQueries({...}) public class ProfilePicture implements Serializable { @Id @Basic(optional = false) @NotNull @Column(name = "consumerId") private Integer consumerId; @Basic(optional = false) @NotNull @Size(min = 1, max = 255) @Column(name = "url") private String url; @JoinColumn(name = "consumerId", referencedColumnName = "id", insertable = false, updatable = false) @OneToOne(optional = false) private Consumer consumer; /* and all the basic getters and setters */ (...) } 

因此,当我想用​​他的ProfilePicture创建一个Consumer时 ,我想我会这样做:

  ProfilePicture profilePicture = new ProfilePicture("http://www.url.to/picture.jpg"); // create the picture object Consumer consumer = new Consumer("John Doe"); // create the consumer object profilePicture.setConsumer(consumer); // set the consumer in the picture (so JPA can take care about the relation consumerFacade.create(consumer); // the facade classes to persist the consumer profilePictureFacade.create(profilePicture); // and when the consumer is persisted (and has an id) persist the picture 

我的问题

我尝试了几乎所有组合中的所有内容,但JPA似乎无法自己链接这两个实体。 大多数时候我遇到这样的错误:

  EJB5184:A system exception occurred during an invocation on EJB ConsumerFacade, method: public void com.me.db.resources.bean.ConsumerFacade.create(com.mintano.backendclientserver.db.resources.entity.Consumer) (...) Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'. Please refer to embedded ConstraintViolations for details. 

因为我理解这个问题,这是因为ProfilePicture不知道Consumer的id,因此实体不能持久化。

它唯一有效的方法是首先保持消费者 ,将其设置为ProfilePicture ,然后保持图片:

  ProfilePicture profilePicture = new ProfilePicture("http://www.url.to/picture.jpg"); // create the picture object Consumer consumer = new Consumer("John Doe"); // create the consumer object consumerFacade.create(consumer); // the facade classes to persist the consumer profilePicture.setConsumerId(consumer.getId()); // set the consumer's new id in the picture profilePictureFacade.create(profilePicture); // and when the consumer is persisted (and has an id) persist the picture 

然而,这两个表只是一个例子,自然数据库要复杂得多,并且像这样手动设置ID似乎非常不灵活,我害怕过于复杂化。 特别是因为我不能在一个事务中持久存在所有实体(这看起来非常低效)。

我做得对吗? 或者还有另一种更标准的方法吗?

编辑:我的解决方案

正如FTR建议的那样,一个问题是ProfilePicture表缺少id (我使用Consumer.id作为外部和主要)。

这些表现在看起来像这样:

 + ----------------- + + -------------------- +
 | 消费者|  |  ProfilePicture |
 + ----------------- + + -------------------- +
 |  id(PK)| _ |  id(PK)|
 |  userName |  \ _ |  consumerId(FK)|
 + ----------------- + | url|
                       + -------------------- +

然后Alan Hay告诉我始终封装添加/删除关系,然后你可以确保正确性 ,我做了:

Consumer.java

 public void addProfilePicture(ProfilePicture profilePicture) { profilePicture.setConsumerId(this); if (profilePictureCollection == null) { this.profilePictureCollection = new ArrayList(); } this.profilePictureCollection.add(profilePicture); } 

由于ProfilePicture现在拥有自己的id,因此它成为OneToMany关系,因此每个消费者现在可以拥有许多个人资料图片。 这不是我最初想要的,但我可以用它生活:)因此我不能只为消费者设置一个ProfilePicture,但必须将它添加到图片集合(如上所述)。

这是我实施的唯一额外方法,现在它可以工作。 再次感谢你的帮助!

当持久化关系的非拥有方的实例(包含’mappedBy’并且在您的情况下为Consumer)时,您必须始终确保关系的两侧都设置为按预期方式进行级联工作。

当然,您应始终执行此操作以确保您的域模型正确无误。

 Consumer c = new Consumer(); ProfilePicure p = new ProfilePicture(); c.setProfilePicture(p);//see implementation //persist c 

Consumer.java

  @Entity @Table(name = "Consumer") @NamedQueries({...}) public class Consumer implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "id") private Integer id; @Basic(optional = false) @NotNull @Size(min = 1, max = 50) @Column(name = "userName") private String userName; @OneToOne(cascade = CascadeType.ALL, mappedBy = "consumer") private ProfilePicture profilePicture; public void setProfilePicture(ProfilePicture profilePicture){ //SET BOTH SIDES OF THE RELATIONSHIP this.profilePicture = profilePicture; profilePicture.setConsumer(this); } } 

始终将添加/删除封装到关系中,然后您可以确保正确性:

 public class Parent{ private Set children; public Set getChildren(){ return Collections.unmodifiableSet(children); //no direct access:force clients to use add/remove methods } public void addChild(Child child){ child.setParent(this); children.add(child); } public class Child(){ private Parent parent; } 

您可以一次保留一个对象及其子对象。 所以我认为这应该有效:

 ProfilePicture profilePicture = new ProfilePicture("http://www.url.to/picture.jpg"); // create the picture object Consumer consumer = new Consumer("John Doe"); // create the consumer object consumer.setProfilePicture(profilePicture); consumerFacade.create(consumer);