EJB3 specification was finalized few weeks back and now EJB3 and JPA are in action and many companies have developing their next generation applications using EJB3 and JPA.
OnJava published my article on EJB3 JPA few weeks back and I got some follow up questions from readers and customers. Can you map an entire hierarchy of objects with multi-level inheritance into a single table?
In my article, I discussed a single level of inheritance and will use the same domain model to add another level.
Let us start with a simple top-level domain object Employee. There may be two type of employees: FullTime and Contractor and hence FullTime and Contractor inherit from Employee object. Let us assume that there are two types of contractors: Consultant who charges hefty amount and Volunteer who works for free and hence Consultant and Volunteer inherit from Contractor object and hence the inheritance will look like follows:
Let us assume that you want to map the entire hierarchy of objects into a single table named EMP table.
Assuming you do not to persist an instance of Employee as is, we defined it to be an abstract entity and defined that it is mapped to EMP table and we have defined the inheritance strategy to SINGLE_TABLE.
We have defined the discriminator column to be EMPLOYEE_TYPE that stores the employee type: F for fulltime, C for Consultant and V for volunteer. Note that Id and Table mapping can be defined only in the top-level entity in a hierarchy as follows when using SINGLE_TABLE inheritance strategy:
@Entity
@Table(name="EMP")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="EMPLOYEE_TYPE",discriminatorType=DiscriminatorType.STRING, length=1)
public abstract class Employee implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(nullable=false)
protected Long empNo;
@Column(name="ENAME")
protected String name;
protected Timestamp hireDate;
protected String job;
..
}
The FullTime entity extends the employee and can be persisted on its own. We define the DiscriminatorValue for this entity to be 'F'
@Entity
@DiscriminatorValue(value="F")
public class FullTime extends Employee {
public FullTime() {
}
@Column(name = "SAL")
private Double salary;
@Column(name = "COMM")
private Double commission;
@Column(name = "DESIG")
private String designation;
..
}
The Contractor entity extends Employee but is not persistence on its own, instead its sub-type Consultant and Volunteer are persisted. Hence there is no discriminator value defined for the Contractor entity:
@Entity
public abstract class Contractor extends Employee {
@Column(name = "END_DATE") private Timestamp endDate;
..
}
The discriminator value for Consultant and Volunteer entities are defined as follows:
@Entity
@DiscriminatorValue(value="C")
public class Consultant extends Contractor {
public Consultant() {
..
}
@Entity
@DiscriminatorValue(value="V")
public class Volunteer extends Contractor implements Serializable {
public Volunteer() {
}
..
}
We are done with the mapping !
Now let us create a session façade for the Employee entity
@Stateless(name="EmployeeFacade")
public class EmployeeFacadeBean implements EmployeeFacade {
@PersistenceContext
private EntityManager em;
public EmployeeFacadeBean() {
}
public Object mergeEntity(Object entity) {
return em.merge(entity);
}
public Object persistEntity(Object entity) throws EmployeeCreationException{
em.persist(entity);
return entity;
}
..
}
Let us create a client that uses the session facade persists each type of Employee instances and then list using a named query. You can use dependency injection to invoke the bean instance as follows:
@EJB
EmployeeFacade employeeFacade;
// Persist a FullTime Entity
FullTime fte = new FullTime();
fte.setName("PNisheet");
fte.setSalary(new Double(1200.0));
fte.setDesignation("Programmer");
employeeFacade.persistEntity(fte);
// Persist a Consultant
Consultant pte = new Consultant();
pte.setName("PandaB");
pte.setHourlyRate(new Double(100.0));
employeeFacade.persistEntity(pte);
//Persist a Volunteer
Volunteer vol = new Volunteer();
vol.setName("RenuMi");
vol.setDesignation("Trainee");
employeeFacade.persistEntity(vol);
// List All employees
List<Employee> emplist = employeeFacade.findAllEmployee();
We are done. After running the the client you will see each entity instance persisted to the EMP table with appropriate EMPLOYEE_TYPE column set.
EMPNO ENAME EMPLOYEE_TYPE ----- ---------- ------------- 1506 RenuMi V 1504 PNisheet F 1505 PandaB C
Hope this helps!
10:28:06 AM
|