I am attempting to use the table-per-class hierarchy strategy to map different subclasses to a parent object by one-to-one associations.
It is possible to create a parent with these associations but when I attempt to retrieve the parent object I get:
net.sf.hibernate.WrongClassException: Object with id: 1 was not of the specified subclass: com.paulkearney.ch3.CreditCard (loaded object was of wrong class) on the attempted retrieval of the child (subclass) objects.
The where clause generated is missing the discriminator clause on the select of the associated child objects and the result is a cartesian product of the associated children.
When I run the generated SQL I get a cartesian product which is the root cause of the WrongClassException i.e. CreditCards being returned with BankAccounts and vice-versa. This explains what is happening but does give me much help in solving my problem.
Is what I am attempting to do possible? From reading the Hibernate documentation/book I think it should be.
If this is possible does anyone have an example?
Hibernate version: 2.1.7c
Mapping documents:
PARENT OBJECT DEFINITIONS
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class
name="com.paulkearney.ch3.Customer"
table="CUSTOMERS">
<id
name="id"
column="CUSTOMER_ID"
type="integer">
<generator class="increment"/>
</id>
<property
name="name"
column="NAME"
type="string"/>
<one-to-one name="creditCard"
class="com.paulkearney.ch3.CreditCard"
property-ref="customer"
cascade="all"
constrained="true"/>
<one-to-one name="bankAccount"
class="com.paulkearney.ch3.BankAccount"
property-ref="customer"
cascade="all"
constrained="true"/>
</class>
</hibernate-mapping>
CHILD OBJECT DEFINITIONS
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class
name="com.paulkearney.ch3.BillingDetails"
table="BILLING_DETAILS">
<meta attribute="scope-class">public abstract</meta>
<id
name="id"
column="BILLING_DETAILS_ID"
type="integer">
<generator class="increment"/>
</id>
<discriminator
column="BILLING_DETAILS_TYPE"
type="string"
force="true"/>
<property
name="name"
column="OWNER"
type="string"/>
<subclass
name="com.paulkearney.ch3.CreditCard"
discriminator-value="CC">
<meta attribute="scope-class">public</meta>
<property
name="customer"
column="CUSTOMER_ID"
type="integer"
not-null="true"/>
<property
name="type"
type="string"
column="CREDIT_CARD_TYPE"/>
</subclass>
<subclass
name="com.paulkearney.ch3.BankAccount"
discriminator-value="BA">
<meta attribute="scope-class">public</meta>
<property
name="customer"
column="CUSTOMER_ID"
type="integer"
not-null="true"/>
<property
name="bankName"
type="java.lang.String"
column="BANK_ACCOUNT_BANK_NAME"/>
</subclass>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():
Customer customer = (Customer)session.load(Customer.class, id);
Full stack trace of any exception that occurs:
java.lang.RuntimeException: Failed
at com.paulkearney.ch3.CustomersTest.testAddCustomer(CustomersTest.java:70)
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 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 junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
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)
Caused by: net.sf.hibernate.WrongClassException: Object with id: 1 was not of the specified subclass: com.paulkearney.ch3.CreditCard (loaded object was of wrong class)
at net.sf.hibernate.loader.Loader.instanceAlreadyLoaded(Loader.java:531)
at net.sf.hibernate.loader.Loader.getRow(Loader.java:498)
at net.sf.hibernate.loader.Loader.getRowFromResultSet(Loader.java:213)
at net.sf.hibernate.loader.Loader.doQuery(Loader.java:281)
at net.sf.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:133)
at net.sf.hibernate.loader.Loader.loadEntity(Loader.java:911)
at net.sf.hibernate.loader.Loader.loadEntity(Loader.java:931)
at net.sf.hibernate.loader.EntityLoader.load(EntityLoader.java:59)
at net.sf.hibernate.loader.EntityLoader.load(EntityLoader.java:51)
at net.sf.hibernate.persister.EntityPersister.load(EntityPersister.java:415)
at net.sf.hibernate.impl.SessionImpl.doLoad(SessionImpl.java:2130)
at net.sf.hibernate.impl.SessionImpl.doLoadByClass(SessionImpl.java:2000)
at net.sf.hibernate.impl.SessionImpl.load(SessionImpl.java:1929)
at com.paulkearney.ch3.CustomerDAO.getCustomer(CustomerDAO.java:57)
at com.paulkearney.ch3.CustomersTest.testAddCustomer(CustomersTest.java:49)
... 15 more
Name and version of the database you are using:
Oracle 9i;
The generated SQL (show_sql=true):
Hibernate: select customer0_.CUSTOMER_ID as CUSTOMER1_2_, customer0_.NAME as NAME2_, creditcard1_.BILLING_DETAILS_ID as BILLING_1_0_, creditcard1_.CUSTOMER_ID as CUSTOMER4_0_, creditcard1_.CREDIT_CARD_TYPE as CREDIT_C5_0_, creditcard1_.OWNER as OWNER0_, bankaccoun2_.BILLING_DETAILS_ID as BILLING_1_1_, bankaccoun2_.CUSTOMER_ID as CUSTOMER4_1_, bankaccoun2_.BANK_ACCOUNT_BANK_NAME as BANK_ACC6_1_, bankaccoun2_.OWNER as OWNER1_ from CUSTOMERS customer0_ left outer join BILLING_DETAILS creditcard1_ on customer0_.CUSTOMER_ID=creditcard1_.CUSTOMER_ID left outer join BILLING_DETAILS bankaccoun2_ on customer0_.CUSTOMER_ID=bankaccoun2_.CUSTOMER_ID where customer0_.CUSTOMER_ID=?
DDLs
CREATE TABLE BILLING_DETAILS (
BILLING_DETAILS_ID NUMBER(4) NOT NULL,
BILLING_DETAILS_TYPE VARCHAR2(2) NOT NULL,
CUSTOMER_ID NUMBER(4) NOT NULL,
OWNER VARCHAR(20) NOT NULL,
CREDIT_CARD_TYPE VARCHAR2(20),
BANK_ACCOUNT_BANK_NAME VARCHAR2(20),
CONSTRAINT BILLING_DETAILS_PK
PRIMARY KEY(BILLING_DETAILS_ID),
CONSTRAINT CUSTOMERS_FK
FOREIGN KEY(CUSTOMER_ID) REFERENCES CUSTOMERS(CUSTOMER_ID));
CREATE TABLE CUSTOMERS (
CUSTOMER_ID NUMBER(4) NOT NULL,
NAME VARCHAR2(20) NOT NULL,
CONSTRAINT CUSTOMERS_PK
PRIMARY KEY(CUSTOMER_ID);
Debug level Hibernate log excerpt:
|