I'm trying to find out how to lazily load a subclass when using the table per subclass inheritance mapping using a discriminator column. The reason being, in about half of the time, we don't want or need to access the properties of the subclasses, we simply need the properties from the parent class.
But it seems that, even specifying "lazy=true" on the subclass mapping / element, Hibernate still ( assuming "fetch=select" on the join element of the subclass mapping ) eagerly fetches the data from the table mapped to the subclass as soon as I access just the property from the parent class.
I tried specifying either the subclass or the parent class in the call to load(), hoping that somehow specifying the parent class will give me a lazily loaded subclass:
1) When specifying the subclass in load(), Hibernate uses an inner join against the table mapped to the subclass.
2) When specifying the parent class in load(), Hibernate uses 2 separate SELECTs ( one against the table mapped to the parent class, another against the table mapped to the subclass ).
I was hoping / expecting that Hibernate will only do a separate SELECT from the table mapped to the subclass only when I access / set the properties of the subclass.
What am I missing ?
Below are sample a sample mapping file, SQL scripts, and sample code, and output:
Hibernate version: 3.3.1 GA
Mapping documents:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="Payment" table="PAYMENT" lazy="true">
<id name="id" type="integer" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
<subclass name="CreditPayment" discriminator-value="CREDIT" lazy="true">
<join table="CREDIT_PAYMENT" fetch="select">
<key column="PAYMENT_ID"/>
<property name="cardType" column="CARD_TYPE" type="string"/>
</join>
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE" lazy="true">
<join table="CHEQUE_PAYMENT" fetch="select">
<key column="PAYMENT_ID"/>
<property name="chequeNumber" column="CHEQUE_NUMBER" type="string"/>
</join>
</subclass>
</class>
</hibernate-mapping>
SQL scripts:Code:
create table payment(
payment_id integer,
payment_type varchar(10),
amount numeric);
alter table payment add constraint payment_pk primary key ( payment_id ) ;
create table credit_payment(
payment_id integer,
card_type varchar(10)
);
alter table credit_payment add constraint credit_payment_pk primary key ( payment_id );
alter table credit_payment add constraint credit_payment_fk1 foreign key ( payment_id ) references payment ( payment_id );
create table cheque_payment(
payment_id integer,
cheque_number varchar(20)
);
alter table cheque_payment add constraint cheque_payment_pk primary key ( payment_id );
alter table cheque_payment add constraint cheque_payment_fk1 foreign key ( payment_id ) references payment ( payment_id );
insert into payment( payment_id, payment_type, amount ) values ( 1, 'CREDIT', 500.00 );
insert into credit_payment( payment_id, card_type ) values ( 1, 'VISA' );
insert into payment( payment_id, payment_type, amount ) values ( 2, 'CHEQUE', 200.00 );
insert into cheque_payment( payment_id, cheque_number ) values ( 2, '12345' );
commit;
Code between sessionFactory.openSession() and session.close():Code:
Payment creditPayment = (Payment) session.load(CreditPayment.class, 1);
creditPayment.getPaymentType();
Payment chequePayment = (Payment) session.load(Payment.class, 2);
chequePayment.getPaymentType();
Full stack trace of any exception that occurs: n/a
Name and version of the database you are using: Oracle 10G Release 2
The generated SQL (show_sql=true):When specifying the subclass in load(), an inner join is used when a property of the superclass is accessed via the getPaymentType() method:
Code:
select creditpaym0_.PAYMENT_ID as PAYMENT1_0_0_, creditpaym0_.AMOUNT as AMOUNT0_0_, creditpaym0_1_.CARD_TYPE as CARD2_1_0_ from PAYMENT creditpaym0_ inner join CREDIT_PAYMENT creditpaym0_1_ on creditpaym0_.PAYMENT_ID=creditpaym0_1_.PAYMENT_ID where creditpaym0_.PAYMENT_ID=1 and creditpaym0_.PAYMENT_TYPE='CREDIT'
When specifying the superclass in load(), two SELECTs are made when a property of the superclass is accessed via the getPaymentType() method:
Code:
select payment0_.PAYMENT_ID as PAYMENT1_0_0_, payment0_.AMOUNT as AMOUNT0_0_, payment0_.PAYMENT_TYPE as PAYMENT2_0_0_ from PAYMENT payment0_ where payment0_.PAYMENT_ID=2
select payment_2_.CHEQUE_NUMBER as CHEQUE2_2_ from CHEQUE_PAYMENT payment_2_ where payment_2_.PAYMENT_ID=2
Debug level Hibernate log excerpt:Debug level output for the code:
Code:
Payment creditPayment = (Payment) session.load(CreditPayment.class, 1);
creditPayment.getPaymentType();
e.g.: When specifying the subclass in load():
Code:
initializing proxy: [CreditPayment#1]
loading entity: [CreditPayment#1]
about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
select creditpaym0_.PAYMENT_ID as PAYMENT1_0_0_, creditpaym0_.AMOUNT as AMOUNT0_0_, creditpaym0_1_.CARD_TYPE as CARD2_1_0_ from PAYMENT creditpaym0_ inner join CREDIT_PAYMENT creditpaym0_1_ on creditpaym0_.PAYMENT_ID=creditpaym0_1_.PAYMENT_ID where creditpaym0_.PAYMENT_ID=? and creditpaym0_.PAYMENT_TYPE='CREDIT'
select creditpaym0_.PAYMENT_ID as PAYMENT1_0_0_, creditpaym0_.AMOUNT as AMOUNT0_0_, creditpaym0_1_.CARD_TYPE as CARD2_1_0_ from PAYMENT creditpaym0_ inner join CREDIT_PAYMENT creditpaym0_1_ on creditpaym0_.PAYMENT_ID=creditpaym0_1_.PAYMENT_ID where creditpaym0_.PAYMENT_ID=1 and creditpaym0_.PAYMENT_TYPE='CREDIT'
about to open ResultSet (open ResultSets: 0, globally: 0)
result row: EntityKey[CreditPayment#1]
about to close ResultSet (open ResultSets: 1, globally: 1)
about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
resolving associations for [CreditPayment#1]
done materializing entity [CreditPayment#1]
initializing non-lazy collections
done entity load
Debug level output for:
Code:
Payment chequePayment = (Payment) session.load(Payment.class, 2);
chequePayment.getPaymentType();
e.g. When specifying the parent class in load():
Code:
initializing proxy: [Payment#2]
loading entity: [Payment#2]
about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
select payment0_.PAYMENT_ID as PAYMENT1_0_0_, payment0_.AMOUNT as AMOUNT0_0_, payment0_.PAYMENT_TYPE as PAYMENT2_0_0_ from PAYMENT payment0_ where payment0_.PAYMENT_ID=?
select payment0_.PAYMENT_ID as PAYMENT1_0_0_, payment0_.AMOUNT as AMOUNT0_0_, payment0_.PAYMENT_TYPE as PAYMENT2_0_0_ from PAYMENT payment0_ where payment0_.PAYMENT_ID=2
about to open ResultSet (open ResultSets: 0, globally: 0)
result row: EntityKey[Payment#2]
about to open PreparedStatement (open PreparedStatements: 1, globally: 1)
select payment_2_.CHEQUE_NUMBER as CHEQUE2_2_ from CHEQUE_PAYMENT payment_2_ where payment_2_.PAYMENT_ID=?
select payment_2_.CHEQUE_NUMBER as CHEQUE2_2_ from CHEQUE_PAYMENT payment_2_ where payment_2_.PAYMENT_ID=2
about to close PreparedStatement (open PreparedStatements: 2, globally: 2)
about to close ResultSet (open ResultSets: 1, globally: 1)
about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
resolving associations for [ChequePayment#2]
done materializing entity [ChequePayment#2]
initializing non-lazy collections
done entity load