Search This Blog

Monday, 3 October 2011

The Hibernate Inheritance Mechanism - 3

Table per Class Hierarchy:
In this technique an entire class hierarchy is mapped to a single table. Both the Footballer and Cricketer Information will be saved as different rows in the same table.To distinguish the data in each row a discriminator is added.
The class mapping would be as follows:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
<hibernate-mapping package="com.inheritance.model">
    <class name="SportsPerson" table="SPORTS_PERSON">
        <id name="id" type="long">
            <column name="ID" />
            <generator class="native" />
        <discriminator column="SPORT_TYPE" type="string" />

        <property name="name" type="string" column="NAME" />

        <subclass name="Cricketer" discriminator-value="cricket">
            <property name="runs" type="integer" column="RUNS" />
            <property name="wickets" type="integer" column="WICKETS" />
            <property name="t20Player" type="boolean" column="T20_PLAYER" />

        <subclass name="Footballer" discriminator-value="footballer">
            <property name="goals" type="integer" column="GOALS" />
            <property name="appearances" type="integer" column="APPEARANCES" />
            <property name="sendOffs" type="integer" column="SEND_OFFS" />

On start up the logs reveal the schema for the newly created table
485  [main] INFO  org.hibernate.cfg.Configuration  - Reading mappings from resou
rce : com/inheritance/approach3/mapping/SportsPerson.hbm.xml
2375 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Ins
ert 0: insert into SPORTS_PERSON (NAME, SPORT_TYPE, ID) values (?, 'com.inherita
nce.model.SportsPerson', ?)
2391 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  - Stat
ic SQL for entity: com.inheritance.model.Cricketer
2391 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Sna
pshot select: select cricketer_.ID, cricketer_.NAME as NAME0_, cricketer_.RUNS a
s RUNS0_, cricketer_.WICKETS as WICKETS0_, cricketer_.T20_PLAYER as T6_0_ from S
PORTS_PERSON cricketer_ where cricketer_.ID=?
2391 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Ins
D) values (?, ?, ?, ?, 'cricket', ?)
2391 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Ide
ntity insert: insert into SPORTS_PERSON (NAME, RUNS, WICKETS, T20_PLAYER, SPORT_
TYPE) values (?, ?, ?, ?, 'cricket')
2422 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  - Stat
ic SQL for entity: com.inheritance.model.Footballer
2422 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Sna
pshot select: select footballer_.ID, footballer_.NAME as NAME0_, footballer_.GOA
LS as GOALS0_, footballer_.APPEARANCES as APPEARAN8_0_, footballer_.SEND_OFFS as
 SEND9_0_ from SPORTS_PERSON footballer_ where footballer_.ID=?
2422 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Ins
E, ID) values (?, ?, ?, ?, 'footballer', ?)
2516 [main] DEBUG org.hibernate.loader.entity.EntityLoader  - Static select for 
entity com.inheritance.model.Cricketer: select cricketer0_.ID as ID0_0_, cricket
er0_.NAME as NAME0_0_, cricketer0_.RUNS as RUNS0_0_, cricketer0_.WICKETS as WICK
ETS0_0_, cricketer0_.T20_PLAYER as T6_0_0_ from SPORTS_PERSON cricketer0_ where 
cricketer0_.ID=? and cricketer0_.SPORT_TYPE='cricket' 
... [several other queries]
2516 [main] DEBUG org.hibernate.loader.entity.EntityLoader  - Static select for 
entity com.inheritance.model.Footballer: select footballer0_.ID as ID0_0_, footb
aller0_.NAME as NAME0_0_, footballer0_.GOALS as GOALS0_0_, footballer0_.APPEARAN
CES as APPEARAN8_0_0_, footballer0_.SEND_OFFS as SEND9_0_0_ from SPORTS_PERSON f
ootballer0_ where footballer0_.ID=? and footballer0_.SPORT_TYPE='footballer'
...[several other queries]
2641 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    drop table if exists SPORTS_PERSON
2688 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    create table SPORTS_PERSON (
        ID bigint not null auto_increment,
        SPORT_TYPE varchar(255) not null,
        NAME varchar(255),
        RUNS integer,
        WICKETS integer,
        T20_PLAYER bit,
        GOALS integer,
        APPEARANCES integer,
        SEND_OFFS integer,
        primary key (ID)
2735 [main] INFO  org.hibernate.tool.hbm2ddl.SchemaExport  - schema export compl
The logs indicate the different queries created for the classes in the SportsPerson hierarchy. The discriminator value is visible in the queries for the subclasses. The queries related to abstract class SportsPerson are executed with single query on the single table that was generated in this approach.The created table now holds the data for the entire SportsPerson Hierarchy.
On executing the code to create a cricketer:
private static void createCricketer() {
    Cricketer crickter = new Cricketer();
    crickter.setName("Rahul Dravid");
    Session session = sessionFactory.openSession();
    Transaction t = session.beginTransaction();;
The SQL generated by Hibernate is as below
3281 [main] DEBUG org.hibernate.event.def.AbstractSaveEventListener  - executing
 identity-insert immediately
3281 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  - Inse
rting entity: com.inheritance.model.Cricketer (native id)
3281 [main] DEBUG org.hibernate.jdbc.AbstractBatcher  - about to open PreparedSt
atement (open PreparedStatements: 0, globally: 0)
3281 [main] DEBUG org.hibernate.SQL  - 
        (?, ?, ?, ?, 'cricket')
The record is created with null values for the columns related to footballer.
As can be seen the record has been created with null values. This makes it clear that adding null-constraints on the properties of the concrete sub classes is not an option. For e.g. if a not null constraint was added to the appearances column, it would not be possible to create any instance of cricketers.
Also the SPORT_TYPE column was set with the value "cricket". This value is set directly by hibernate and is currently not even visible in the Model classes.
Query query = session.createQuery("from SportsPerson");
The query generated for this request is as below:
4015 [main] DEBUG org.hibernate.SQL  -  
        sportspers0_.ID as ID0_,
        sportspers0_.NAME as NAME0_,
        sportspers0_.RUNS as RUNS0_,
        sportspers0_.WICKETS as WICKETS0_,
        sportspers0_.T20_PLAYER as T6_0_,
        sportspers0_.GOALS as GOALS0_,
        sportspers0_.APPEARANCES as APPEARAN8_0_,
        sportspers0_.SEND_OFFS as SEND9_0_,
        sportspers0_.SPORT_TYPE as SPORT2_0_ 
        SPORTS_PERSON sportspers0_
The sql created for the query on subclass is as below.
Query query = session.createQuery("from Footballer");
The generated query will fetch the rows that have discriminator with value "footballer"
3297 [main] DEBUG org.hibernate.SQL  - 
        footballer0_.ID as ID0_,
        footballer0_.NAME as NAME0_,
        footballer0_.GOALS as GOALS0_,
        footballer0_.APPEARANCES as APPEARAN8_0_,
        footballer0_.SEND_OFFS as SEND9_0_ 
        SPORTS_PERSON footballer0_ 
The major drawbacks of this approach are :
  1. Inability to add null constraints on the properties of the sub classes.
  2. Need to modify the same table every time a new sub class is created.
  3. Existence of unrelated columns such as GOALS and RUNS in the same table.
As can be seen this technique is a slight improvement over technique 1, in that it allows polymorphic queries and polymorphic associations. However the disadvantages are quite a lot.
Personally I wouldn't like to use this approach too often due to the shortcomings stated above

Update: For a brief summary of the Hibernate Inheritance types check this post.

No comments:

Post a Comment