Hello there,
I'm rather new to Hibernate (and absolutely to the forums here) but I already spent a lot of time on it and a major application is already running powered by Hibernate - but I'm experiencing some error that I cannot seem to be able to solve without changing my application's model classes.
You can find the mapping document and some code below. But first the problem description (with symbolic class names):
I use the Template Method Pattern in some classes. An abstract base B class defines a template method and within this method it calls some abstract method that tells it how to behave, in my case: whether to do a certain check or not and in case it does the checking there could be an exception thrown.
So I have 2 subclasses X and Y of this abstract base class and furthermore one of those 2 classes is a subclass of the other (Y subclass of X; you can see that easily from the code and mapping documents).
Now what happens: I call the template method for subclass Y but the Hibernate CGLIB proxy seems to be subclassing class X and so breaks my code because subclass X defines a different implementation of the abstract method than Y!
At least that is what I see in the stack trace.
Do you have ideas how I could fix this without code changes? Or is there even something wrong with my code?
I know that I could use some other pattern than Template Method (like Strategy), but I think that would be a bit clumsy in the code.
I'd appreciate some help =)
Kind regards,
Denis
Hibernate version: hibernate-3.0
Mapping documents:
Code:
<hibernate-mapping>
<class name="xxx.model.impl.PopulationImpl"
table="ageofpirates.population" discriminator-value="1">
<id name="populationId" type="integer">
<column name="population_id" />
<generator class="native" />
</id>
<discriminator column="discr" type="integer" />
<version name="version" column="version" />
... some properties ...
<subclass
name="xxx.model.impl.AnotherPopulationImpl"
discriminator-value="2" />
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():Code:
public abstract class ItemMapping
{
...
/**
* Implement this method to achieve checked or unchecked behaviour.
*
* @return true if checking should be done.
*/
protected abstract boolean doItemLimitChecking();
/**
* The template method.
*/
protected final void addItemsUnmapped(String itemType, int num)
{
...
if (doItemLimitChecking())
{
// do something that might throw an exception
...
}
...
}
...
}
---------
public class PopulationImpl extends ItemMapping implements Population
{
...
public void transferTo(Map<String, Integer> m, Population target)
{
for (String key : m.keySet())
{
subtract(key, m.get(key));
target.add(key, m.get(key));
}
}
public void add(String type, Integer num)
{
super.addItemsUnmapped(type, num);
}
...
@Override
protected boolean doItemLimitChecking()
{
return true;
}
}
------------
public class AnotherPopulationImpl extends PopulationImpl
{
@Override
protected boolean doItemLimitChecking()
{
return false;
}
}
Full stack trace of any exception that occurs:Code:
at xxx.model.util.ItemMapping.doCheck(ItemMapping.java:126)
at xxx.model.util.ItemMapping.addItemsUnmapped(ItemMapping.java:69)
at xxx.model.impl.PopulationImpl.add(PopulationImpl.java:121)
at xxx.model.impl.PopulationImpl$$FastClassByCGLIB$$48c488b6.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.hibernate.proxy.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:137)
at xxx.model.impl.PopulationImpl$$EnhancerByCGLIB$$8fb3f49f.add(<generated>)
at xxx.model.impl.PopulationImpl.transferTo(PopulationImpl.java:101)
at xxx.model.impl.PopulationImpl$$FastClassByCGLIB$$48c488b6.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.hibernate.proxy.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:137)
at xxx.model.impl.IslePopulationImpl$$EnhancerByCGLIB$$477c785a.transferTo(<generated>)
at xxx.model.Isle.removeCaptainFromShip(Isle.java:831)
at xxx.model.Isle$$FastClassByCGLIB$$283125a8.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.hibernate.proxy.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:137)
at xxx.model.Isle$$EnhancerByCGLIB$$392133e1.removeCaptainFromShip(<generated>)
at xxx.business.sf.impl.GameSessionFacadeImpl.removeCaptainFromShip(GameSessionFacadeImpl.java:893)
Name and version of the database you are using: that's unimportant
The generated SQL (show_sql=true): nothing to do with generated SQL
Debug level Hibernate log excerpt: nothing interesting there