gpt4 book ai didi

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

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:29:00 25 4
gpt4 key购买 nike

我在我的 web 应用程序中使用 JPA,但我不知道如何保留两个相互关联的新实体。这里有一个例子:

这是两个实体

+-----------------+   +--------------------+|     Consumer    |   |   ProfilePicture   |+-----------------+   +--------------------+| id    (PK)      |---| consumerId (PPK+FK)|| userName        |   | url                |+-----------------+   +--------------------+

The Consumer has an id and some other values. The ProfilePicture uses the Consumer's id as it's own primary key and as foreign key. (Since a ProfilePicture will not exist without a Consumer and not every Consumer has a ProfilePicture)

I used NetBeans to generate the entity classes and the session beans (facades).

This is how they look like in short

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 作为外部和主要)。

表格现在看起来像这样:

+-----------------+   +--------------------+|     Consumer    |   |   ProfilePicture   |+-----------------+   +--------------------+| id    (PK)      |_  | id (PK)            || userName        | \_| consumerId (FK)    |+-----------------+   | url                |                      +--------------------+

Then Alan Hay told me to Always encapsulate add/remove to relationships and then you can ensure correctness, which I did:

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,而是必须将它添加到图片集合中(如上所述)。

这是我实现的唯一附加方法,现在可以使用了。再次感谢您的帮助!

最佳答案

当持久化关系的非拥有方的实例时(包含“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;
}

关于java - 如何使用 JPA 持久化两个实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26219099/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com