[b]Description[/b]
When (a) a persisted class has two associated properties of the same type, (b) that type has a proxy, (c) that type has a complex property, only the first association (alphabetically) is eagerly fetchable. If both have an outer-join="true" setting in the mapping, the second is ignored, leading to lazy initialization exceptions.
This is due to how the sql string for the persisted class is constructed (in AbstractEntityLoader.initStatementString). The list of associations that is a parameter there, is constructed by walking the association tree. When walking the association tree, a set of visisted persisters is maintained. The persister for the properties is added to this set when evaluating the complex property of this property. When the visitor wants to visit the second property, the persister is already in the set and the property is not visisted and thus not added to the list of associations and eventually not added to the select statement.
This also leads to unnecessary SELECT statements when loading an object in the described situation where the property is not proxied.
Is this a bug?
[b]Hibernate version:[/b] 2.1.8
[b]Mapping documents:[/b]
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping>
<class name="org.hibernate.test.fetchProxy.Main">
<id name="id" unsaved-value="null">
<generator class="increment"/>
</id>
<many-to-one name="one" outer-join="true" cascade="all"/>
<many-to-one name="two" outer-join="true" cascade="all"/>
</class>
<class name="org.hibernate.test.fetchProxy.Proxied" lazy="true">
<id name="id" unsaved-value="null">
<generator class="increment"/>
</id>
<property name="name"/>
<many-to-one name="spec" cascade="all"/>
</class>
<class name="org.hibernate.test.fetchProxy.Spec">
<id name="id" unsaved-value="null">
<generator class="increment"/>
</id>
</class>
</hibernate-mapping>
[b]Code between sessionFactory.openSession() and session.close():[/b]
Session s;
Main main = new Main();
main.setOne(new Proxied("first"));
main.setTwo(new Proxied("second"));
s = openSession();
Serializable id = s.save(main);
s.flush();
s.close();
s = openSession();
Main persisted = (Main) s.get(Main.class,id);
s.close();
assertEquals("first",persisted.getOne().getName());
assertEquals("second",persisted.getTwo().getName());
public class Main {
private Long id;
private Proxied one;
private Proxied two;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Proxied getOne() {
return one;
}
public void setOne(Proxied one) {
this.one = one;
}
public Proxied getTwo() {
return two;
}
public void setTwo(Proxied two) {
this.two = two;
}
}
public class Proxied {
private Long id;
private String name;
private Spec spec;
protected Proxied() {}
public Proxied(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Spec getSpec() {
return spec;
}
public void setSpec(Spec spec) {
this.spec = spec;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Spec {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
[b]Full stack trace of any exception that occurs:[/b]
net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed
at net.sf.hibernate.proxy.LazyInitializer.initialize(LazyInitializer.java:47)
at net.sf.hibernate.proxy.LazyInitializer.initializeWrapExceptions(LazyInitializer.java:60)
at net.sf.hibernate.proxy.LazyInitializer.getImplementation(LazyInitializer.java:164)
at net.sf.hibernate.proxy.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:108)
at org.hibernate.test.fetchProxy.Proxied$$EnhancerByCGLIB$$86691509.getName(<generated>)
at org.hibernate.test.fetchProxy.EagerTest.testBothSet(EagerTest.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at junit.framework.TestCase.runTest(TestCase.java:154)
at org.hibernate.test.TestCase.runTest(TestCase.java:107)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:421)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:305)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:186)
[b]Name and version of the database you are using:[/b] hypersonic 1.7.3.3
|