EclipseLink JPA inheritance without discriminator column

Viju Nil picture Viju Nil · Mar 14, 2014 · Viewed 8.9k times · Source

I have a Client and Affiliate class, inheriting from Person class. Joined inheritance strategy type is being used - each of them sharing primary key with the parent class. As there's no discriminator column we chose to use DescriptorCustomizer and ClassExtractor. But it doesn't really give any idea how it works, also, the code doesnt seem to compile. It would be nice if someone gives a nice example with code snippet for understanding.

Answer

wypieprz picture wypieprz · Mar 16, 2014

According to the mentioned documentation:

If you are mapping to an existing database, and the tables do not have a discriminator column you can still define inheritance using the @ClassExtractor annotation or <class-extractor> element. The class extractor takes a class that implements the ClassExtractor interface. An instance of this class is used to determine the class type to use for a database row. The class extractor must define a extractClassFromRow() method that takes the database Record and Session.

we need to annotate the root entity in a hierarchy with user defined using the class extractor:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@ClassExtractor(PersonClassExtractor.class)
public abstract class Person {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String name;
    private int age;
    // ...
}

Notice that we don't use @Customizer annotations since as this is not required in case of JOINED inheritance strategy:

If a class extractor is used with SINGLE_TABLE inheritance, the rows of the class type must be able to be filtered in queries. This can be accomplished by setting an onlyInstancesExpression() or withAllSubclassesExpression() for branch classes. These can be set to Expression objects using a DescriptorCustomizer.

The class extractor must be able to determine and return the class type from the database row. In general we need a replacement of a discriminator column, i.e.

  • column name unique for a given entity type among others
  • criteria based on values of a given column of the root entity

Suppose that each of inherited entity type in a hierarchy has a column with unique name:

@Entity
public class Client extends Person {
    @Column(name = "CLIENT_SPECIFIC")
    private String clientSpecific;
    // ...
}

@Entity
public class Affiliate extends Person {
    @Column(name = "AFFILIATE_SPECIFIC")
    private float affiliateSpecific;
    // ...
}

then class extractor may look as follows:

public class PersonClassExtractor extends ClassExtractor {
    @Override
    public Class<?> extractClassFromRow(Record databaseRow, Session session) {
        if (databaseRow.containsKey("CLIENT_SPECIFIC")) {
            return Client.class;
        } else if (databaseRow.containsKey("AFFILIATE_SPECIFIC")) {
            return Affiliate.class;
        } else {
            return Person.class; // this should never happen
        }
    }
}

  • retrieve a list of clients and affiliates
List<Person> polymorphicResults = em.createQuery("SELECT p FROM Person p")
                                    .getResultList();
  • retrieve a list of affiliates or clients respectively
List<Affiliate> concreteResults = em.createQuery("SELECT a FROM Affiliate a")
                                    .getResultList();

List<Client> concreteResults = em.createQuery("SELECT c FROM Client c")
                                 .getResultList();