Search This Blog

Saturday, 1 October 2011

The Hibernate Inheritance Mechanism - 2

Table Per Concrete Class + Unions
Continuing with the inheritance mechanisms, I shall now attempt to implement the second approach. This approach tries to overcome the drawbacks of the first technique.
In this technique
  1. The abstract class is also mapped into Hibernate. 
  2. The common properties are mapped inside this class.
  3. The concrete classes representing the tables are then mapped using the union-subclass attribute
  4. The attributes specific to the concrete implementations are mapped here.
The Java classes remain same as the previous 2 posts. The only change is the mapping document.
<?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.inheritance.model">
    <class name="SportsPerson" abstract="true">
        <id name="id" type="long">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="name" type="string">
            <column name="NAME" />
        </property>

        <union-subclass name="Cricketer" table="CRICKETER">
            <property name="runs" type="integer">
                <column name="RUNS" />
            </property>
            <property name="wickets" type="integer">
                <column name="WICKETS" />
            </property>
            <property name="t20Player" type="boolean">
                <column name="T20_PLAYER" />
            </property>
        </union-subclass>

        <union-subclass name="Footballer" table="FOOTBALLER">
            <property name="goals" type="integer">
                <column name="GOALS" />
            </property>
            <property name="appearances" type="integer">
                <column name="APPEARANCES" />
            </property>
            <property name="sendOffs" type="integer">
                <column name="SEND_OFFS" />
            </property>

        </union-subclass>
    </class>

</hibernate-mapping>
As can be seen, the SportsPerson class is mapped as an abstract class with the two concrete implementations being mapped within it. The base class attributes have been placed within the <class> element. The properties specific to the Concrete class are mapped inside the <union-subclass> element.
On server start up the  logs(cleaned) are as below:
Exception in thread "main" org.hibernate.MappingException: Cannot use identity 
column key generation with <union-subclass> mapping for: 
com.inheritance.model.SportsPerson
    at org.hibernate.persister.entity.UnionSubclassEntityPersister.<init>(UnionSubc
lassEntityPersister.java:67)
    at org.hibernate.persister.PersisterFactory.createClassPersister(PersisterFacto
ry.java:61)
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:226)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1294)
    at test.inheritance.approach2.mapping.TestApproach2.main(TestApproach2.java:48)
Hibernate does not allow the use of identity column (auto increment field) when using union-subclass. For a more detailed explanation refer this link. I modified the generator class to hilo.
<class name="SportsPerson" abstract="true">
    <id name="id" type="long">
        <column name="ID" />
        <generator class="hilo" />
    </id>
I turned on the auto-create property of Hibernate.The start up logs are now as below:
359  [main] DEBUG org.hibernate.cfg.Configuration  - null<-org.dom4j.tree.Defaul
tAttribute@183f74d [Attribute: name resource value "com/inheritance/approach2/ma
pping/SportsPerson.hbm.xml"]
...
765  [main] INFO  org.hibernate.cfg.HbmBinder  - Mapping class: com.inheritance.
model.SportsPerson -> SportsPerson
781  [main] DEBUG org.hibernate.cfg.HbmBinder  - Mapped property: id -> ID
812  [main] DEBUG org.hibernate.cfg.HbmBinder  - Mapped property: name -> NAME
1000 [main] INFO  org.hibernate.cfg.HbmBinder  - Mapping union-subclass: com.inh
eritance.model.Cricketer -> CRICKETER
1000 [main] DEBUG org.hibernate.cfg.HbmBinder  - Mapped property: runs -> RUNS
1000 [main] DEBUG org.hibernate.cfg.HbmBinder  - Mapped property: wickets -> WIC
KETS
1000 [main] DEBUG org.hibernate.cfg.HbmBinder  - Mapped property: t20Player -> T
20_PLAYER
1000 [main] INFO  org.hibernate.cfg.HbmBinder  - Mapping union-subclass: com.inh
eritance.model.Footballer -> FOOTBALLER
1000 [main] DEBUG org.hibernate.cfg.HbmBinder  - Mapped property: goals -> GOALS
1000 [main] DEBUG org.hibernate.cfg.HbmBinder  - Mapped property: appearances ->
 APPEARANCES
1000 [main] DEBUG org.hibernate.cfg.HbmBinder  - Mapped property: sendOffs -> SE
ND_OFFS
3203 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  - Stat
ic SQL for entity: com.inheritance.model.SportsPerson
3203 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Ver
sion select: select ID from SportsPerson where ID =?
3203 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Sna
pshot select: select sportspers_.ID, sportspers_.NAME as NAME0_ from ( select T2
0_PLAYER, ID, null as GOALS, NAME, null as APPEARANCES, RUNS, null as SEND_OFFS,
 WICKETS, 1 as clazz union select null as T20_PLAYER, ID, GOALS,
 NAME, APPEARANCES, null as RUNS, SEND_OFFS, null as WICKETS, 2 as clazz_ from F
OOTBALLER ) sportspers_ where sportspers_.ID=?
3203 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Ins
ert 0: insert into SportsPerson (NAME, ID) values (?, ?)
3203 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Upd
ate 0: update SportsPerson set NAME=? where ID=?
3203 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Del
ete 0: delete from SportsPerson where ID=?
3218 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  - Stat
ic SQL for entity: com.inheritance.model.Cricketer
3218 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Ver
sion select: select ID from CRICKETER where ID =?
3218 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Sna
pshot select: select cricketer_.ID, cricketer_.NAME as NAME0_, cricketer_.RUNS a
s RUNS1_, cricketer_.WICKETS as WICKETS1_, cricketer_.T20_PLAYER as T3_1_ from C
RICKETER cricketer_ where cricketer_.ID=?
3218 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Ins
ert 0: insert into CRICKETER (NAME, RUNS, WICKETS, T20_PLAYER, ID) values (?, ?,
 ?, ?, ?)
3218 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Upd
ate 0: update CRICKETER set NAME=?, RUNS=?, WICKETS=?, T20_PLAYER=? where ID=?
3218 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Del
ete 0: delete from CRICKETER where ID=?
3250 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  - Stat
ic SQL for entity: com.inheritance.model.Footballer
3250 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Ver
sion select: select ID from FOOTBALLER where ID =?
3250 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Sna
pshot select: select footballer_.ID, footballer_.NAME as NAME0_, footballer_.GOA
LS as GOALS2_, footballer_.APPEARANCES as APPEARAN2_2_, footballer_.SEND_OFFS as
 SEND3_2_ from FOOTBALLER footballer_ where footballer_.ID=?
3250 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Ins
ert 0: insert into FOOTBALLER (NAME, GOALS, APPEARANCES, SEND_OFFS, ID) values (
?, ?, ?, ?, ?)
3250 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Upd
ate 0: update FOOTBALLER set NAME=?, GOALS=?, APPEARANCES=?, SEND_OFFS=? where I
D=?
3250 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Del
ete 0: delete from FOOTBALLER where ID=?
3359 [main] DEBUG org.hibernate.loader.entity.EntityLoader  - Static select for 
entity com.inheritance.model.SportsPerson: select sportspers0_.ID as ID0_0_, spo
rtspers0_.NAME as NAME0_0_, sportspers0_.RUNS as RUNS1_0_, sportspers0_.WICKETS 
as WICKETS1_0_, sportspers0_.T20_PLAYER as T3_1_0_, sportspers0_.GOALS as GOALS2
_0_, sportspers0_.APPEARANCES as APPEARAN2_2_0_, sportspers0_.SEND_OFFS as SEND3
_2_0_, sportspers0_.clazz_ as clazz_0_ from ( select T20_PLAYER, ID, null as GOA
LS, NAME, null as APPEARANCES, RUNS, null as SEND_OFFS, WICKETS, 1 as clazz_ fro
m CRICKETER union select null as T20_PLAYER, ID, GOALS, NAME, APPEARANCES, null 
as RUNS, SEND_OFFS, null as WICKETS, 2 as clazz_ from FOOTBALLER ) sportspers0_ 
where sportspers0_.ID=? for update
3390 [main] DEBUG org.hibernate.loader.entity.EntityLoader  - Static select for 
action ACTION_MERGE on entity com.inheritance.model.SportsPerson: select sportsp
ers0_.ID as ID0_0_, sportspers0_.NAME as NAME0_0_, sportspers0_.RUNS as RUNS1_0_
, sportspers0_.WICKETS as WICKETS1_0_, sportspers0_.T20_PLAYER as T3_1_0_, sport
spers0_.GOALS as GOALS2_0_, sportspers0_.APPEARANCES as APPEARAN2_2_0_, sportspe
rs0_.SEND_OFFS as SEND3_2_0_, sportspers0_.clazz_ as clazz_0_ from ( select T20_
PLAYER, ID, null as GOALS, NAME, null as APPEARANCES, RUNS, null as SEND_OFFS, W
ICKETS, 1 as clazz_ from CRICKETER union select null as T20_PLAYER, ID, GOALS, N
AME, APPEARANCES, null as RUNS, SEND_OFFS, null as WICKETS, 2 as clazz_ from FOO
TBALLER ) sportspers0_ where sportspers0_.ID=?
3390 [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 RUNS1_0_, cricketer0_.WICKETS as WICK
ETS1_0_, cricketer0_.T20_PLAYER as T3_1_0_ from CRICKETER cricketer0_ where cric
keter0_.ID=?
...[Other Cricketer Table queries for CRUD, MERGE and Refresh]
3390 [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 GOALS2_0_, footballer0_.APPEARAN
CES as APPEARAN2_2_0_, footballer0_.SEND_OFFS as SEND3_2_0_ from FOOTBALLER foot
baller0_ where footballer0_.ID=?
3390 [main] DEBUG org.hibernate.loader.entity.EntityLoader  - Static select for 
action ACTION_MERGE on entity com.inheritance.model.Footballer: select footballe
r0_.ID as ID0_0_, footballer0_.NAME as NAME0_0_, footballer0_.GOALS as GOALS2_0_
, footballer0_.APPEARANCES as APPEARAN2_2_0_, footballer0_.SEND_OFFS as SEND3_2_
0_ from FOOTBALLER footballer0_ where footballer0_.ID=?
...[Other Footballer Table queries for CRUD, MERGE and Refresh]
3453 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    drop table if exists CRICKETER
3578 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    drop table if exists FOOTBALLER
3640 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    drop table if exists hibernate_unique_key
3703 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    create table CRICKETER (
        ID bigint not null,
        NAME varchar(255),
        RUNS integer,
        WICKETS integer,
        T20_PLAYER bit,
        primary key (ID)
    )
3797 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    create table FOOTBALLER (
        ID bigint not null,
        NAME varchar(255),
        GOALS integer,
        APPEARANCES integer,
        SEND_OFFS integer,
        primary key (ID)
    )
3797 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    create table hibernate_unique_key (
         next_hi integer 
    )
3828 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    insert into hibernate_unique_key values (0)
3828 [main] INFO  org.hibernate.tool.hbm2ddl.SchemaExport  - schema export compl
ete 
From the huge list of queries generated it can be seen that queries have been generated for the abstract class and each of the concrete sub-classes too. I now execute the code to create a cricketer:
private static void createCricketer() {
    Cricketer crickter = new Cricketer();
    crickter.setName("Rahul Dravid");
    crickter.setRuns(12000);
    crickter.setT20Player(false);
    crickter.setWickets(2);
    
    Session session = sessionFactory.openSession();
    Transaction t = session.beginTransaction();
    session.save(crickter);
    t.commit();    
}
The generated logs show the below query being fired
3203 [main] DEBUG org.hibernate.jdbc.AbstractBatcher  - about to open PreparedSt
atement (open PreparedStatements: 0, globally: 0)
3203 [main] DEBUG org.hibernate.SQL  -  
    insert 
    into
        CRICKETER
        (NAME, RUNS, WICKETS, T20_PLAYER, ID) 
    values
        (?, ?, ?, ?, ?)

Also I executed an HQL query to return the footballer with name Tom.
private static void getFootballerWithName() {
    Session session = sessionFactory.openSession();
    try {
        Query query = session.createQuery("from Footballer f where f.name like 'Tom%'");
        System.out.println("The Player is " + ((Footballer)query.uniqueResult()).getName());
    } catch (HibernateException e) {
        e.printStackTrace();
    } finally {
        session.close();
    }
}
The logs are as below
3453 [main] DEBUG org.hibernate.SQL  - 
    select
        footballer0_.ID as ID0_,
        footballer0_.NAME as NAME0_,
        footballer0_.GOALS as GOALS2_,
        footballer0_.APPEARANCES as APPEARAN2_2_,
        footballer0_.SEND_OFFS as SEND3_2_ 
    from
        FOOTBALLER footballer0_ 
    where
        footballer0_.NAME like 'Tom%'
The Player is Tom Cleaverly
3547 [main] DEBUG org.hibernate.impl.SessionImpl  - closing session
The generated query was directly on the Footballer entity. Finally I decided to fetch the records of all sportsperson in the system.
private static void getAllSportsPerson() {
    Session session = sessionFactory.openSession();
    try {
        Query query = session.createQuery("from SportsPerson");
        @SuppressWarnings("unchecked")
        final List<SportsPerson> sportsPersons = query.list(); 
        System.out.println("Total Records : " + sportsPersons.size());
        for (SportsPerson sportsPerson : sportsPersons) {
            System.out.println("Id: " + sportsPerson.getId());
            System.out.println("Name: " + sportsPerson.getName());
            if (sportsPerson instanceof Cricketer) {
                Cricketer cricketer = (Cricketer) sportsPerson;
                System.out.println("Sport - Cricket");
                System.out.println("Runs Scored: " + cricketer.getRuns());
                System.out.println("Wickets taken : " + cricketer.getWickets());
                System.out.println("T20 Player: " + cricketer.getT20Player());
            } else if (sportsPerson instanceof Footballer) {
                Footballer footballer = (Footballer) sportsPerson;
                System.out.println("Sport - Football");
                System.out.println("Goals Scored: " + footballer.getGoals());
                System.out.println("Appearances : " + footballer.getAppearances());
                System.out.println("Send Offs: " + footballer.getSendOffs());
                }
        }
    } catch (HibernateException e) {
        e.printStackTrace();
    } finally {
        session.close();
    }
}
On executing the code a single query was executed which fetched the records from all the sub-classes:
2703 [main] DEBUG org.hibernate.SQL  - 
    select
        sportspers0_.ID as ID0_,
        sportspers0_.NAME as NAME0_,
        sportspers0_.RUNS as RUNS1_,
        sportspers0_.WICKETS as WICKETS1_,
        sportspers0_.T20_PLAYER as T3_1_,
        sportspers0_.GOALS as GOALS2_,
        sportspers0_.APPEARANCES as APPEARAN2_2_,
        sportspers0_.SEND_OFFS as SEND3_2_,
        sportspers0_.clazz_ as clazz_ 
    from
        ( select
            T20_PLAYER,
            ID,
            null as GOALS,
            NAME,
            null as APPEARANCES,
            RUNS,
            null as SEND_OFFS,
            WICKETS,
            1 as clazz_ 
        from
            CRICKETER 
        union
        select
            null as T20_PLAYER,
            ID,
            GOALS,
            NAME,
            APPEARANCES,
            null as RUNS,
            SEND_OFFS,
            null as WICKETS,
            2 as clazz_ 
        from
            FOOTBALLER 
    ) sportspers0_
2719 [main] DEBUG org.hibernate.jdbc.AbstractBatcher  - preparing statement
...
2797 [main] DEBUG org.hibernate.loader.Loader  - result row: EntityKey[com.inher
itance.model.SportsPerson#1]
2797 [main] DEBUG org.hibernate.type.IntegerType  - returning '1' as column: cla
zz_
2797 [main] DEBUG org.hibernate.loader.Loader  - Initializing object from Result
Set: [com.inheritance.model.Cricketer#1]
2828 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  - Hydr
ating entity: [com.inheritance.model.Cricketer#1]
2828 [main] DEBUG org.hibernate.type.StringType  - returning 'Rahul Dravid' as c
olumn: NAME0_
...
2828 [main] DEBUG org.hibernate.loader.Loader  - result row: EntityKey[com.inher
itance.model.SportsPerson#32768]
2828 [main] DEBUG org.hibernate.type.IntegerType  - returning '2' as column: cla
zz_
2828 [main] DEBUG org.hibernate.loader.Loader  - Initializing object from Result
Set: [com.inheritance.model.Footballer#32768]
2828 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  - Hydr
ating entity: [com.inheritance.model.Footballer#32768]
2844 [main] DEBUG org.hibernate.type.StringType  - returning 'Tom Cleaverly' as 
column: NAME0_
2844 [main] DEBUG org.hibernate.type.IntegerType  - returning '3' as column: GOA
LS2_
2844 [main] DEBUG org.hibernate.type.IntegerType  - returning '5' as column: APP
EARAN2_2_
2844 [main] DEBUG org.hibernate.type.IntegerType  - returning '0' as column: SEN
D3_2_
Total Records : 2
Id: 1
Name: Rahul Dravid
Sport - Cricket
Runs Scored: 12000
Wickets taken : 2
T20 Player: false
Id: 32768
Name: Tom Cleaverly
Sport - Football
Goals Scored: 3
Appearances : 5
Send Offs: 0 
2844 [main] DEBUG org.hibernate.impl.SessionImpl  - closing session
Hibernate executed a single query using a sub query in the form clause. it also added a returned value CLAZZ to help distinguish between the appropriate sub-classes. Based on this value for each of the returned row in the result set, the Hibernate Loader accordingly created appropriate objects of the subclass.
The outer select clause combines all the fields of all the sub-classes. Thus null padding has been applied in the inner queries based on the different properties present in each of the sub-class. Thus we have managed to solve the issue of polymorphic queries.
It is now possible to implement polymorphic associations correctly without any duplications.
 I decided that every Sportsperson must have an address.
public class Address {
    private Long id;
    private String city;
    private SportsPerson sportsPerson;
        //setter getters
}
The Address class has a polymorphic association to SportsPerson class. It is not directly aware of the sports details as in Cricketer or Footballer. Like a good object it is only concerned with the generic SportsPerson class. The hbm file for the same is :
<?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.inheritance.model">
    <class name="Address" table="ADDRESS">
        <id name="id" type="long">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="city" type="string">
            <column name="CITY" />
        </property>
        <many-to-one name="sportsPerson" class="SportsPerson"
            column="sp_id" foreign-key="address_fk1" not-null="true" />
    </class>
</hibernate-mapping>
The DDL created for this table is as below:
create table ADDRESS (
   ID bigint not null auto_increment,
   CITY varchar(255),
   sp_id bigint not null,
   primary key (ID)
)
As can be seen the foreign key was not applied here. This is simply because there is no table for Sportsperson. This is a disadvantage of the technique - certain SQL constraints will not work because the abstract class has no representation in the database. I also wrote a code to create a Cricketer with an address.
public static void createCricketerWithAddress() {
    Cricketer cricketer = new Cricketer();
    cricketer.setName("Rahul Dravid");
    cricketer.setRuns(12000);
    cricketer.setT20Player(false);
    cricketer.setWickets(2);
    Address address = new Address();
    address.setCity("Bangalore");
    address.setSportsPerson(cricketer);
    Session session = sessionFactory.openSession();
    Transaction t = session.beginTransaction();
    session.save(cricketer);
    session.save(address);
    t.commit();
}
The fired SQL is :
2766 [main] DEBUG org.hibernate.SQL  - 
    insert 
    into
        CRICKETER
        (NAME, RUNS, WICKETS, T20_PLAYER, ID) 
    values
        (?, ?, ?, ?, ?)
2781 [main] DEBUG org.hibernate.SQL  - 
    insert 
    into
        ADDRESS
        (CITY, sp_id) 
    values
        (?, ?)
As can be seen, the code worked fine. Now what if I want all sportsperson residing in Bangalore ?
public static void getSportsPersonWithAddress() {
        Session session = sessionFactory.openSession();
    try {
        Query query = session.createQuery("select sp from SportsPerson sp " +
           ",Address a where a.city like 'Ba%'");
        final List<SportsPerson> sportsPersons = query.list(); 
        for (SportsPerson sportsPerson : sportsPersons) {
            System.out.println("Id: " + sportsPerson.getId());
            System.out.println("Name: " + sportsPerson.getName());
            System.out.println("class is " + sportsPerson.getClass());
        }    
    } catch (HibernateException e) {
        e.printStackTrace();
    } finally {
        session.close();
    }
}
The generated SQL is :
3219 [main] DEBUG org.hibernate.SQL  - 
    select
        sportspers0_.ID as ID0_,
        sportspers0_.NAME as NAME0_,
        sportspers0_.RUNS as RUNS1_,
        sportspers0_.WICKETS as WICKETS1_,
        sportspers0_.T20_PLAYER as T3_1_,
        sportspers0_.GOALS as GOALS2_,
        sportspers0_.APPEARANCES as APPEARAN2_2_,
        sportspers0_.SEND_OFFS as SEND3_2_,
        sportspers0_.clazz_ as clazz_ 
    from
        ( select
            T20_PLAYER,
            ID,
            null as GOALS,
            NAME,
            null as APPEARANCES,
            RUNS,
            null as SEND_OFFS,
            WICKETS,
            1 as clazz_ 
        from
            CRICKETER 
        union
        select
            null as T20_PLAYER,
            ID,
            GOALS,
            NAME,
            APPEARANCES,
            null as RUNS,
            SEND_OFFS,
            null as WICKETS,
            2 as clazz_ 
        from
            FOOTBALLER 
    ) sportspers0_, ADDRESS address1_ 
where
    address1_.CITY like 'Ba%'
Id: 1
Name: Rahul Dravid
class is class com.inheritance.model.Cricketer
The query generated is similar to the earlier polymorphic query. The inner query simply does the union of the two tables. It is on this result set that the where clause is applied. Thus polymorphic associations is possible here.
 Property duplication has also been avoided
Thus the disadvantages observed in approach 1 are eliminated here. 
At the same time the sub-classes still retain their identity.
public static void getFootballerWithName() {
    Session session = sessionFactory.openSession();
    try {
        Query query = session.createQuery("from Footballer f where f.name like 'Tom%'");
        System.out.println("The Player is " + ((Footballer)query.uniqueResult()).getName());
    } catch (HibernateException e) {
        e.printStackTrace();
    } finally {
        session.close();
    }
}
The logs are as below:
3093 [main] DEBUG org.hibernate.engine.query.QueryPlanCache  - located HQL query
 plan in cache (from Footballer f where f.name like 'Tom%')
Hibernate: 
    select
        footballer0_.ID as ID0_,
        footballer0_.NAME as NAME0_,
        footballer0_.GOALS as GOALS2_,
        footballer0_.APPEARANCES as APPEARAN2_2_,
        footballer0_.SEND_OFFS as SEND3_2_ 
    from
        FOOTBALLER footballer0_ 
    where
        footballer0_.NAME like 'Tom%'

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

2 comments:

  1. you said that "certain SQL constraints will not work because the abstract class has no representation in the database" ... like what ?? can you give some examples

    ReplyDelete
  2. Hi,
    In the post you can see the Address-SportsPerson relation. Every address is associated with a Sportsperson. What this means is that there is need for column representing SportsPerson to have a foreign key constraint.
    But how to apply this constraint? Should it be in Cricketer table or Footballer table?
    This is the problem.

    ReplyDelete