【发布时间】:2014-10-06 15:02:49
【问题描述】:
我在我的 web 应用程序中使用 JPA,但我不知道如何保留两个相互关联的新实体。举个例子:
这是两个实体
+-----------------+ +--------------------+ |消费者 | |简介图片 | +-----------------+ +--------------------+ |编号 (PK) |---|消费者 ID (PPK+FK)| |用户名 | |网址 | +-----------------+ +--------------------+Consumer 有一个id 和一些其他值。 ProfilePicture 使用 Consumer 的 id 作为它自己的主键和外键。 (因为没有消费者,个人资料图片就不会存在,而且并非每个消费者都有个人资料图片)
我使用 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,因此实体无法持续存在。
它曾经工作的唯一方法是首先持久化 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 作为外部和主要)..
现在的表格如下所示:
+-----------------+ +--------------------+ |消费者 | |简介图片 | +-----------------+ +--------------------+ |编号 (PK) |_ |身份证 (PK) | |用户名 | \_|消费者 ID (FK) | +-----------------+ |网址 | +--------------------+然后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 关系,所以每个 Consumer 现在可以拥有许多个人资料图片。这不是我最初的意图,但我可以接受它:) 因此我不能只为消费者设置 ProfilePicture,而是必须将其添加到图片集合中(如上)。
这是我实现的唯一附加方法,现在它可以工作了。再次感谢您的所有帮助!
【问题讨论】:
-
如果我没看错的话 ProfilePicture 没有自动生成的 ID,所以除非你明确设置一个,否则这应该会失败。分离主键和外键是一种选择吗?
-
@ftr:我只是试图给 ProfilePicture 它自己的主键。最后,我为每个消费者获得了一个
Collection的 ProfilePictures。这还不错——尽管我知道每个消费者最多只能有一张照片。并且在调用profilePicture.setConsumerId(consumer);时似乎有效