UserType for Polymorphic AssociationsThe default mapping mechanism for <any> mapping is the class name. This custom type allows for creating integer values for each class, and mapping the classes onto the integer values. Thus the table needs to have a integer column and a column corresponding to the primary key of the other table. Using this class requires creating a derived class as suggested below. Sample mapping will be as follows :
Scenario An invoice table stores a reference to a Customer. Customer is an abstract base class and has two derived classes ExternalCustomer and InternalCustomer. Table-per-concreteclass mapping is used. Invoice table has two columns for mapping the customer - customerType and customerId. CustomerType == 1 implies InternalCustomer, CustomerType == 2 implies ExternalCustomer. Invoice class needs to support a method called getCustomer()
<!-- Mapping snippet in Invoice table -->
<any name="customer" id-type="long" meta-type="com.example.hibernate.customtype.CustomerTypeMapper">
<column name="customerType"/>
<column name="customerId"/>
</any>
Sample Class (to be created by End User )
public class CustomerTypeMapper extends ClassToIntegerMapper
{
static public final Integer INTERNAL_CUSTOMER = new Integer(1);
static public final Integer EXTERNAL_CUSTOMER = new Integer(2);
static final private ClassToIntValue[] mappings =
{
new ClassToIntValue(INTERNAL_CUSTOMER, InternalCustomer.class),
new ClassToIntValue(EXTERNAL_CUSTOMER, ExternalCustomer.class)
};
public ClassToIntValue[] getMappings()
{
return mappings;
}
}
Utility class used by the Custom Type
class ClassToIntValue
{
private Integer intVal;
private Class clazz;
public ClassToIntValue(Integer intVal, Class clazz)
{
this.intVal = intVal;
this.clazz = clazz;
}
public Integer getIntVal()
{
return intVal;
}
public Class getClazz()
{
return clazz;
}
}
The Custom Type itself
package com.example.hibernate.customtype;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import net.sf.hibernate.Hibernate;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.UserType;
/**
* @author Dhananjay Nene
*/
abstract public class ClassToIntegerMapper implements UserType
{
static final private int[] TYPES = {Types.INTEGER };
abstract public ClassToIntValue[] getMappings();
public int[] sqlTypes()
{
return TYPES;
}
public Class returnedClass()
{
return java.lang.Class.class;
}
public boolean equals(Object arg0, Object arg1) throws HibernateException
{
if (arg0 == arg1) return true;
if ((arg0 == null) || (arg1 == null)) return false;
return ((java.lang.Class) arg0).equals((java.lang.Class) arg1);
}
public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
throws HibernateException, SQLException
{
Integer result = (Integer) Hibernate.INTEGER.nullSafeGet(rs,names[0]);
if(result == null)
{
return null;
}
ClassToIntValue[] mappings = getMappings();
for(int i = 0 ; i < mappings.length ; i++)
{
if (result.equals(mappings[i].getIntVal()))
{
return mappings[i].getClazz();
}
}
return null;
}
public void nullSafeSet(PreparedStatement stmt, Object value, int index)
throws HibernateException, SQLException
{
java.lang.Class clazz = (java.lang.Class) value;
if (clazz == null)
{
Hibernate.INTEGER.nullSafeSet(stmt,null,index);
return;
}
Integer intval = null;
ClassToIntValue[] mappings = getMappings();
for(int i = 0 ; i < mappings.length ; i++)
{
if (clazz.equals(mappings[i].getClazz()))
{
intval = mappings[i].getIntVal();
}
}
Hibernate.INTEGER.nullSafeSet(stmt,intval,index);
}
public Object deepCopy(Object arg0) throws HibernateException
{
if (arg0 == null) return null;
Class clazz = (Class)arg0;
Class newClazz = (Class) Hibernate.CLASS.deepCopy(clazz);
return newClazz;
}
public boolean isMutable()
{
return false;
}
}
|