Sunday, June 12, 2005

OJB to Hibernate 3 Migration

I've spent far too long on this already so I'll make this brief but still try to help others out who want to go through the same process.

To convert your repository_user.xml file to a hibernate mapping file, try using this stylesheet. It's not completely automatic. I still had to add inverse="true" attributes in a few places were we're using bi-directional associations (see section 2.3.6 of the Hibernate Reference Documentation).

I also needed to tweak a few proxy attributes because we're using Castor XML which turned out to make things much more difficult. Hibernate uses CGlib for what it calls proxy generation. The problem is that it changes the class signature of the original class it's proxying for. When the modified classes get sent to Castor it complains about illegal/invalid characters because the class name is now suffixed with $$EnhancerByCGLIB$$. I had to patch the Castor source with a hack. Still looking for a long term solution.

Before all that mess I spent some time trying to figure out why Hibernate wasn't reading my JNDI datasource resource. It complained about an empty driver setting and a null URL. For those issues, refer to this helpful forum thread. Not the solution I was hoping for but it works.

Update:

I'm posting the change I made to Castor in response to a comment but it's pretty embarrassing :) These lines went below line 1293 of org.exolab.castor.xml.Marshaller in version 0.9.5.3 (notice the second if condition):

if (!containerField) {
//-- Make sure qName is not null
if (qName == null) {
//-- hopefully this never happens, but if it does, it means
//-- we have a bug in our naming logic
String err = "Error in deriving name for type: " +
_class.getName() + ", please report bug to: " +
"http://castor.exolab.org.";
throw new IllegalStateException(err);
}
if( qName.indexOf("$$-enhancer-by-cGLIB$$") != -1 ) {
return;
}
handler.startElement(qName, atts);
}

Lovely solution, right? Hope it helps you, Michael.

4 comments:

Matt said...

Hibernate's error messages loading .hbm.xml files are about as useful as "Error #2" in Mac Classic.

Hibernate team: better error messages, please?

Michael said...

"I also needed to tweak a few proxy attributes because we're using Castor XML which turned out to make things much more difficult. Hibernate uses CGlib for what it calls proxy generation. The problem is that it changes the class signature of the original class it's proxying for. When the modified classes get sent to Castor it complains about illegal/invalid characters because the class name is now suffixed with $$EnhancerByCGLIB$$. I had to patch the Castor source with a hack. Still looking for a long term solution."

any chance you could share what changes you had to make to Castor? We have the same problem and are hitting our heads against the wall!

Thanks,
Michael

Michael said...

Thanks Matt,

We did something down the same lines and it seems to work (for now)..
;-)

private Object getObjectID(Object object)
throws MarshalException
{
if (object == null) return null;

Class clazz = object.getClass();
Object id = null;
if( clazz.getName().indexOf("CGLIB") != -1 )
{
String realClassname = clazz.getName().substring(0,
clazz.getName().indexOf("$$"));
try
{
Class realClass = Class.forName(realClassname);
clazz = realClass;
}
catch (ClassNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}

Jean-Paul said...

Hi Martin,

thanks for the XSLT stylesheet you supplied. It helped me convert an OJB repository that's about 11000 lines of xml code! However, the stylesheet did not work out of the box as it contained a few errors:

The first one is at the block that matches the reference-descriptor. The current code for the column attribute is as follows:{../field-decscriptor[@name = foreignkey/@field-ref}/@column. This gives an XPath error or Stylesheet cannot be compiled error. Instead it should have been:{../field-decscriptor[@name = foreignkey/@field-ref]/@column}



The second error is related to the block that matches the document's root(e.g.: /). In there, there is an xsl tag that will only include user classes. Running the stylesheet like that returns an xml document with just the hibernate-mapping element in it. So I had to modify that block to include the following:

<xsl:apply-templates select="class-descriptor" />
<xsl:apply-templates select="field-descriptor" />
<xsl:apply-templates select="collection-descriptor" />
<xsl:apply-templates select="reference-descriptor" />
<xsl:apply-templates select="extent-class" />


After re-hacking the xslt file, I got it to compile and produce a hibernate mapping file without a problem.

Cheers,

Jean-Paul H.