JPA - Non-Entity Superclasses

[Updated: Jan 7, 2018, Created: Jul 23, 2017]
A quick overview of Non-Entity Superclasses.
  • A non-entity superclass (abstract or concrete) provides only inheritance of behavior to its entity subclasses and its state is not persisted in database.
  • A superclass is treated as non-entity class if no mapping related annotations such as @Entity or @MappedSuperclass are used on the class level.
  • Any mapping related annotations on fields or methods of such superclasses are ignored.
  • As non-entity superclasses classes are not managed by JPA, they cannot be passed as arguments to methods of the EntityManager or Query, for example, we cannot use EntityManager#persist(myNonEntityClass).

Example

A Non-Entity Superclass

public abstract class Shape {
  private int x = 0;
  private int y = 0;

  public void setOrigin(int x, int y) {
      this.x = x;
      this.y = y;
  }

  public void draw() {
      System.out.println("----");
      System.out.println("Drawing:");
      //Template method pattern
      drawShape();
      System.out.printf("At (%s, %s) %n", x, y);
      System.out.println("----");
  }

  protected abstract void drawShape();
}

Entity Subclasses

@Entity
public class Circle extends Shape {
  @Id
  @GeneratedValue
  private long id;
  private int radius;
  //getters/setters
    .............
  @Override
  protected void drawShape() {
      System.out.printf("Circle, radius: %s%n", radius);
  }
    .............
}
@Entity
public class Rectangle extends Shape {
  @Id
  @GeneratedValue
  private long id;
  private int width;
  private int height;
  //getters/setters
    .............
  @Override
  protected void drawShape() {
      System.out.printf("Rectangle, dimensions: %sx%s%n", width, height);
  }
    .............
}

In above mapping, we are only interested to persist shapes without their current location (origin). On loading shapes from database, shapes will always be set to their default position.

Main class showing table mappings

public class ExampleMain {

  public static void main(String[] args) {
      EntityManagerFactory emf =
              Persistence.createEntityManagerFactory("example-unit");
      try {
          EntityManager em = emf.createEntityManager();
          nativeQuery(em, "SHOW TABLES");
          nativeQuery(em, "SHOW COLUMNS from RECTANGLE");
          nativeQuery(em, "SHOW COLUMNS from CIRCLE");
      } finally {
          emf.close();
      }
  }

  public static void nativeQuery(EntityManager em, String s) {
      System.out.printf("'%s'%n", s);
      Query query = em.createNativeQuery(s);
      List list = query.getResultList();
      for (Object o : list) {
          if (o instanceof Object[]) {
              System.out.println(Arrays.toString((Object[]) o));
          } else {
              System.out.println(o);
          }
      }
  }
}
'SHOW TABLES'
[CIRCLE, PUBLIC]
[RECTANGLE, PUBLIC]
'SHOW COLUMNS from RECTANGLE'
[ID, BIGINT(19), NO, PRI, NULL]
[HEIGHT, INTEGER(10), NO, , NULL]
[WIDTH, INTEGER(10), NO, , NULL]
'SHOW COLUMNS from CIRCLE'
[ID, BIGINT(19), NO, PRI, NULL]
[RADIUS, INTEGER(10), NO, , NULL]

A quick overview of the mapping:

Persisting and loading data

public class ExampleMain2 {

  public static void main(String[] args) throws Exception {
      EntityManagerFactory emf =
              Persistence.createEntityManagerFactory("example-unit");
      try {
          persistEntities(emf);
          runNativeQueries(emf);
          loadEntities(emf);
      } finally {
          emf.close();
      }
  }

  private static void persistEntities(EntityManagerFactory emf) throws Exception {
      System.out.println("-- Persisting entities --");
      EntityManager em = emf.createEntityManager();

      Rectangle r = new Rectangle();
      r.setWidth(200);
      r.setHeight(100);
      r.setOrigin(10,10);
      System.out.println(r);

      Circle c = new Circle();
      c.setRadius(50);
      System.out.println(c);

      em.getTransaction().begin();
      em.persist(r);
      em.persist(c);
      em.getTransaction().commit();
      em.close();
  }

  private static void runNativeQueries(EntityManagerFactory emf) {
      System.out.println("-- Native queries --");
      EntityManager em = emf.createEntityManager();
      ExampleMain.nativeQuery(em, "Select * from RECTANGLE");
      ExampleMain.nativeQuery(em, "Select * from CIRCLE");
  }

  private static void loadEntities(EntityManagerFactory emf) {
      System.out.println("-- Loading and drawing shapes --");
      EntityManager em = emf.createEntityManager();
      System.out.println("Loading from Rectangle:");
      List<Shape> shapes = em.createQuery("Select t from Rectangle t")
                                 .getResultList();
      shapes.forEach(System.out::println);
      for (Shape shape : shapes) {
          shape.setOrigin(5, 10);
          shape.draw();
      }

      System.out.println("Loading from Circle:");
       shapes = em.createQuery("Select t from Circle t")
                                 .getResultList();
      shapes.forEach(System.out::println);
      for (Shape shape : shapes) {
          shape.setOrigin(10, 10);
          shape.draw();
      }

      em.close();
  }
}
-- Persisting entities --
Rectangle{id=0, width=200, height=100}
Circle{id=0, radius=50}
-- Native queries --
'Select * from RECTANGLE'
[1, 100, 200]
'Select * from CIRCLE'
[2, 50]
-- Loading and drawing shapes --
Loading from Rectangle:
Rectangle{id=1, width=200, height=100}
----
Drawing:
Rectangle, dimensions: 200x100
At (5, 10)
----
Loading from Circle:
Circle{id=2, radius=50}
----
Drawing:
Circle, radius: 50
At (10, 10)
----

Example Project

Dependencies and Technologies Used :

  • h2 1.4.196: H2 Database Engine.
  • hibernate-core 5.2.10.Final: The core O/RM functionality as provided by Hibernate.
  • JDK 1.8
  • Maven 3.3.9

JPA Non Entity Superclass Example Select All Download
  • non-entity-super-class
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
          • META-INF

See Also