Reputation: 1501
My question is about cascading deletes with JPA and Eclipselink.
I would like to model a simple relationship between two entities: A and B. B references A through a property ref2a (in DB terms B.ref2a is connected to A.id through a foreign key with "ON DELETE CASCADE"). My goal is when an A object is deleted to cascade the delete to all B objects that reference it.
I searched a lot, but I cannot make it work. Most solutions I have found are for the opposite situation: A contains a collection of references to B. This works like a charm. But if the reference is on the B side, I don't know how to do it.
Here is the Code sample:
@Entity
public class A
{
@Id
@GeneratedValue
private Integer id;
private String name;
// ...
}
@Entity
public class B
{
@Id
@GeneratedValue
private Integer id;
private String name;
@OneToOne
@JoinColumn(
foreignKey=@ForeignKey(
foreignKeyDefinition="FOREIGN KEY ref2a REFERENCES A id ON DELETE CASCADE"
)
)
private A ref2a;
// ...
}
And the test code:
public class CascadeTest extends TestCase
{
private EntityManagerFactory emf;
private EntityManager em;
@Override
protected void setUp() throws Exception {
emf = Persistence.createEntityManagerFactory("myDB");
em = emf.createEntityManager();
}
@Override
protected void tearDown() throws Exception {
em.close();
emf.close();
}
public void testApp()
{
Integer aid = -1, bid = -1;
try {
em.getTransaction().begin();
A a = new A();
a.setName("My name is A");
B b = new B();
b.setRef2a(a);
b.setName("My name is B, please delete me when A is gone.");
em.persist(a);
em.persist(b);
em.getTransaction().commit();
aid = a.getId();
bid = b.getId();
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
try {
em.getTransaction().begin();
B b = em.find(B.class, bid);
assertNotNull(b);
assertEquals("My name is B, please delete me when A is gone.", b.getName());
assertEquals("My name is A", b.getRef2a().getName());
assertEquals(aid, b.getRef2a().getId());
A a = em.find(A.class, aid);
assertEquals("My name is A", a.getName());
em.remove(a);
em.getTransaction().commit();
em.getTransaction().begin();
// a should have been removed.
// This passes OK.
a = em.find(A.class, aid);
assertNull(a);
// Cascading deletes should have deleted also b.
b = em.find(B.class, bid);
// PROBLEM: This fails - b is still here.
assertNull(b);
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
}
}
Upvotes: 2
Views: 755
Reputation: 1501
I have solved my problem. Really really simple - my initial code was almost right. I just had a syntax problem in the foreign key cascade. The attributes needed to be in brackets "()", I had overlooked that in the documentation. So the change I needed to do is:
@OneToOne
@JoinColumn(
foreignKey=@ForeignKey(
foreignKeyDefinition="FOREIGN KEY (ref2a) REFERENCES A (id) ON DELETE CASCADE"
)
)
private A ref2a;
Please notice the brackets around the two attributes.
This works, deleting an A object also cascades its linked B objects.
Thanks to everybody for your help!
Upvotes: 2
Reputation: 21145
EclipseLink provides a @CascadeOnDelete annotation that aligns with database "ON DELETE CASCADE" contraint. This annotation tells EclipseLink that the entity will be deleted by the database foriegn key constraint when this entity is deleted, and if using DDL, EclipseLink will generate the table with the proper constraint. see https://wiki.eclipse.org/EclipseLink/Examples/JPA/DeleteCascade for details.
I think though that you can get by with a simple cascade delete on the FriendshipRelation.person mapping:
@Entity
public class FriendshipRelation {
..
@OneToOne(cascade=CascadeType.REMOVE)
private Person person;
This will force JPA to remove any referenced person when the FriendshipRelation instance is removed.
Upvotes: -1