[英]How to persist two entities with JPA
我在我的Web應用程序中使用了JPA,但我不知道如何持久化彼此相關的兩個新實體。 這里是一個例子:
+-----------------+ +--------------------+ | Consumer | | ProfilePicture | +-----------------+ +--------------------+ | id (PK) |---| consumerId (PPK+FK)| | userName | | url | +-----------------+ +--------------------+
消費者有一個ID和其他一些值。 ProfilePicture使用使用者的ID作為其自己的主鍵和外鍵。 (由於沒有使用者,ProfilePicture將不存在,並且並非每個使用者都具有ProfilePicture)
我使用NetBeans生成實體類和會話bean(外觀)。
消費者.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不知道使用者的ID,因此實體無法持久。
它唯一有效的方法是,首先持久保存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用作外部和主要)。
這些表現在看起來像這樣:
+-----------------+ +--------------------+ | Consumer | | ProfilePicture | +-----------------+ +--------------------+ | id (PK) |_ | id (PK) | | userName | \_| consumerId (FK) | +-----------------+ | url | +--------------------+
然后, 艾倫·海伊 ( Alan Hay)告訴我, 始終封裝對關系的添加/刪除,然后可以確保正確性 ,我這樣做了:
消費者.java
public void addProfilePicture(ProfilePicture profilePicture) {
profilePicture.setConsumerId(this);
if (profilePictureCollection == null) {
this.profilePictureCollection = new ArrayList<>();
}
this.profilePictureCollection.add(profilePicture);
}
由於ProfilePicture現在具有其自己的ID,因此它已成為OneToMany關系,因此每個Consumer現在可以擁有許多個人資料圖片。 那不是我最初想要的,但是我可以忍受它:)因此,我不能只為使用者設置一個ProfilePicture,而必須將其添加到Pictures的集合中(如上所述)。
這是我實施的唯一附加方法,現在可以使用。 再次感謝你的幫助!
當持久保留關系的非所有權方的實例(包含“ mappedBy”並且在您的情況下為“消費者”)時,必須始終確保將關系的雙方都設置為具有預期的級聯工作。
您當然應該總是這樣做,以確保您的域模型正確。
Consumer c = new Consumer();
ProfilePicure p = new ProfilePicture();
c.setProfilePicture(p);//see implementation
//persist c
消費者.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<Child> children;
public Set<Child> 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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.