Home >> Hibernate
Object hierarchy mapping to Database :
Three mapping strategies for handling object heirarchy to database table,
as follows:
1. Table per concrete class
2. Table per class hierarchy
3. Table per subclass
1. Table per concrete class :
In this style of mapping, suppose there will be a abstract class or interface
and multiple classes are either extending abstract class or implementing this
interface, then those concrete classes can be used to persist by mapping to
appropriate Table in database.
Now I shall try to demonstrate this perticular scenario by using an abstract
class as 'A':
A.java
package com.techienjoy.hibernate.example;
public abstract class A {
String str;
public abstract void setStr(String arg);
public abstract String getStr();
}
|
The above abstract class is extended and following concrete class file is made:
AImpl.java
package com.techienjoy.hibernate.example;
public class AImpl extends A {
String subStr;
long id;
public String getSubStr() {
return subStr;
}
public void setSubStr(String subStr) {
this.subStr = subStr;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public void setStr(String arg) {
str = subStr+"***"+arg;
}
@Override
public String getStr() {
return str;
}
}
|
Corresponding HBM mapping is something like as follows:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.techienjoy.hibernate.example">
<!-- If require, these setting needs to be changed accordingly. -->
<class name="AImpl" table="AIMPL">
<id name="id" type="long"/>
<property name="str" column="STR"/>
<property name="subStr" column="SUBSTR"/>
</class>
<class name="BImpl" table="BIMPL">
<id name="id" type="long"/>
<property name="str" column="STR"/>
<property name="subStr" column="SUBSTR"/>
</class>
</hibernate-mapping>
|
Following code is the Test harness for seeing this example works:
Test.java
package com.techienjoy.hibernate.example;
/**
* This code is provided on "AS IS" basis, without warranty
* or guaranty of any kind.
* Author : Ishtek
* Date: 28-November-2010
*/
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class Test {
private static SessionFactory sessionFactory;
/**
* @param args
*/
public static void main(String[] args) {
try {
sessionFactory = new Configuration().configure()
.buildSessionFactory();
A a = new AImpl();
((AImpl) a).setSubStr("ASUB1");
a.setStr("A1");
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
tx.begin();
session.saveOrUpdate(a);
session.flush();
tx.commit();
session.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
|
As one might have noticed in the code above that I am trying to
persist a variable of a abstract class with an instance of a concrete
class AImpl.
For this example I have used HSQLDB as database server with the
hibernate.cfg.xml file configuration as follows:
<?xml version="1.0"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- If require, these setting needs to be changed accordingly. -->
<session-factory>
<property name="connection.driver_class">
org.hsqldb.jdbcDriver
</property>
<property name="connection.url">
jdbc:hsqldb:hsql://localhost
</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="connection.pool_size">1</property>
<property name="dialect">
org.hibernate.dialect.HSQLDialect
</property>
<property name="current_session_context_class">
thread
</property>
<property name="cache.provider_class">
org.hibernate.cache.NoCacheProvider
</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">create</property>
<mapping resource="com/techienjoy/hibernate/example/Example.hbm.xml"/>
</session-factory>
</hibernate-configuration>
|
As one might have noticed that the above configuration is using a Contextual
Hibernate session of type thread with no second level cache provider.
All the tables related to this example will be automatically created
as the hbm2ddl.auto property is set as 'create'.
Second part of this example is to use interface instead of an abstract class
'A', so now I change this A.java from abstract class to an interface as
follows:
A.java
package com.techienjoy.hibernate.example;
public interface A {
public abstract void setStr(String arg);
public abstract String getStr();
}
|
With this interface implementor class looks someting like as follows:
AImpl.java
package com.techienjoy.hibernate.example;
public class AImpl implements A {
String subStr;
long id;
public String getSubStr() {
return subStr;
}
public void setSubStr(String subStr) {
this.subStr = subStr;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public void setStr(String arg) {
subStr = subStr+"***"+arg;
}
@Override
public String getStr() {
return subStr;
}
}
|
All other files in this example won't require to change, but
the data in database table changes, on execution of the Test.java
client code.
2. Table per class hierarchy :
Extending this article to include an example to show one possible
way to do mapping of a class hierarchy turned object graph to a
single table with the help of a discriminator with hard coded value
in the HBM mapping configuration file.
For this example I am defined two class files
SuperA as the super class and SubA is the sub class that extends
SuperA.
SuperA
package com.techienjoy.hibernate.example;
public class SuperA {
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
String str;
}
|
SubA.java
package com.techienjoy.hibernate.example;
public class SubA extends SuperA {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
String name;
int age;
}
|
Example.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.techienjoy.hibernate.example">
<class name="SuperA" table="SUPERA" discriminator-value="SP">
<id name="str" type="string" column="id"/>
<discriminator column="DISC" type="string"/>
<subclass name="SubA" discriminator-value="SB">
<property name="name" column="NAME"/>
<property name="age" column="AGE" type="int"/>
</subclass>
</class>
</hibernate-mapping>
|
In order to test these code I have following Test Harness code as follows:
Test.java
package com.techienjoy.hibernate.example;
/**
* This code is provided on "AS IS" basis, without warranty
* or guaranty of any kind.
* Author : TechIEnjoy
* Date: 28-November-2010
*/
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class Test {
private static SessionFactory sessionFactory;
/**
* @param args
*/
public static void main(String[] args) {
try {
sessionFactory = new Configuration().configure()
.buildSessionFactory();
SuperA a = new SubA();
a.setStr("test");
((SubA) a).setName("Name");
((SubA) a).setAge(30);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
tx.begin();
session.saveOrUpdate(a);
session.flush();
tx.commit();
session.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
|
Once this program is executed one can find a table created with name
as 'SUPERA' with the record as
ID |DISC | Name | age
test| SB | Name | 30
|
Now extending this example by adding another sub class 'SubB' that
extends SuperA class.
SubB.java
package com.techienjoy.hibernate.example;
public class SubB extends SuperA {
String address;
int postalCode;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getPostalCode() {
return postalCode;
}
public void setPostalCode(int postalCode) {
this.postalCode = postalCode;
}
}
|
Example.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.techienjoy.hibernate.example">
<class name="SuperA" table="SUPERA" discriminator-value="SP">
<id name="str" type="string" column="id"/>
<discriminator column="DISC" type="string"/>
<subclass name="SubA" discriminator-value="SB">
<property name="name" column="NAME"/>
<property name="age" column="AGE" type="int"/>
</subclass>
<subclass name="SubB" discriminator-value="SB1">
<property name="address" column="AD"/>
<property name="postalCode" column="POSTALCODE" type="int"/>
</subclass>
</class>
</hibernate-mapping>
|
Test.java
package com.techienjoy.hibernate.example;
/**
* This code is provided on "AS IS" basis, without warranty
* or guaranty of any kind.
* Author : TechIEnjoy
* Date: 28-November-2010
*/
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class Test {
private static SessionFactory sessionFactory;
/**
* @param args
*/
public static void main(String[] args) {
try {
sessionFactory = new Configuration().configure()
.buildSessionFactory();
SuperA a = new SubA();
a.setStr("test");
((SubA) a).setName("Name");
((SubA) a).setAge(30);
SuperA a1 = new SubB();
a1.setStr("test1");
((SubB) a1).setAddress("Address 1");
((SubB) a1).setPostalCode(30056);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
tx.begin();
session.saveOrUpdate(a);
session.saveOrUpdate(a1);
session.flush();
tx.commit();
session.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
|
3. Table per subclass :
In this strategy we can have one table for all the class in the class hierarchy
and a column in the table that is mapped to the subclass and is not inherited
fields from the sub class.
Example.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.techienjoy.hibernate.example">
<class name="SuperA" table="SUPERA">
<id name="str" type="string" column="id"/>
<joined-subclass name="SubA" table="SUBA">
<key column="SUBAID" />
<property name="name" column="NAME"/>
<property name="age" column="AGE" type="int"/>
</joined-subclass>
<joined-subclass name="SubB" table="SUBB">
<key column="SUBBID" />
<property name="address" column="ADDR"/>
<property name="postalCode" column="POSTALCODE" type="int"/>
</joined-subclass>
</class>
</hibernate-mapping>
|
I am using all these Java example class files:
SuperA.java,
SubA.java,
SubB.java
By using the same Test Harness as follows:
Test.java
package com.techienjoy.hibernate.example;
/**
* This code is provided on "AS IS" basis, without warranty
* or guaranty of any kind.
* Author : IQTF
* Date: 28-November-2010
*/
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class Test {
private static SessionFactory sessionFactory;
/**
* @param args
*/
public static void main(String[] args) {
try {
sessionFactory = new Configuration().configure()
.buildSessionFactory();
SuperA a = new SubA();
a.setStr("test");
((SubA) a).setName("Name");
((SubA) a).setAge(30);
SuperA a1 = new SubB();
a1.setStr("test1");
((SubB) a1).setAddress("Address 1");
((SubB) a1).setPostalCode(30056);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
tx.begin();
session.saveOrUpdate(a);
session.saveOrUpdate(a1);
session.flush();
tx.commit();
session.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
|
On execution of this Test Harness, one can see records inserted in three tables,
'SUBA', 'SUBB', 'SUPERA' as follows, respectively:
| SUBAID | | NAME | | AGE |
| test | | Name | | 30 |
| SUBBID | | ADDR | | POSTALCODE |
| test1 | | Address 1 | | 30056 |
If anything missed out , please let me know at
techienjoy at yahoo . com