Saturday, March 14, 2009

Hibernate key generator per model

Hibernate is designed to create a key generator instance for each model or each inheritance class tree. The logic is in the SessionFactoryImpl class:


while ( classes.hasNext() ) {
PersistentClass model = (PersistentClass) classes.next();
if ( !model.isInherited() ) {
IdentifierGenerator generator = model.getIdentifier().createIdentifierGenerator(settings.getDialect(),
settings.getDefaultCatalogName(), settings.getDefaultSchemaName(),
(RootClass) model);
identifierGenerators.put( model.getEntityName(), generator );
}
}


This is quite flexible and customizable. Each table can configure a different sequence High Low key generator.

Same EntityKey for parent and child objects

This happens when modeling inheritance classes. If parent and child object which all extend same root class happens to use the same value for id, that's pretty fine since parent and child are different tables, but Hibernate will actually generate same entity key for them. That will lead to org.hibernate.WrongClassException.

After tracing into Hibernate, the reason looks to me is that Hibernate is using rootEntityName to generate hashcode for all subclasses. It will break if subclasses are using same id.

For example, Department (Dept) and Employee (Emp) class all extend from class Entity which has id attribute. When loading department and join fetching employees, if id in Dept table happens to have the same id in Emp table, that won't work. As of Hibernate 3.3.1 GA, in org.hibernate.engine.EntityKey:



private int generateHashCode() {
int result = 17;
result = 37 * result + rootEntityName.hashCode();
result = 37 * result + identifierType.getHashCode( identifier, entityMode, factory );
return result;
}



So the Dept and Emp are getting the same hashcode, and when loading Emp, Hibernate think it's already in cache, but that's Dept instance.

There is actually a ticket (not assigned yet) in Hibernate Jira: http://opensource.atlassian.com/projects/hibernate/browse/HHH-3445

It's easy to patch, but not sure whether it will break other parts in Hibernate.

Friday, March 13, 2009

Duplicated resultsets by join fetch

Hibernate's join fetch is supposed to reduce resultset returned. But it returns duplicated results and behavior is not consistent between specifying join fetch in HQL and mapping.

For example, Department (Dept) has many employees (Emp) and sales deparment has 5 employees:
  • in HQL: from Dept d join fetch d.emps where d.name = 'sales' will return 5 exact same department records, each with totally initialized employees
  • in mapping: <set name="emps" table="EMP" inverse="true" fetch="join"> will only return 1 department with 5 initialized employees. This works as expected.
Hibernate has some suggestions: http://www.hibernate.org/117.html#A12

One possible workaround is using setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY). This works, although I feel they should fix it in the core.

Also see: http://glueclue.blogspot.com/2006/02/hibernate-duplicates-with-join-fetch.html

Sunday, March 8, 2009