-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Non intrusive way of using interfaces in hbm files
PostPosted: Tue Nov 09, 2010 12:13 pm 
Newbie

Joined: Tue Nov 09, 2010 11:52 am
Posts: 3
Hi all,

I am working in a project where it is essential to be able to create implementation types on runtime and use only interface types in hibernate mapping files. All types are created by a Factory implementation.
We have successfully used Interceptors in order to
(a) assist hibernate in resolving the correct hbm file for a runtime object (mapping the implementation type to the interface)
(b) assist hibernate in instantiating the correct implementation type for a type appearing in an hbm file as an interface

Code:
@Override
public String getEntityName(Object obj) {
   Class<?> type = obj.getClass();
   return Factory.declarationTypeName(type);
}

public Object instantiate(String entityName, EntityMode entityMode, Serializable id) {
        //factory instantiation of the correct implementation type
   PersistentObject<Key> po = (PersistentObject<Key>) Factory.create(entityName);
   po.setKey((Key) id);
   return po;
}


This works quite well. However, there are cases where instantiation type events are not caught by the interceptor. This happens, for instance, during compilation of mapping files on factory instantiation. As I understand it, Hibernate attempts to resolve the Class<?> of the class type specified in a <component> declaration on the instantiation of the SessionFactory. For example, assume the following component mapping definition, where product.setup.po.Range is an interface.

Code:
<component name="rowRange" class="product.setup.po.Range">
   <property name="from" not-null="true" type="string">
      <column name="ROW_FROM" sql-type="varchar(32)" not-null="false"/>
   </property>
   <property name="to" not-null="true" type="string">
      <column name="ROW_TO" sql-type="varchar(32)" not-null="false"/>
   </property>
</component>


Altering the org.hibernate.mapping.Component.getComponentClass() method to:

Code:
public Class getComponentClass() {
   return Factory.getType(getComponentClassName());
}


resolves this problem, but this approach is obviously more intrusive than we'd like (for example, there are clear maintance issues whenever we choose to upgrade hibernate (currently using Hibernate 3.5.3 by the way)).

Is there a programming extension point to handle such cases in an API non-obtrusive way, like we did with the Session Interceptor?

Thanks in advance for any responses,

Dimitris


Top
 Profile  
 
 Post subject: Re: Non intrusive way of using interfaces in hbm files
PostPosted: Fri Nov 12, 2010 11:05 am 
Newbie

Joined: Tue Nov 09, 2010 11:52 am
Posts: 3
(Gentle bump) Any ideas?


Top
 Profile  
 
 Post subject: Re: Non intrusive way of using interfaces in hbm files
PostPosted: Tue Nov 22, 2011 5:28 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
You would use a contract called org.hibernate.tuple.Tuplizer, specifically org.hibernate.tuple.component.ComponentTuplizer and org.hibernate.tuple.entity.EntityTuplizer. Good to know about org.hibernate.EntityNameResolver as well.

This is all covered in the docs.


Top
 Profile  
 
 Post subject: Re: Non intrusive way of using interfaces in hbm files
PostPosted: Tue Nov 22, 2011 5:32 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
BTW, there are even a set of tests doing *exactly* what you are looking to do based on your explanation. The base test package is org.hibernate.test.dynamicentity. There are 3 approaches outlined there, one each per sub-package. The recommended approach would be org.hibernate.test.dynamicentity.tuplizer2


Top
 Profile  
 
 Post subject: Re: Non intrusive way of using interfaces in hbm files
PostPosted: Thu Nov 24, 2011 4:49 pm 
Newbie

Joined: Tue Nov 09, 2010 11:52 am
Posts: 3
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


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.