4

DB Table:

Employee Table
ID, Type, Salary, Rate

Type can be full time or contractor. And full time has attribute of salary, contractor has attribute of rate. (I know it's not normalized. But it's what it is. Appreciate it if you can educate me why it's a bad design)

JPA Class: (Oracle JPA)

@Entity
@Table(name="Employee")
public abstract class Employee{...}



@Entity
public class FullTimeEmployee extends Employee{...}



public class EmployeeService{
...
Employee joe = entityManager.find(Employee.class, id);
((FullTimeEmployee) joe).getSalary(); //Can I do this? 
//Or I have to use Employee joe = entityManager.find(FullTimeEmployee.class, id);
...
}

Can I downcast a entity class this way? Thanks!

Edit:

My other concerns are this. Both 2 types of employee was stored in the same table. So how come entityManager.find(Employee.class, id) know that which id is fulltime?

So, does it make a difference if I did

entityManager.persist((Employee)fullTimeEmp);

instead of

entityManager.persist(fullTimeEmp);
Weishi Z
  • 1,629
  • 5
  • 18
  • 38
  • 1
    Yes, if you know it is a FullTimeEmployee. What you received was an Employee instance, which will behave like any java object. If it is not a FullTimeEmployee or one of its subclasses, you will get a ClassCastException. You might want to use find(FullTimeEmployee.class,id) if you want to prevent the exception, and instead handle the issue that might occur if there isn't a FullTimeEmployee with the specified id. – Chris Oct 05 '15 at 20:28
  • @Chris Thanks! My other concerns are this. Both 2 types of employee was stored in the same table. So how come entityManager.find(Employee.class, id) know that which id is fulltime? – Weishi Z Oct 05 '15 at 20:56
  • Of course, I need to entityManager.persisit(fullTimeEmp); Does it make a difference if I did entityManager.persisit( (Employee)fullTimeEmp) ? – Weishi Z Oct 05 '15 at 20:58

2 Answers2

3

Can I downcast a entity class this way?

You can, and it will work, but not always. So I would not do it. For example, if you have a lazy toOne association with an employee, or if you happen to have used em.getReference() to "load" the employee in the same session before, you will get a ClassCastException because Hibernate will return a proxy to Employee, that is neither a FullTimeEmployee nor a Contractor.

You're safe if you avoid instanceof and casts, and use polymorphism instead. For example, you could define an abstract getSalary() method in the base class and provide different implementations in the subclasses. Or you could use the visitor pattern.

Both 2 types of employee was stored in the same table. So how come entityManager.find(Employee.class, id)

It knows because you must have a discriminator column, containing the type of the entity. When loading the row from the database, Hibernate reads the type from this column and instantiates the appropriate subclass.

So, does it make a difference if I did

entityManager.persist((Employee)fullTimeEmp);

No, not at all. The actual, concrete type of fullTimeEmp is FullTimeEmployee, whether you cast it to Employee or not, and that's what Hibernate uses to know how theemployee must be saved (in this case, which value it must store in the discriminator column).

Community
  • 1
  • 1
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
0

Since you did not explicitly set the strategy for Inheritance mapping, the default will be InheritanceType.SINGLE_TABLE. That means, all of your persisted entities (regardless of what concrete subclass type is persisted), will be stored in a single table. If a single table is used, you will most likely have a discriminator column, that will distinguish a particular row of what subclass type it should be. Here is a good example.

On your concern regarding how to query a particular subtype, then you can use JP QL keyword TYPE.

Example:

SELECT e FROM Employee e WHERE TYPE(e) = FullTimeEmployee

The result of this query will be all FullTimeEmployee objects.

Also, if you're looking for a specific FullTimeEmployee object given an ID. The code below is also possible:

em.find(FullTimeEmployee.class, id);
Ish
  • 3,992
  • 1
  • 17
  • 23