jongusmoe
jongusmoe

Reputation: 676

Hibernate OneToMany children and cascading

I have what is becoming a pretty complex entity setup:

A -> (one-to-one, share same primary key) B -> (one-to-many) C

@Entity
public class A {
    @Id
    @Column(unique = true, nullable = false)
    private Long id;

    @MapsId
    @OneToOne
    @JoinColumn(name = "id")
    private B b;
}

@Entity
public class B {
    @Id
    @Column(unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToOne(fetch = FetchType.LAZY, optional = true)
    @PrimaryKeyJoinColumn
    private A a;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "id")
    private Set<C> c;
}  

@Entity
public class C {
    @Id
    @Column(unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long cId;

    @MapsId
    @ManyToOne
    @JoinColumn("id")
    private B b;
} 

I want to create A, B, and C at the same time. First of all, is this possible? Real quick unit test:

A a = new A();
B b = new B();

C c1 = new C();
c1.setB(b);
C c2 = new C();
c2.setB(b);

Set<C> set = new HashSet<C>(2);
set.add(c1);
set.add(c2); // works if this is commented out

b.setA(a);
b.setC(set);

a.setB(b);

crudRepo.save(a);

DataIntegrityViolationException: A different object with the same identifier value was already associated with the session

I have tried a whole load of combinations with different cascade types, different "ownedBys" and either end up with it trying to generate multiple "C"s with the same ID/primary key, or an error saying C needs to be persisted first. Apologies if I copied over some code incorrectly - tried to keep the irrelevant stuff out.

Upvotes: 2

Views: 60

Answers (1)

SternK
SternK

Reputation: 13051

Try to correct your mapping in this way:

@Entity
public class A {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(unique = true, nullable = false)
    private Long id;

    @MapsId
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id")
    private B b;
}

@Entity
public class B {
    // ...
    @OneToOne(mappedBy = "b")
    private A a;
}  

and then save your entities in this way:

A a = new A();
B b = new B();
C c1 = new C();
C c2 = new C();

Set<C> set = new HashSet<C>(2);
set.add(c1);
set.add(c2);

b.setC(set);

// sync both side of bi-directional @OneToOne association
a.setB(b);
b.setA(a)

crudRepo.save(a);

Upvotes: 1

Related Questions