jeudi 23 juin 2016

Testing Hibernate delete of entity with unidirectional One-To-One relationship owned by child entity

The setup is traditional Spring + Hibernate (4.3) app.

I have unidirectional relationship between two entities A and B (B is the owner of the relationship). Unlikely all examples I found, my parent entity A is not the owner of the relationship.

Here is how they look:

@Entity
@Table(name = "a")
public class A {

    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid2")
    @Type(type = "pg-uuid")    private UUID id;

    /* Some other fields non-referring B */
    /* rest omitted for simplicity */
}


@Entity
@Table(name = "b")
public class B {

    @Id
    @Type(type = "pg-uuid")
    private UUID id;

    @MapsId
    @ManyToOne()
    @OnDelete(action = OnDeleteAction.CASCADE)
    private A a;

    @Embedded
    private SomeEmbeddable someEmbeddable;

    /* rest omitted for simplicity */
}

Now when I delete A I want B to be also deleted. There is the first question:

Q1: How Hibernate will handle the cascading delete since A dosn't know of the existence of B (the unidirectional relationship B to A)? Will it be able to delete B and should it be able to?

Thinking about it I created the following test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {PersistenceConfig.class})
@Rollback(false)
@Transactional
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class Test {

    @PersistenceContext
    private EntityManager em;

    private static UUID aId = null;

    @Test
    public void test1() throws Exception {

        /* creating entities */
        A a = new A();
        A savedA = em.merge(a);

        B b = new B();
        b.setA(A);
        B savedB = em.merge(b);

        /* check entities are saved and have IDs */
        assertNotNull("Saved A should not be null.", savedA);
        assertNotNull("Saved A's ID should not be null.", savedA.getId());
        assertNotNull("Saved B should not be null.", savedB);
        assertNotNull("Saved B's ID should not be null.", savedB.getId());
        assertEquals("A and B IDs should be equal", savedA.getId(), savedB.getId());

        /* save the ID for use in the second test method */
        aId = savedA.getId();

        /* delete the parent entity (A) */
        em.remove(savedA);
    }

    /* In another test to force different transaction */
    @Test
    public void test2() throws Exception {

        /* find entities */
        A foundA = em.find(A.class, aId);
        B foundA = em.find(B.class, aId);

        assertNull("A should be null after it was deleted.", foundA);

        /* What about B ?!? */
        assertNull("Should B also gets deleted after A is deleted?", foundB);
    }
}

What happens is the first test test1() passes and the second test2 fails on the first assert "A should be null after it was deleted.".

Q2: Why A does not get deleted?

Interestingly if I ignore (@Ignore) the second test and remove the last line em.remove(savedA); then the records are persisted in the DB. Then I get the ID of A and hard-code it in the second test it passes successfully. I am misunderstanding something here.

P.S. I don't want to have A referencing B.

Aucun commentaire:

Enregistrer un commentaire