Hi Steve,
First of all I would like to thank you very much for taking the time to reply to my post. Your advice was very useful and to-the-point and provided a very nice opportunity for me to have another look at the docs (and source code) and learn a couple of things.
I will try to explain a few things about our needs and what we did, following your advice, in order to have everything working with the Hibernate 3.6.8 distribution, so that other users may find this as a reference. The aim is for every class appearing in a hibernate mapping file to be an interface and also every Entity to have a composite-id.
A typical Entity example is the following (both Invoice and InvoiceKey are interfaces).
Code:
<class name="Invoice" table="TB1SINVO">
<composite-id name="key" class="InvoiceKey">
<key-property name="invoiceNo">
<column name="INVOICE_NO" sql-type="integer" not-null="true"/>
</key-property>
</composite-id>
...
</class>
The session scoped Interceptor approach that I described in my original post works nicely for Entities, but falls short when we deal with Components and therefore with composite-ids as well. Of course the custom Tuplizer registration approach you suggested is much more elegant, so we ended up not needing the Interceptor at all. The code below is a basic stub (I ommit any checks and fall-back calls to super-class methods etc) and for simplicity's sake assume that Factory is a facade to a factory implementation that knows how to resolve concrete implementation types from interface types and vice-versa.
For Entities, a PojoEntityTuplizer sub-class and an Instantiator implementation did the trick. The tuplizer is registered in the hibernate configuration with which the SessionFactory is created:
Code:
public class MyPojoEntityTuplizer extends PojoEntityTuplizer {
public MyPojoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
super(entityMetamodel, mappedEntity);
}
@Override
public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) {
return Factory.declarationTypeName(entityInstance.getClass());
}
@Override
protected Instantiator buildInstantiator(PersistentClass persistentClass) {
return new MyPojoEntityInstantiator(persistentClass);
}
public class MyPojoEntityInstantiator implements Instantiator {
private Class<?> entityClass;
public MyPojoEntityInstantiator(PersistentClass persistentClass) {
this.entityClass = persistentClass.getMappedClass();
}
public Object instantiate(Serializable id) {
return Factory.create(entityClass);
}
public Object instantiate() {
throw new RuntimeException("No Entities without ids allowed.");
}
public boolean isInstance(Object object) {
return entityClass.isInstance(object);
}
}
//And in SesionFactory initialization
Configuration conf = ...
conf.getEntityTuplizerFactory().registerDefaultTuplizerClass(EntityMode.POJO, MyPojoEntityTuplizer.class);
...
When dealing with components, from what I can gather, there is no way to programmatically register custom ComponentTuplizers, for the time being at least. Instead one can specify the custom tuplizer in the <component> XML element as a nested <tuplizer> element. All this is explained very thoroughly in the docs, as you pointed out. However, the only way I found for registering a custom tuplizer for a composite-id is by actually modifying the hibernate-mapping-3.0.dtd of the hibernate3.jar in the distribution (after getting over the InvalidMappingException everything worked - mad props for that). Below is the DTD modification, a mapping example for a Component and a composite-id and the PojoComponentTuplizer sub-class and Instantiator implementation.
Code:
public class MyPojoComponentTuplizer extends PojoComponentTuplizer {
public MyPojoComponentTuplizer(Component component) {
super(component);
}
protected Instantiator buildInstantiator(Component component) {
return new MyPojoComponentInstantiator(component);
}
}
public class MyPojoComponentInstantiator implements Instantiator {
private Class<?> componentClass;
public MyPojoComponentInstantiator(Component component) {
this.componentClass = component.getComponentClass();
}
public Object instantiate(Serializable id) {
throw new RuntimeException("No components with ids allowed.");
}
public boolean isInstance(Object object) {
return componentClass.isInstance(object);
}
public Object instantiate() {
return Factory.create(componentClass);
}
}
...
<!ELEMENT composite-id ( meta*, tuplizer*, (key-property|key-many-to-one)+, generator? )> <!-- (added tuplizer*) -->
...
<composite-id name="key" class="FooKey">
<tuplizer class="xxx.hibernate.tuple.MyPojoComponentTuplizer" entity-mode="pojo"/>
<key-property name="no">
<column name="foo_no" sql-type="integer" not-null="true"/>
</key-property>
</composite-id>
<component name="info" class="xxx.test.def.samples.FooInfo">
<tuplizer class="xxx.hibernate.tuple.MyPojoComponentTuplizer" entity-mode="pojo"/>
<property name="barCode" type="string">
<column name="BARCODE" sql-type="varchar(100)"></column>
</property>
</component>
The last intricacy our unit tests came across has to do with inheritance hierarchies that have a discriminator column. The configuration that worked is in the example below. The documentation examples do not use the abstract attribute of the subclass element, which the DTD defines to be implied, with no default value, i.e. not required. In any case, in our case everything worked after specifying explicitly abstract="false" to the sub-classes. The problem was, specifically, that when the SQL to read the Entities was generated, a "1=2" was appended by the InFragment class, because the SingleTableEntityPersister reached the conclusion that the Entity is abstract, and, therefore, did not provide SQL for the discriminator column. Here is what worked:
Code:
<class name="BarFoo" table="BarFoo">
...
<discriminator column="foo_or_bar" type="string"/>
<property name="baseProp" type="java.lang.String">
<column name="baseProp" />
</property>
<subclass name="Bar" discriminator-value="bar" abstract="false">
<property name="bar" type="java.lang.String">
<column name="bar" />
</property>
</subclass>
<subclass name="Foo" discriminator-value="street" abstract="false">
<property name="foo" type="int">
<column name="foo" />
</property>
</subclass>
</class>
Once again, thank you very much for your time. A quick google search showed that the DTD issue is/should be somewhat known (I found the very similar HHH-3275 to be resolved), so nothing more to do there. I hope that this post might be helpful for other users.
Regards // Dimitris