Issue
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.
Solution
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);
Answered By - SternK
Answer Checked By - Katrina (JavaFixing Volunteer)