Hi to all.
I made this test using the base class org.hibernate.test.TestCase which is bundled in every hibernate source distribution.
Can someone give me a comment about it?
The object model is very simple: a superclass A and two subclasses, B and C. B has a list of C's and C has a reference to B.
The hierarchy is mapped using union-subclass, the list of C's in B is mapped as a one-to-many bidirectional association.
The test creates one instance of B with four C's in the list and saves it in one session. In another session, it loads B and tries to get the first C in the list, and surprisingly a ClassCastException is thrown. This is because the list has the same instance of B four times instead of having thouse four instances of C!
When the property
Code:
<property name="description" type="string" column="DESCRIPTION" />
is added in the mapping of C, *before* the many-to-one tag, the test succeeds!
After debugging both the succeeding and the failing cases, I think this has to do with the generation of aliases that are returned by OneToManyLoader in CollectionAliases[] getCollectionAliases().
Hibernate: 3.2.0.ga (2006.10.16)
Database: Oracle 10g
jdk/jre: jdk1.5.0_08
Test caseCode:
package org.myself.test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.test.TestCase;
public class UnionSubclassCollectionTestCase extends TestCase {
public UnionSubclassCollectionTestCase(String name) {
super(name);
}
@Override
protected String[] getMappings() {
return new String[]{"A.hbm.xml", "B.hbm.xml", "C.hbm.xml"};
}
@Override
protected String getBaseForMappings() {
return "org/myself/test/";
}
@Override
protected boolean recreateSchema() {
return true;
}
public void test() {
B b = new B();
b.setName("b");
b.setAge(1l);
C c1 = new C();
c1.setName("c1");
c1.setDescription("c 1");
C c2 = new C();
c2.setName("c2");
c2.setDescription("c 2");
C c3 = new C();
c3.setName("c3");
c3.setDescription("c 3");
C c4 = new C();
c4.setName("c4");
c4.setDescription("c 4");
b.getCs().add(c1);
b.getCs().add(c2);
b.getCs().add(c3);
b.getCs().add(c4);
Session s = this.openSession();
Transaction tx = s.beginTransaction();
s.save(b);
Long bId = b.getOid();
tx.commit();
s.close();
s = this.openSession();
b = (B) s.load(B.class, bId);
assertEquals(4, b.getCs().size());
try {
C newC = b.getCs().get(0);
} catch (ClassCastException e) {
fail("The elements in the list b.getCs() must be of type "
+ C.class);
}
s.close();
}
}
Class A mappingCode:
<?xml version="1.0" encoding="WINDOWS-1251"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.myself.test.A" abstract="true">
<id name="oid" column="ID_CLASSA" type="long"
unsaved-value="0">
<generator class="native" />
</id>
<version name="version" column="VERSION" />
<property name="name" type="string" column="NAME"
not-null="true" />
</class>
</hibernate-mapping>
Class B mappingCode:
<?xml version="1.0" encoding="WINDOWS-1251"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<union-subclass name="org.myself.test.B" extends="org.myself.test.A"
table="TEST_CLASSB">
<list name="cs" inverse="false" lazy="true" cascade="all">
<key column="ID_B" />
<list-index column="IDX" />
<one-to-many class="org.myself.test.C" />
</list>
<property name="age" type="long" column="AGE" />
</union-subclass>
</hibernate-mapping>
Class C mappingCode:
<?xml version="1.0" encoding="WINDOWS-1251"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<union-subclass name="org.myself.test.C" extends="org.myself.test.A"
table="TEST_CLASSC">
<property name="description" type="string" column="DESCRIPTION" />
<many-to-one name="b" column="ID_B" cascade="save-update"
lazy="proxy" />
</union-subclass>
</hibernate-mapping>
Class ACode:
package org.myself.test;
public abstract class A {
private Long oid;
private int version;
private String name;
public A() {
super();
}
public Long getOid() {
return oid;
}
public void setOid(Long oid) {
this.oid = oid;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Class BCode:
package org.myself.test;
import java.util.ArrayList;
import java.util.List;
public class B extends A {
private long age;
private List<C> cs;
public B() {
super();
}
public long getAge() {
return age;
}
public void setAge(long age) {
this.age = age;
}
public List<C> getCs() {
if (cs == null) {
cs = new ArrayList<C>();
}
return cs;
}
public void setCs(List<C> cs) {
this.cs = cs;
}
}
Class CCode:
package org.myself.test;
public class C extends A {
private String description;
private B b;
public C() {
super();
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}