Hi everyone! (it's my first post on hibernate forum)
At the beginning I would inform that I am hibernate newbie, so your patience is very appreciated.
My question refers to mapping the following class hierarchy:
(the following code is complete and ready to run if you wish to try it in your IDE)
Code:
package pl.edu.uj.spring.hibernate.collections;
public interface Identifiable {
Long getId();
}
Identifiable interface allows to retrieve primary key of an object (used when working with DB). Please notice that there is no setter here. I don't want to make possible changing this value from Java code. ID (key) should be set ones, and stay the same up to removing object. As far as I know in fact many DB vendors do not allow to update primary key of an entity.
Code:
package pl.edu.uj.spring.hibernate.collections;
public interface Person extends Identifiable {
public String getName();
public String getSurname();
public void setName(String name);
public void setSurname(String surname);
}
Code:
package pl.edu.uj.spring.hibernate.collections;
public class Student implements Person {
private Long id;
private String name;
private String surname;
private int classNumber;
public Student(String name, String surname, int classNumber) {
setName(name);
setSurname(surname);
setClassNumber(classNumber);
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
public int getClassNumber() {
return classNumber;
}
public void setName(String name) {
this.name = name;
}
public void setSurname(String surname) {
this.surname = surname;
}
public void setClassNumber(int classNumber) {
this.classNumber = classNumber;
}
}
Code:
package pl.edu.uj.spring.hibernate.collections;
public class Teacher implements Person {
private Long id;
private String name;
private String surname;
private String specializationSubject;
public Teacher(String name, String surname, String specializationSubject) {
setName(name);
setSurname(surname);
setSpecializationSubject(specializationSubject);
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
public String getSpecializationSubject() {
return specializationSubject;
}
public void setName(String name) {
this.name = name;
}
public void setSurname(String surname) {
this.surname = surname;
}
public void setSpecializationSubject(String specializationSubject) {
this.specializationSubject = specializationSubject;
}
}
Please notice that persons collections uses interface as a generic type! Classes Teacher and Student implements this interface:
Code:
package pl.edu.uj.spring.hibernate.collections;
import java.util.List;
public class School implements Identifiable {
private Long id;
private List<Person> persons;
public Long getId() {
return id;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
public List<Person> getPersons(List<Person> persons) {
return persons;
}
public void addPerson(Person person) {
persons.add(person);
}
}
Here is how this everything is used (Spring + Hibernate):
Code:
package pl.edu.uj.spring.hibernate.collections;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class SchoolDAO extends HibernateDaoSupport {
public void persist(School school) {
getHibernateTemplate().save(school);
}
public School load(Long id) {
return getHibernateTemplate().load(School.class, id);
}
}
Code:
public class HibernateCollections {
public static void main(String[] args) {
List<Person> persons = new ArrayList<Person>();
persons.add(new Teacher("John","Smith","History"));
persons.add(new Teacher("Michael","Black","Biology"));
persons.add(new Student("Wladimir","Vodka", 3));
persons.add(new Student("Marko","Ramirez",4));
School school = new School();
school.setPersons(persons);
ApplicationContext ctx = new ClassPathXmlApplicationContext("pl/edu/uj/spring/hibernate/collections/context.xml");
SchoolDAO dao = ctx.getBean("schoolDao", SchoolDAO.class);
dao.persist(school);
}
}
(I know that I can simplify this code by using inheritance but I just wanted to flash on collection persisting)
Ok so my question is how to map everything in xml file?
I've tried in this way (using inheritance - classes Teacher and Student implements interface Person):
Code:
<?xml version="1.0" ?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="pl.edu.uj.spring.hibernate.collections.School" table="SCHOOLS">
<id name="id" column="ID" access="field">
<generator class="sequence">
<param name="sequence">HIBERNATE_SCHOOLSEQ</param>
</generator>
</id>
<!--
http://docs.jboss.org/hibernate/stable/core/reference/en/html/associations.html
one - to - many
-->
<bag name="persons" table="School2Person">
<key column="schoolId"/>
<many-to-many column="personId" unique="true" class="pl.edu.uj.spring.hibernate.collections.Person"/>
</bag>
</class>
<class name="pl.edu.uj.spring.hibernate.collections.Person" abstract="true">
<id name="id" column="ID" access="field">
<generator class="sequence">
<param name="sequence">HIBERNATE_PERSONSEQ</param>
</generator>
</id>
<joined-subclass name="pl.edu.uj.spring.hibernate.collections.Student" table="STUDENTS">
<key column="ID"/>
<!--
<id name="id" column="ID" access="field">
<generator class="sequence">
<param name="sequence">HIBERNATE_STUDENTSEQ</param>
</generator>
</id> -->
<property name="name" column="NAME"/>
<property name="surname" column="SURNAME"/>
<property name="classNumber" column="CLASS_NUMBER"/>
</joined-subclass>
<joined-subclass name="pl.edu.uj.spring.hibernate.collections.Teacher" table="TEACHERS">
<key column="id"/>
<!--
<id name="id" column="ID" access="field">
<generator class="sequence">
<param name="sequence">HIBERNATE_TEACHERSEQ</param>
</generator>
</id>
-->
<property name="name" column="NAME"/>
<property name="surname" column="SURNAME"/>
<property name="specializationSubject" column="SPECIALIZATION_SUBJECT"/>
</joined-subclass>
</class>
</hibernate-mapping>
But it results in exception:
Quote:
org.hibernate.PropertyNotFoundException: field [id] not found on pl.edu.uj.spring.hibernate.collections.Person
... and he's right. But I do not want to add this field to interface (it would become public static final...). When working with classes I could do
<id name="id" column="ID" access="field"> but it's impossible with interfaces.
Is there any way to map this?
I would also ask question to Hibernate experienced developers if it is shame using classes all the time instead interfaces? I know that in regular Java programming we should program to interfaces but I found out that it is sometimes extremely complicated to map it in hibernate (like in the preceding example). As I can see in many open source projects built on Hibernate, programmers in domain tier do not use interfaces (almost) at all - only classes - how about you? In my opinion (but as I said I am newbie) it is far more complicated to make it work with hibernate when using interfaces than using only classes and make some refactoring when needed.
Thank you in advance!
Piotr