[英]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.