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:
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.
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" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.inheritance.model"> <class name="SportsPerson" table="SPORTS_PERSON"> <id name="id" type="long"> <column name="ID" /> <generator class="native" /> </id> <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> <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" /> </subclass> </class> </hibernate-mapping>
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 ert 0: insert into SPORTS_PERSON (NAME, RUNS, WICKETS, T20_PLAYER, SPORT_TYPE, I 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 ert 0: insert into SPORTS_PERSON (NAME, GOALS, APPEARANCES, SEND_OFFS, SPORT_TYP 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 ete
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:
On executing 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 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 - insert into SPORTS_PERSON (NAME, RUNS, WICKETS, T20_PLAYER, SPORT_TYPE) values (?, ?, ?, ?, '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.
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 -
select
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_
from
SPORTS_PERSON sportspers0_
The sql created for the query on subclass is as below.
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.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 - select footballer0_.ID as ID0_, footballer0_.NAME as NAME0_, footballer0_.GOALS as GOALS0_, footballer0_.APPEARANCES as APPEARAN8_0_, footballer0_.SEND_OFFS as SEND9_0_ from SPORTS_PERSON footballer0_ where footballer0_.SPORT_TYPE='footballer'The major drawbacks of this approach are :
- Inability to add null constraints on the properties of the sub classes.
- Need to modify the same table every time a new sub class is created.
- Existence of unrelated columns such as GOALS and RUNS in the same table.
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