-
Notifications
You must be signed in to change notification settings - Fork 300
Inheritance
Inheritance in Hector Object Mapper (HOM) is JPA style inheritance. Where JPA has several ways to implement inheritance, HOM only works with "single table" - meaning all derivations of the base class are stored in a single ColumnFamily row. At this time I feel this is sufficient because there is no rigid schema defined in Cassandra. Each object only stores in a Cassandra "row" what it needs. In traditional RDBMS there is a good argument for having multiple inheritance strategies because of wasting space or rows becoming too large to accommodate all possible derivations.
So let's start with an example modeling furniture. We have a chair, a table, and a couch - each a type of furniture. So we'll create four classes; Chair, Table, and Couch, each derived from a base class, Furniture:
package com.mycompany.furniture;
// imports omitted
@Entity
@Table(name="Furniture")
@Inheritance
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
public abstract class Furniture {
@Id
private int id;
@Column(name="material")
private String material;
@Column(name="color")
private String color;
// getters/setters required, but not showing to conserve space
}
package com.mycompany.furniture;
// imports omitted
@Entity
@DiscriminatorValue("chair")
public class Chair extends Furniture {
@Column(name="recliner")
private boolean recliner;
@Column(name="arms")
private boolean arms;
// getters/setters required, but not showing to conserve space
}
package com.mycompany.furniture;
// imports omitted
@Entity
@DiscriminatorValue("table")
public class BasicTable extends Furniture {
@Column(name="extendable")
private boolean extendable;
@Column(name="shape")
private String shape;
// getters/setters required, but not showing to conserve space
}
package com.mycompany.furniture;
// imports omitted
@Entity
@DiscriminatorValue("couch")
public class Couch extends Furniture {
@Column(name="foldOutBed")
private boolean foldOutBed;
@Column(name="numCushions")
private int numCushions;
// getters/setters required, but not showing to conserve space
}
package com.mycompany.furniture;
// imports omitted
@Entity
@DiscriminatorValue("table_desk")
public class Desk extends BasicTable {
@Column(name="numDrawers")
private int numDrawers;
// getters/setters required, but not showing to conserve space
}
With single table inheritance all derivations of the class hierarchy are persisted in the same ColumnFamily. The base class defines the ColumnFamily using @Table, and it is illegal to define @Table in any of its derived classes. There can be multiple levels of inheritance as long as each is annotated with @Entity and each defines a unique DiscriminatorValue (See class, Desk, above.)
The new annotations to discuss are:
- @Inheritance : defines base class of inheritance hierarchy
- @DiscriminatorColumn : the column name to use for discriminating between class types
- @DiscriminatorValue : a unique value for each class in the hierarchy
@Inheritance is required by the base class of the hierarchy and signals the mapper to persist the set of classes in the hierarchy based on the strategy. @Inheritance can specify an inheritance "strategy", but at this moment, only JPA style "single table" storage is available.
@DiscriminatorColumn is required by the base class of the hierarchy and defines the column name for storing a discriminator value. A type can also be specified (see Furniture class) for the column value. The acceptable values are defined by the enumeration, DiscriminatorType (STRING is the default.) (You may notice that the discriminator column is not defined as a POJO property anywhere in the hierarchy, even though it is stored in the ColumnFamily. This is by design because it is not needed from an application client perspective.)
@DiscriminatorValue is required by all classes in the hierarchy and defines the class' unique value within the hierarchy. The one exception is if the base class is abstract. This value is stored in the DiscriminatorColumn when the object is saved. It is used to determine the class type when loading data from Cassandra.
Saving is no different than non-inheritance. Use the EntityManager to save the object and as long as the classes are properly configured, everything will simply work!
Keyspace keyspace = HFactory.createKeyspace("TestKeyspace", cluster);
entityMgr = new EntityManager(keyspace, "com.mycompany.furniture");
Desk desk = new Desk();
desk.setId(4);
desk.setMaterial("pressBoard");
desk.setColor("black");
desk.setExtendable(false);
desk.setShape("rectangle");
desk.setNumDrawers(2);
entityMgr.persist(desk);
The above code sample will save to the ColumnFamily Furniture, row key = 4, the following columns: material, color, extendable, shape, numDrawers, and type. The discriminator column, type, will have a value of "table_desk".
To load the Desk object saved above is also very simple. The following code will use the EntityManager to load a piece of furniture:
Keyspace keyspace = HFactory.createKeyspace("TestKeyspace", cluster);
entityMgr = new EntityManager(keyspace, "com.mycompany.furniture");
Furniture furniturePiece = entityMgr.load(Furniture.class, 4);
System.out.println( "class type = " + furniturePiece.getClass().getName());