Well I can see why you suggested I rein in... just for my trivial example I wound up adding four new pretty much identical classes at 75 lines each to my codebase, whose sole contribution was code bloat to work around the fact Hibernate can't reflect a property used as an Id as it can for any other attribute.
I'm sure with some refactoring I could reduce the redundancy but that doesn't change the fact that now I have more code to maintain and more opportunities to introduce defects. Hibernate is most attractive to me when I write almost no custom code to support it, otherwise I may as well go back to JDO/DAO-DTO patterns with a JCS framework.
What is the reasoning for Custom Ids? Is this perhaps overkill in my particular case? I'm simply strongly typing/refining a base identity class which just wraps a single attribute - that single attribute maps to a table's key column. In some cases a refined identity class maps to an integer column and in other cases to a string column - but the behavior for all is identical.
On top of all this Hibernate failed to map the custom type (or I failed to code it correctly, more likely!).
Here is the error, and farther down the custom type:
Code:
[ERROR] Configuration - -Could not compile the mapping document <net.sf.hibernate.MappingException: Cannot instantiate custom type: com.product.persistence.hibernate.CompanyIdType>net.sf.hibernate.MappingException: Cannot instantiate custom type: com.product.persistence.hibernate.CompanyIdType
at net.sf.hibernate.type.CustomType.<init>(CustomType.java:43)
at net.sf.hibernate.type.TypeFactory.hueristicType(TypeFactory.java:149)
at net.sf.hibernate.cfg.Binder.getTypeFromXML(Binder.java:787)
at net.sf.hibernate.cfg.Binder.bindValue(Binder.java:354)
at net.sf.hibernate.cfg.Binder.bindRootClass(Binder.java:224)
at net.sf.hibernate.cfg.Binder.bindRoot(Binder.java:1095)
at net.sf.hibernate.cfg.Configuration.add(Configuration.java:230)
at net.sf.hibernate.cfg.Configuration.addInputStream(Configuration.java:252)
at net.sf.hibernate.cfg.Configuration.addClass(Configuration.java:286)
at com.product.persistence.hibernate.ProductPersistenceTest.<clinit>(ProductPersistenceTest.java:44)
at com.product.persistence.hibernate.TestHibernate.main(TestHibernate.java:32)
java.lang.ExceptionInInitializerError: java.lang.RuntimeException: couldn't get connection
at com.product.persistence.hibernate.ProductPersistenceTest.<clinit>(ProductPersistenceTest.java:54)
at com.product.persistence.hibernate.TestHibernate.main(TestHibernate.java:32)
Exception in thread "main"
Here is the custom type - the example Greg linked appeared to have syntax errors so it's possible I introduced defect by tweaking the code.Code:
import java.sql.*;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.UserType;
import com.product.CompanyId;
public abstract class CompanyIdType
implements UserType
{
private static final int[] SQL_TYPES =
new int[] { Types.VARCHAR }
;
public int[] sqltypes() {
return SQL_TYPES;
}
public boolean isMutable() {
return false;
}
public Class returnedClass()
{
return CompanyId.class;
}
public boolean equals(Object x, Object y) {
return (x==y) || ( x!=null && y!=null && x.equals(y) );
}
public Object deepCopy(Object value) {
return value;
}
public Object nullSafeGet(
ResultSet rs,
String[] names,
Object owner
)
throws HibernateException, SQLException
{
String id = rs.getString(names[0]);
if ( rs.wasNull() ) return null;
return new CompanyId(id);
}
public void nullSafeSet(
PreparedStatement st,
Object value,
int index
)
throws HibernateException, SQLException
{
if (value==null) {
st.setNull(index, Types.VARCHAR);
}
else {
st.setString( index, ( (CompanyId) value ).getValue() );
}
}
}
And here is the HBM.Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="com.product.CompanyDto" table="CUBEPRDCOMPANY">
<id
name="Id"
column="COMPANYID"
type="com.product.persistence.hibernate.CompanyIdType"
>
<generator class="assigned"/>
</id>
<property
name="Description"
column="COMPANYDESC"
type="string"
length="50"
/>
<property
name="TotalSales"
column="SALES"
type="float"
/>
<!-- FK to child Departments -->
<set
name="ProductDepartmentList"
cascade="all"
inverse="true"
lazy="true"
>
<key column="COMPANYID"/>
<one-to-many class="com.product.DepartmentDto"/>
</set>
</class>
</hibernate-mapping>