1

I have three entities. Both ResolvedCorpus and UnqualifiedCorpus are subclasses of Corpus to help model and address certain concerns for some Corpus.

Joined inheritance type is selected for data persistence via JPA. The entities are defined as below.

@Entity
@Table(name="corpus")
@Inheritance(strategy = InheritanceType.JOINED)
public class Corpus implements Serializable  {}

@Entity
@Table(name = "resolved_corpus")
@OnDelete(action=OnDeleteAction.CASCADE)
public class ResolvedCorpus extends Corpus {}

@Entity
@Table(name="unqualified_corpus")
@OnDelete(action=OnDeleteAction.NO_ACTION)
public class UnqualifiedCorpus extends Corpus {}

With @OnDelete annotation specified, I want to achieve the follows:

  1. when a ResolvedCorpus gets deleted via repository.delete(), the corresponding Corpus is deleted as well in Table corpus;
  2. when a UnqualifiedCorpus gets deleted via repository.delete(), the corresponding Corpus is remained.

However, @OnDelete does not work as expected. BTW, H2 is used as the embedded DB.

The testing code is copied below.

        //create corpora, one for each type: normal, resolved, unqualified.
        Corpus normal = new Corpus();
        normal.setName("normal");
        corpusRepository.save(normal);

        ResolvedCorpus resolved = new ResolvedCorpus();
        resolved.setName("resolved");
        resolved.setResolved("resolved");
        resolvedCorpusRepository.save(resolved);

        UnqualifiedCorpus unqualified = new UnqualifiedCorpus();
        unqualified.setName("unqualified");
        unqualified.setReason("unqualified");
        unqualifiedCorpusRepository.save(unqualified);

        //query
        // 3 records are expected in corpus table
        // 1 record in resolved table
        // 1 in unqualified table
        assertTrue(corpusRepository.count() == 3);
        assertTrue(resolvedCorpusRepository.count() == 1);
        assertTrue(unqualifiedCorpusRepository.count() == 1);
        resolvedCorpusRepository.findAll().forEach((item) -> {
            assertTrue(item.getName().equals("resolved"));
        });
        unqualifiedCorpusRepository.findAll().forEach((item) -> {
            assertTrue(item.getName().equals("unqualified"));
        });

        //delete
        // when deleting resolved, the delete action is expected to get cascaded to ancestor table "corpus", as OnDeleteAction suggested.
        // when deleting unqualified, the delete action is not expected to get cascaded to ancestor table "corpus", as OnDeleteAction suggested.
        resolvedCorpusRepository.deleteAll();
        unqualifiedCorpusRepository.deleteAll();
        //query
        //HOWEVER, OnDeleteAction.NO_ACTION is NOT working as expected, and this assertion is failed.
        assertTrue(corpusRepository.count() == 2);

And you can find the testing application here.

I diagnosed the DDL for table creation and row deletion. I suspect the @OnDelete is aiming to do the opposite, which means it controls how sub-entity shall behave when the base entity object gets deleted. when calling repository.delete(Resolved), the DDL generated is delete from corpus instead of delete from resolved_corpus; and for Unqualified, both delete from unqualified and delete from corpus are called in order.

I guess, the request like I have should be a reasonable feature expected from JPA, but I cannot find a proper solution after some search online.

glenlivet1986
  • 260
  • 3
  • 12
  • `@OnDelete` is poorly documented, but I suspect all it does is it affects the DDL generated for the FK columns, adding the `ON DELETE` clauses. It likely does not affect the way Hibernate deals with deleting entities at runtime. Also, I wouldn't call what you're asking reasonable. JPA tends to respect Java semantics, and in Java, there is no such thing as 'degrading an object to its base class instead of deleting it'. If parts of the object can be deleted, but others live on, you want composition, not inheritance – crizzis Aug 26 '19 at 20:38
  • @crizz: Indeed JPA generates DDL details based on the annotations, but in this case, JPA also, from my POV, opinionatedly did delete cascading without providing a configurable way as the convention, and that is what caught my curiosity. Semantically speaking, I guess "Class" might be the right tool because an unqualified corpus is still a corpus and inheriting all the methods a corpus has. I guess the JPA `@Entity` annotation slightly changes the interpretation of a class. Composition I guess may see more when one object owns another but not necessarily be the same kind. – glenlivet1986 Aug 26 '19 at 23:27

0 Answers0