D-FENS
D-FENS

Reputation: 1501

jpa cascading deletes reverse relationship

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

Answers (2)

D-FENS
D-FENS

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

Chris
Chris

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

Related Questions