Inside Scoop on J2EE : Tips and tricks on J2EE and Oracle Application Server by Debu Panda
Updated: 7/11/2006; 9:45:18 AM.

 

 
 

Thursday, June 08, 2006

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    comment []

© Copyright 2006 Debu Panda.

PS: These are my own thoughts and not of my employer ..



Click here to visit the Radio UserLand website.
 


June 2006
Sun Mon Tue Wed Thu Fri Sat
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30  
May   Jul