Archive for the ‘inheritance’ Tag
Handling Inheritance via Hibernate Annotations
Getting back to blogging after a long pause (I guess about a month or so) and in this post I am going to discuss how we can handle Java inheritance using Hibernate Annotations. In the process of doing this I discovered a flaw (or I guess the right word should be missing documentation) in the Hibernate documentation which is discussed below.
So I guess the first question is – why should Hibernate even bother trying to support inheritance ? Because, Hibernate is an Object Relational Mapping (ORM) tool and it is quite common to come up with a data model in which classes inherit from one another. For example, a Car and a Truck can be modelled as subclasses of a Vehicle class, a NetworkDevice and a Server can be modelled as subclasses of an ITAsset class, and in this post we discuss a model (for handling a portfolio of stocks) where a parent class – AccountTransaction – has two subclasses, MoneyTransaction and StockTransaction.
As always, all the code mentioned here is available via the Google Code Project – DalalStreet.
So this is how the object model looks like.
There are multiple ways in which such a relationship can be handled by Hibernate and I opted to use the “single table per class heirarchy” approach. This translates into the following annotations. The key annotations are the @Inheritance, @DiscriminatorColumn, and @DiscriminatorValue.
Source code for “AccountTransaction” entity :
@Entity
@Table (name="entity_transaction")
@Inheritance (strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn (name="transaction_type", discriminatorType=DiscriminatorType.STRING)
public abstract class AccountTransaction
{
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column (name = "transaction_id")
private Long id = null;
@Column (name = "transaction_type", insertable = false, updatable = false)
private String txType = null;
@Column (name = "transaction_date")
private Date txDate = null;
@Column (name = "transaction_desc")
private String txDescription = null;
@Column (name = "transaction_fee")
private Double txFee = null;
public AccountTransaction(String type)
{
txType = type;
txDate = new Date();
}
...
Source code for “StockTransaction” (DiscriminatorValue is “stock”) entity :
@Entity
@DiscriminatorValue("stock")
public class StockTransaction extends AccountTransaction
{
@Column (name = "is_sale")
private boolean isSale = false;
@Column (name = "stock_symbol")
private String stockSymbol = null;
@Column (name = "company_name")
private String companyName = null;
@Column (name = "num_shares")
private Integer numShares = 0;
@Column (name = "price_per_share")
private Double pricePerShare = 0.0;
public StockTransaction()
{
super("stock");
}
...
Source code for “MoneyTransaction” (DiscriminatorValue is “money”) entity :
@Entity
@DiscriminatorValue("money")
public class MoneyTransaction extends AccountTransaction
{
@Column (name = "is_deposit")
private boolean isDeposit = false;
@Column (name = "money_amount")
private Double moneyAmount = 0.0;
public MoneyTransaction()
{
super("money");
}
...
Pay special attention to the annotations that describe “transaction_type” – line 11 of the AccountTransaction.java – the “insertable” and “updatable” attributes need to be set to “false”, otherwise you will get a error like this (this is the part that is missing from Hibernate documentation that I mentioned at the beginning of this post) :
Caught an exception. Error message : null
java.lang.ExceptionInInitializerError
at org.ds.biz.user.HibernateUtil.(HibernateUtil.java:17)
at org.ds.biz.user.TestInheritance.main(TestInheritance.java:60)
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: org.ds.biz.user.StockTransaction column: transaction_type (should be mapped with insert=”false” update=”false”)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:652)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:674)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:696)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:450)
at org.hibernate.mapping.SingleTableSubclass.validate(SingleTableSubclass.java:43)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1108)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1293)
at org.ds.biz.user.HibernateUtil.(HibernateUtil.java:13)
… 1 more
The output schema from “ant schemaexport” looks something like this :
The values in the database looks like this :
Sample code using the Criteria API to retreive the Transaction objects (AccountTransaction, MoneyTransaction or StockTransaction) can be found in the TestInheritance class. The test class uses the different classes to retrieve the appropriate objects (see code snippet below) :
Criteria criteria1 = session.createCriteria(AccountTransaction.class);
List list1 = criteria1.list();
int size1 = list1.size();
// should print out size as 5
System.out.println("size of list (all transactions) : " + size1);
...
Criteria criteria2 = session.createCriteria(MoneyTransaction.class);
List list2 = criteria2.list();
int size2 = list2.size();
// should print out size as 2
System.out.println("size of list (money transactions) : " + size2);
...
Criteria criteria3 = session.createCriteria(StockTransaction.class);
List list3 = criteria3.list();
int size3 = list3.size();
// should print out size as 3
System.out.println("size of list (stock transactions) : " + size3);
Here are the links to the files in case you want to take a detailed look at the code.
Happy coding, until next time … Take care.
Comments (5)

