EJB3 and manual hierarchy persistence

2019-03-22 09:16发布

问题:

I have a legacy database, which I am using EJB3 to model. The database is in quite a poor shape, and we have certain unusual restrictions on how we insert into the DB. Now I want to model the database in a hierarchy that fits in with the DB strucuture, but I want to be able to manually insert each entity individually without the persistence manager trying to persist the entities children.

I am trying something like the following (boilerplate left out):

@Entity
@Table(name = "PARENT_TABLE")
public class Parent {
    @Id
    @Column(name = "ID")
    int id;

    @OneToMany
    List<Child> children;
}


@Entity
@Table(name = "CHILD_TABLE")
public class Child {
    @Id
    @Column(name = "ID")
    int id;   
}

Now this throws an exception:

java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST

Now I know the entity isn't marked PERSIST - I don't want the EntityManager to persist it! I want to be able to persist the parent first, and then the child - but not together. There are good reasons for wanting to do it this way, but it doesn't seem to want to play.

回答1:

Heh welcome to the hair-pulling that is JPA configuration.

In your case you have two choices:

  1. Manually persist the new object; or
  2. Automatically persist it.

To automatically persist it you need to annotate the relationship. This is a common one-to-many idiom:

@Entity
@Table(name = "PARENT_TABLE")
public class Parent {
  @Id private Long id;

  @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
  private Collection<Child> children;

  public void addChild(Child child) {
    if (children == null) {
      children = new ArrayList<Child>();
    }
    child.setParent(parent);
    children.add(child);
  }
}

@Entity
@Table(name = "CHILD_TABLE")
public class Child {
  @Id private Long id;

  @ManyToOne
  private Parent parent;

  public void setParent(Parnet parent) {
    this.parent = parent;
  }
}

Parent parent = // build or load parent
Child child = // build child
parent.addChild(child);

Because of the cascade persist this will work.

Note: You have to manage the relationship at a Java level yourself, hence manually setting the parent. This is important.

Without it you need to manually persist the object. You'll need an EntityManager to do that, in which case it is as simple as:

entityManager.persist(child);

At which point it will work correctly (assuming everything else does).

For purely child entities I would favour the annotation approach. It's just easier.

There is one gotcha I'll mention with JPA:

Parent parent = new Parent();
entityManager.persist(parent);
Child child = new Child();
parent.addChild(child);

Now I'm a little rusty on this but I believe that you may run into problems if you do this because the parent was persisted before the child was added. Be careful and check this case no matter what you do.