我在我的webapp中使用JPA,我无法弄清楚如何持久保存两个彼此相关的新实体。这是一个例子:
+-----------------+ +--------------------+ | Consumer | | ProfilePicture | +-----------------+ +--------------------+ | id (PK) |---| consumerId (PPK+FK)| | userName | | url | +-----------------+ +--------------------+
消费者有 id 和其他一些值。 ProfilePicture 使用消费者的 id 作为自己的主键和外键。 (因为如果没有消费者,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 创建消费者时,我想我会这样做:
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,因此实体无法持久存在。
它唯一有效的方法是首先坚持消费者,将其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告诉我始终封装添加/删除关系,然后你可以确保正确,我做了:
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,但必须将它添加到图片集合(如上所述)。
这是我实施的唯一附加方法,现在它可以正常工作。再次感谢您的帮助!
答案 0 :(得分:2)
当持久存在关系的非拥有方的实例(包含'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<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;
}
答案 1 :(得分:0)
您可以一次保留一个对象及其子对象。所以我认为这应该有效:
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);