13

I have 3 classes corresponding to 3 tables V, D and P. D had a FK to V (FK_V) and is join using OneToMany relationship. Also their exits a 4th table V_D_P which has the relationship of these V, D and P.

Following is what the data model for these looks like:

@Entity
@Table(name = "V")                                                   
public class V {

     @Id
     @GeneratedValue(strategy = GenerationType.SEQUENCE)
     @Column(name = "ID")
     private Long id;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name = "FK_V", referencedColumnName="Id", nullable = false)
    private Set<D> d;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name = "FK_V", referencedColumnName="Id", nullable = false)
    private Set<V_D_P> vdp;

    //Getters Setters etc.
}


@Entity
@Table(name = "V_D_P")
public class V_D_P {

      @Id
      @GeneratedValue(strategy = GenerationType.SEQUENCE)
      @Column(name = "ID")
      private Long id;

     @ManyToOne(cascade=CascadeType.ALL)
     @JoinColumn(name = "FK_D", nullable = false)
     private D d;

     @ManyToOne(cascade=CascadeType.ALL)
     @JoinColumn(name = "FK_P", nullable = false)
     private P p;

     //Getters Setters etc.       
}

@Entity
@Table(name = "D")                                                   
public class D {
      @Id
      @GeneratedValue(strategy = GenerationType.SEQUENCE)
      @Column(name = "ID")
      private Long id;

    //Getters Setters etc.
}

@Entity
@Table(name = "P")                                                  
public class P {

      @Id
      @GeneratedValue(strategy = GenerationType.SEQUENCE)
      @Column(name = "ID")
      private Long id;

    //Getters Setters etc.
}

Now I want to persist V, D and P along with their relationship. I am

V v = new V();

D d = new D();
Set<D> dSet = new HashSet<>();
dSet.add(d);
v.setD(dSet); // Adding d to V ....(1)

P p = new P();
V_D_P vdp = new V_D_P();

vdp.setD(d); // Adding d to V_D_P ....(2)
vdp.setP(p);

Set<V_D_P> vdpSet = new HashSet<V_D_P>();
vdpSet.add(vdp);
v.setVdp(vdpSet);

entityManager.persist(v);

Now you can see the I am adding the same d object twice. Once to P and once to V_D_P. However since these are the same objects, only once of them should persist. However based on the hibernate logs I see that hibernate is trying to insert 2 different objects.

I also see the following exception: ORA-00001: unique constraint

Is there a way to let hibernate that these are the same objects and to persis them only once ?

Suraj Menon
  • 1,486
  • 3
  • 28
  • 50
  • 1
    I couldn't reproduce your issue; only one insert statement for `D` is generated. Are all annotations from `javax.persistence.*` package? – Dragan Bozanovic Aug 10 '15 at 11:27
  • 1
    @DraganBozanovic : Yes. Both all annotations are from javax.persistence.* package. Can you conform that you are adding the same obj twice ( like dSet.add(d); & vdp.setD(d); ) and then trying to persist both ? – Suraj Menon Aug 10 '15 at 12:43
  • 1
    Yes, I copy-pasted the code you posted. [Here](http://pastie.org/10341717) is the Hibernate generated SQL; I've tried it with H2 database (`org.hibernate.dialect.H2Dialect`). Which dialect do you use and can you post the SQL generated by Hibernate in your case? – Dragan Bozanovic Aug 10 '15 at 13:19
  • 1
    Can you fix your mapping on V.vdp (it seems it has been copy-pasted from V.d). – Xavier Dury Aug 12 '15 at 05:38

3 Answers3

6

Objects are uniquely identified by id. Therefore they have to have the same id.

  • Either provide the id before,
  • or persist once and refresh d object before saving the other (so it has the id set).
darijan
  • 9,725
  • 25
  • 38
2

Try save() instead of persist(). As noted in this post, persist doesn't necessarily update the ID of the object immediately. That means that hibernate may encounter your 'd' object twice as is traverses the object graph. Both times it might say "the id is null, I need to insert a new one!".

Apparently this behavior isn't well defined, so it may not happen in all cases.

T.D. Smith
  • 984
  • 2
  • 7
  • 22
1

Do you really need the VDP.d field: can't you just use a bi-directional (mapped) relation between V and V(D)P (V(D)P would have a v field) and just navigate V(D)P.v.d?

Xavier Dury
  • 1,530
  • 1
  • 16
  • 23