Hi,
Hibernate 3.5.4-Final, but same problem manifests with 3.6.10.Final
I have run into a problem with an inverse one-to-many bi-directional relationship which references a subclass from a parent (abstract) class. The following simplification demonstrates my problem:
I have the following inheritance hierarchy:
Code:
AbstractContainer
/ \
Container Item
An AbstractContainer (i.e. an Item or a Container) contains a collection of Items (not illustrated). This collection is mapped with inverse=true and is bi-directional.
I have mapped these classes as follows:
Hibernate mappingsCode:
<hibernate-mapping>
<class abstract="true" name="test.hibernate.subclassjoinedtoparent.AbstractContainer" table="TBL_ABSTRACT_CONTAINER"
discriminator-value="ABSTRACT_CONTAINER">
<id name="id" column="ID" unsaved-value="null">
<generator class="native" />
</id>
<discriminator column="ACTUAL_TYPE" type="string" />
<list name="items" cascade="all" inverse="false">
<key column="CONTAINER_ID" />
<list-index column="INDEX" />
<one-to-many class="test.hibernate.subclassjoinedtoparent.Item" />
</list>
<!-- Container subclass -->
<subclass name="test.hibernate.subclassjoinedtoparent.Container" discriminator-value="CONTAINER" />
<!-- Item subclass -->
<subclass name="test.hibernate.subclassjoinedtoparent.Item" discriminator-value="ITEM">
<join table="TBL_ITEM">
<key column="ID" />
<many-to-one name="container" column="CONTAINER_ID" class="test.hibernate.subclassjoinedtoparent.AbstractContainer"
not-null="true" foreign-key="FK_CONTAINERID" />
</join>
</subclass>
</class>
</hibernate-mapping>
Using this mapping, I have a very simple test case which creates a Container and adds an item in one transaction:
Source codeCode:
SessionFactory sessionFactory =
new Configuration().configure("hibernate-subclassjoinedtoparent.cfg.xml").buildSessionFactory();
AbstractContainer container;
Item item;
Session session = sessionFactory.openSession();
try {
session.beginTransaction();
container = new Container();
item = new Item();
container.addItem(item);
session.save(container);
session.flush();
System.out.println("TX #1: Saved container with id: " + container.getId());
System.out.println("TX #1: Container has " + container.getItems().size() + " items.");
session.getTransaction().commit();
}
finally {
session.close();
}
Then, in a second transaction (session) immediately after the first code (same test method) I look up the container and get the size of the items:
Code:
session = sessionFactory.openSession();
try {
session.beginTransaction();
AbstractContainer savedContainer = (AbstractContainer)session.get(AbstractContainer.class, container.getId());
System.out.println("TX #2: Found the container with id: " + savedContainer.getId());
System.out.println("TX #2: Container has " + savedContainer.getItems().size() + " items.");
session.getTransaction().commit();
}
finally {
session.close();
}
The output is as follows:
Code:
TX #1: Saved container with id: 1
TX #1: Container has 1 items.
Hibernate: select abstractco0_.ID as ID0_0_, abstractco0_1_.CONTAINER_ID as CONTAINER2_1_0_, abstractco0_.ACTUAL_TYPE as ACTUAL2_0_0_ from TBL_ABSTRACT_CONTAINER abstractco0_ left outer join TBL_ITEM abstractco0_1_ on abstractco0_.ID=abstractco0_1_.ID where abstractco0_.ID=?
TX #2: Found the container with id: 1
Hibernate: select items0_.CONTAINER_ID as CONTAINER3_0_1_, items0_.ID as ID1_, items0_.INDEX as INDEX1_, items0_.ID as ID0_0_, items0_1_.CONTAINER_ID as CONTAINER2_1_0_ from TBL_ABSTRACT_CONTAINER items0_ inner join TBL_ITEM items0_1_ on items0_.ID=items0_1_.ID where items0_.CONTAINER_ID=?
TX #2: Container has 0 items.
A couple of things that I have noticed1) The effect of inverse=true
Note the last line:
TX #2: Container has 0 items.
If I turn the relationship to inverse="false", I get:
TX #2: Container has 1 items.
2) The DDL (on postgres):
Notice the container_id column in the TBL_ABSTRACT_CONTAINER table. Interestingly, it appears never to be populated.
Code:
Table "public.tbl_abstract_container"
Column | Type | Modifiers
--------------+------------------------+-----------
id | bigint | not null
actual_type | character varying(255) | not null
container_id | bigint |
index | integer |
Indexes:
"tbl_abstract_container_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"fkc86866c569382e12" FOREIGN KEY (container_id) REFERENCES tbl_abstract_container(id)
Table "public.tbl_item"
Column | Type | Modifiers
--------------+--------+-----------
id | bigint | not null
container_id | bigint | not null
Indexes:
"tbl_item_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"fk404b7b1461f77f4" FOREIGN KEY (id) REFERENCES tbl_abstract_container(id)
"fk_containerid" FOREIGN KEY (container_id) REFERENCES tbl_abstract_container(id)
3. The insert statement:
Notice that the insert of the CONTAINER_ID happens in the TBL_ITEM table only
Code:
Hibernate: insert into TBL_ABSTRACT_CONTAINER (ACTUAL_TYPE, ID) values ('CONTAINER', ?)
Hibernate: insert into TBL_ABSTRACT_CONTAINER (ACTUAL_TYPE, ID) values ('ITEM', ?)
Hibernate: insert into TBL_ITEM (CONTAINER_ID, ID) values (?, ?)
4. The query used to select the items.
Notice that the query is selecting on the foreign key in the TBL_ABSTRACT_CONTAINER table - which is the column that is NOT populated during the insert
Code:
select ...
from TBL_ABSTRACT_CONTAINER items0_
inner join TBL_ITEM items0_1_ on items0_.ID=items0_1_.ID
where items0_.CONTAINER_ID=?
Any ideas would be much appreciated