AnimalImpl

Figure 1. One table per class - implementation of abstract base class
package curam.inheritance;
import curam.inheritance.Animal;
import curam.inheritance.struct.AnimalDtls;
import curam.util.persistence.EntityAdapter;
import curam.util.persistence.helper.BasePlusConcreteTableImpl;
import curam.util.type.DeepCloneable;

abstract class AnimalImpl<CONCRETE_ENTITY extends Animal,
  CONCRETE_CLASS_DTLS_STRUCT extends DeepCloneable> extends
    BasePlusConcreteTableImpl<Long, CONCRETE_ENTITY,
    AnimalDtls, CONCRETE_CLASS_DTLS_STRUCT>
    implements Animal {

  protected AnimalImpl() {
  }

  @Override
  protected void setDiscriminator(final String value) {
    setAnimalType(value);
  }

  @Override
  protected EntityAdapter<Long, AnimalDtls> getBaseEntityAdapter() {
    return new AnimalAdapter();
  }

  public String getName() {
    return getBaseRowDtls().name;
  }

  public void setName(final String value) {
    getBaseRowDtls().name = value;
  }

  protected void setAnimalType(final String value) {
    getBaseRowDtls().animalType = value;
  }
}

There are a number of important features of this implementation which are explained below.

Class declaration

abstract class AnimalImpl<CONCRETE_ENTITY extends Animal,
  CONCRETE_CLASS_DTLS_STRUCT extends DeepCloneable> extends
  BasePlusConcreteTableImpl<Long, CONCRETE_ENTITY, AnimalDtls,
    CONCRETE_CLASS_DTLS_STRUCT>

The implementation class extends the helper class BasePlusConcreteTableImpl, which provides support for simple two-level class hierarchies (such as the one in this example).

BasePlusConcreteTableImpl is parameterized with the key type, the concrete entity interface and the Dtls structs used to store the abstract and base rows. AnimalImpl can directly supply two of these parameters (namely Long and AnimalDtls), but the name of the concrete interface and Dtls struct must be specified by the subclass implementations, and so the Animal class takes these types as parameters.

The class is package-protected and marked abstract. In this example the subclasses will be placed in the same code-package; if you require some of your subclasses to be in a different package, you will need to mark your abstract implementation class public.

The class implements the Animal interface; note that the class implements only some of the methods required by the interface, leaving others to the subclass implementation, e.g:
  • AnimalImpl provides an implementation for getName and setName, as the behavior is identical for all Animal instances; but
  • AnimalImpl does not provide an implementation for speak, as the behavior will differ between Cat and Dog instances.

Protected constructor

protected AnimalImpl() {
}

Store discriminator value

@Override
  protected void setDiscriminator(final String value) {
    setAnimalType(value);
  }

The class must override the BasePlusConcreteTableImpl.setDiscriminator method to store the discriminator in an appropriate column (in this example the animalType column). A protected setter is used to set the column value.

Base entity adapter

@Override
  protected EntityAdapter<Long, AnimalDtls> getBaseEntityAdapter() {
    return new AnimalAdapter();
  }

The class must override the BasePlusConcreteTableImpl.getBaseEntityAdapter method to provider an entity adapter for retrieving and storing the database row for the base class.

Getters and Setters

The getters and setters make use of the BasePlusConcreteTableImpl.getBaseRowDtls to retrieve the Dtls struct for the base row (in this example an AnimalDtls struct).