[
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2318?page=c...
]
Lars J. Nilsson commented on HHH-2318:
--------------------------------------
Bumping this issue. I currently have a setup where one class loader (lets call it
"B") exposes JPA interfaces as a service towards modules in a sibling class
loader ("C"). These two class loaders share the parent class loader
("A") which load the JPA classes but not the hibernate classes. It appears this
issue stops us from from using lazy loading.
Is there any way to disable lazy loading in configuration when using JPA? Currently
I've patched the hibernate to forcibly disable lazy loading, which is of course not
very desirable.
Sometimes wrong classloader used for proxy interfaces
-----------------------------------------------------
Key: HHH-2318
URL:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2318
Project: Hibernate3
Issue Type: Bug
Components: core
Affects Versions: 3.2.1
Reporter: Jan Wiemer
Attachments: ProxyIfcNamespaceProblem.zip
For some of our business classes we used a Mapping declaring the class to be lazy
initialized and providing a proxy interface like e.g.:
<class name="TestClassImpl" proxy="TestClass"
table="testTable" lazy="true"> ... </class>
Using classes mapped this way - e.g. as endpoint of a one to one relation - sporadically
leads to exceptions like the following:
org.hibernate.PropertyAccessException: IllegalArgumentException occurred while calling
setter of test.AbstractTestClass2Impl.testField
at
org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:104)
at
org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:337)
at
org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:200)
at
org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:3514)
at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:129)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:842)
at org.hibernate.loader.Loader.doQuery(Loader.java:717)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224)
at org.hibernate.loader.Loader.doList(Loader.java:2211)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2095)
at org.hibernate.loader.Loader.list(Loader.java:2090)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:388)
at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:338)
at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:172)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1121)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
at org.hibernate.impl.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:804)
Note that the application is residing in a different classloader than hibernate (and
CGLIB...).
Examining the situation we see that the value passed to the setter was a CGLIB proxy.
Internally the proxy stores an array of interfaces the proxy should implement. In our
situation this interface contains the HibernateProxy interface and our business interface
provided as proxy interface in the mapping. In this interface array we checked the
classloader of the interfaces. As expected the HibernateProxy interface is loaded by the
system classloader and our interface was loaded by our custom classloader. However
examining the actual interfaces of the proxy (with
proxy.getClass().getInterfaces()[i].getClassLoader() for all i) shows that all interfaces
are loaded with the system classloader. This causes the exception above.
Doing some more experiments we experience that the problem does not occur all the time.
Sometimes the actual proxy interfaces are as expected (the HibernateProxy interface is
loaded by the system classloader and our interface was loaded by our custom classloader).
We notice that each time the test failed the HibernateProxy was the first interface in the
interface array stored in the proxy.
Some experiments with the CGLIB (the Enhancer class) shows us that (if there is no
superclass given) they use the classloader of the first passed interface as default
classloader (compare method net.sf.cglib.proxy.Enhancer.getDefaultClassLoader()).
Finally we find out that hibernate passes the proxy interfaces in an arbitrary order
since they are using a HashSet for the proxy interfaces in the method
org.hibernate.tuple.entity.PojoEntityTuplizer.buildProxyFactory(PersistentClass
persistentClass, Getter idGetter, Setter idSetter). This Hash set is passed to the method
org.hibernate.proxy.pojo.cglib.CGLIB_ProxyFactory.postInstantiate(...). There it is simply
converted to an Array (leading to a randomized order).
As a workaround it is possible to patch the class
org.hibernate.proxy.pojo.cglib.CGLIB_ProxyFactory and add reorganize the array if the
HibernateProxy interface is the first one:
this.interfaces = (Class[]) interfaces.toArray(NO_CLASSES);
//----->PATCH<--------
if(this.interfaces.length > 1) {
Class firstIfc = this.interfaces[0];
if(firstIfc.getName().startsWith("org.hibernate")) {
this.interfaces[0] = this.interfaces[1];
this.interfaces[1] = firstIfc;
System.err.println("Replace: " + firstIfc.getName() + " by "
+ this.interfaces[0].getName());
}
}
//--------------------
After applying this patch everything woks as expected.
Compare with the discussion in:
http://forum.hibernate.org/viewtopic.php?p=2334617#2334617
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://opensource.atlassian.com/projects/hibernate/secure/Administrators....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira