The first approach will only use one table. From the class diagram below you can see that a plant has some fields and that each subclass adds a field, for example flower adds the color. This results can be found in one big table holding all the fields. The disadvantage of this approach is that fields in the subclasses Tree and Flower must accept null values. A flower will not set the field has_fruits. So has_fruits must allow null values. Full source code is provided in the package: de.laliluna.inheritance.singletable
Hibernate distinguishes the different classes using a discriminator column. When the column type contains KitchenMouse then the row will be treated as KitchenMouse.
Annotation mapping.
import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorType; import javax.persistence.Entity; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; .......... snip ....... @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "type") public class Mouse { @Id @GeneratedValue private Integer id; private String name; ........
The LibraryMouse and KitchenMouse class are fairly simple. They inherit from Mouse and add only their specific attributes.
@Entity public class KitchenMouse extends Mouse{ private String favouriteCheese; ......
Optionally you can define a different discriminator value. By default the class name is used.
@Entity @DiscriminatorValue("lm") public class LibraryMouse extends Mouse{ private String favouriteBook; .....
Other classes can have relations to the subclass (Flower) as well as to the parent class (Plant).
import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; ........ snip ........ @Entity public class House implements Serializable { .... snip ..... @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "garden_plant_id") private Set<Mouse> allMice = new HashSet<Mouse>(); @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "garden_flower_id") private Set<KitchenMouse> kitchenMice = new HashSet<KitchenMouse>();
XML mapping.
<hibernate-mapping package="de.laliluna.inheritance.singletable" > <class name="Mouse" > ......... snip ....... <discriminator column="plant_type" type="string"></discriminator> <subclass name="KitchenMouse" discriminator-value="KitchenMouse"> <property name="favouriteCheese" /> </subclass> <subclass name="LibraryMouse" discriminator-value="LibraryMouse"> <property name="favouriteBook"/> </subclass> </class> </hibernate-mapping>
Other classes can have relations to the subclass (KitchenMouse) as well as to the parent class (Mouse).
<hibernate-mapping package="de.laliluna.inheritance.singletable" > <class name="House" > ...... snip ........ <set name="allMice" table="house_mouse"> <key column="house_id"></key> <many-to-many class="Mouse"> <column name="mouse_id"></column> </many-to-many> </set> <set name="kitchenMice" table="house_kitchen_mice" > <key column="house_id"></key> <many-to-many class="KitchenMouse"> <column name="kitchen_mouse_id"></column> </many-to-many> </set> </class> </hibernate-mapping>
Samples of use:
/* create and set relation */ House house = new House(); Mouse bea = new Mouse("Bea"); house.getMice().add(bea); KitchenMouse john = new KitchenMouse("John"); house.getMice().add(john); LibraryMouse tim = new LibraryMouse("Tim"); house.getMice().add(tim); session.save(bea); session.save(john); session.save(tim); session.save(house); /* get all kind of mice*/ List<Mouse> result = session.createQuery("select m from Mouse m") .list(); /* select all kitchen mice who like Gauda cheese blue flowers */ List<KitchenMouse result = session .createQuery("select m from KitchenMouse m where m.favouriteCheese ='Gauda'") .list(); /* select all mice of type LibraryMouse */ List<LibraryMouse> result = session .createQuery("select m from Mouse m where type(m) = LibraryMouse ") .list();