How to convert an ORM to its subclass using Hibernate?
For example, I have two classes : Person and Employee (Employee is a subclass of Person). Person : has a lastname and a firstname. Employee : has also a salary.
On the client-side, I have a single HTML form where i can fill the person informations (like lastname and firstname). I also have a "switch" between "Person" and "Employee", and if the switch is on Employee I can fill the salary field.
On the server-side, Servlets receive informations from the client and use the Hibernate framework to create/update data to/from the database. The mapping i'm using is a single table for persons and 开发者_JAVA技巧employee, with a discriminator.
I don't know how to convert a Person
to an Employee
, by modifying its type
, which is used as a DiscriminatorColumn
.
I firstly tried to :
- load the Person p from the database
- create an empty Employee e object
- copy values from p into e
- set the salary value
- save e into the database
But i couldn't, as I also copy the ID, and so Hibernate told me they where two instanciated ORM with the same id.
And I can't cast a Person into an Employee directly, as Person is Employee's superclass.
There seems to be a dirty way : delete the person, and create an employee with the same informations, but I don't really like it..
So I'd appreciate any help on that :)
Some precisions :
The person class :
public class Person {
protected int id;
protected String firstName;
protected String lastName;
// usual getters and setters
}
The employee class :
public class Employee extends Person {
// string for now
protected String salary;
// usual getters and setters
}
And in the servlet :
// type is the "switch"
if(request.getParameter("type").equals("Employee")) {
Employee employee = daoPerson.getEmployee(Integer.valueOf(request.getParameter("ID")));
modifyPerson(employee, request);
employee.setSalary(request.getParameter("salary"));
daoPerson.save(employee );
}
else {
Person person = daoPerson.getPerson(Integer.valueOf(request.getParameter("ID")));
modifyPerson(employee, request);
daoPerson.save(person);
}
And finally, the loading (in the dao) :
public Contact getPerson(int ID){
Session session = HibernateSessionFactory.getSession();
Person p = (Person) session.load(Person.class, new Integer(ID));
return p;
}
public Contact getEmployee(int ID){
Session session = HibernateSessionFactory.getSession();
Employee = (Employee) session.load(Employee.class, new Integer(ID));
return p;
}
With this, i'm getting a ClassCastException when trying to load a Person using getEmployee.
XML Hibernate mapping :
<class name="domain.Person" table="PERSON" discriminator-value="P">
<id name="id" type="int">
<column name="ID" />
<generator class="native" />
</id>
<discriminator column="type" type="character"/>
<property name="firstName" type="java.lang.String">
<column name="FIRSTNAME" />
</property>
<property name="lastName" type="java.lang.String">
<column name="LASTNAME" />
</property>
<subclass name="domain.Employee" discriminator-value="E">
<property name="salary" column="SALARY" type="java.lang.String" />
</subclass>
</class>
Is it clear enough ? :-/
When I have done a review some years ago I found a bad bad hack that could solve your problem.
When I understand you right – you need to transform a Person to an Employee and back. They used a discriminator column, let’s call it “disc” and assume that you use a String discriminator with the two values PERSON and EMPLOYEE. Then you need a extra column that mapps the discriminator column to you Person class. Let’s call it the “hackType”.
@Column(columnName=”disc”)
private String hackType;
Now you have coupled hackType and the type of the Object. First you need to make sure is that hackType is PERSON for all Persons and EMPLOYEE for all Employees. – But if you want to “transfer” a Person to an Employee, you just need to load the Person (by ID), set its field hackType to EMPLOYEE, save it, clear the cache, and reload it (by ID) as a Employee.
But this is a really bad hack, I have seen it for nHibernate, but I assume it could work for (Java) Hibernate too. – Anyway I strongly recommend not to do it in this way, but it is interesting to know this hack.
At least I believe the problem is that your domain model is wrong: Employee should not be a subclass of Person. Because this would mean that an Employee will always be an Employee, and a Person (if it is created as an Person) will never become an Employee. I strongly recommend to build an 1:1 relationship between Person and Employee. Where Person is the main object, and Employee is an optional extension to Person. (In this case you need to rename Employee to some thing like EmploymentFacts to make clear that this is only the Employee part of an person.)
I don't think there is a good way out. You can fire an update query on the person to modify its type to 'E' and change the salary, then you can get it using getEmployee()
if you still need that.
精彩评论