如何使用JPA保留两个实体

时间:2014-10-06 15:02:50

标签: java jpa javabeans

我在我的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,但必须将它添加到图片集合(如上所述)。

这是我实施的唯一附加方法,现在它可以正常工作。再次感谢您的帮助!

2 个答案:

答案 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);
相关问题