In my previous blog I shared my code re-factoring experience by using Mapped Superclass. Using Abstract Entity was another interesting experience. In our project there are some Entities like Customer, Vendor which have some common attributes like 'First Name', 'Last Name', etc. These attributes were defined in both Customer and Vendor Class. This approach not only produced code duplication but also column duplication in underlying Datastore. JPA Abstract Entity feature inspires me to use a third Entity Class which is abstract to avoid this duplication.
Let's see how it works. Suppose you have two Entities Student and Teacher, and they have following attributes
Three attributes are common in this case and you can introduce a Person class which is abstract and has these common properties. Student and Teacher class will extend Person and only have their individual properties.
Your Person, Student and Teacher Entity classes will be like this
@Entity @Inheritance(strategy=InheritanceType.JOINED) @Table(name = "persons") public abstract class Person implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "id_nr") protected Long idNr; @Column(name = "first_name") protected String firstName; @Column(name = "last_name") protected String lastName; public Person() { } //getter setter of the properties ....
@Entity @Table(name = "teachers") public class Teacher extends Person { private static final long serialVersionUID = 1L; @Column(name = "designation") protected String designation; // constructor, getter setter of the property....
@Entity @Table(name = "students") public class Student extends Person { private static final long serialVersionUID = 1L; @Column(name = "role_number") protected String roleNumber; // constructor, getter setter of the property....
For above scenario three tables will be generated in the database, persons, students and teachers. Mapping strategy of @Inheritance annotation has a great impact in table generation process. As we have used InheritanceType.JOINED as the mapping strategy, three different tables are created. If we were using default mapping strategy which is InheritanceType.SINGLE_TABLE, only persons table will be created with all the attributes. In both cases Persistence provider will also create a discriminator column in the person table called DTYPE, which will store the name of the inherited entity used to create the person.
Now run the following code and see how data is stored in the database in this design.
@PersistenceContext(unitName = "TaskBoard-ejbPU") EntityManager em; Teacher teacher = new Teacher(); teacher.setFirstName("Sarwar"); teacher.setLastName("Hossain"); teacher.setDesignation("Associate Proffessor"); teacher.setIdNr(null); em.persist(teacher); Student student = new Student(); student.setFirstName("Shams"); student.setLastName("Zawoad"); student.setRoleNumber("001525"); student.setIdNr(null); em.persist(student); em.flush();
If you try with InheritanceType.SINGLE_TABLE mapping strategy you will get the following output by running the same code
Attractive feature of abstract entities is that they can be queried just like concrete queries, which were not possible in Mapped Superclass. If an abstract entity is the target of a query, the query operates on all the concrete subclasses of the abstract entity.