Mapping composite foreign key to composite primary key fails

Description

I am seeing a problem similiar to the problems described at:
https://forum.hibernate.org/viewtopic.php?f=25&t=996445

I have mapped a composite foreign key to a primary composite key and it does not cache the results properly. It's a bi-directional, many-to-one association mapped essentially a cut+paste (with variable name substitution) from:
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#d0e2177
but with a @NotFound set to IGNORE.

When I include the relationship in an HQL left fetch join, I can see that the Child's Parent entity is placed in the session-cache. But when the Child class's getParent() method is invoked, the result is loaded from the database instead of the session. It completely ignores the session and the second-level cache, so I'm left with the N + 1 problem. If I enable batch fetch (size = 8), the entities loaded from the batch are also not placed into either session cache (or level 2), so I get 1 + N (8-batch) fetches.

If the query runs again, everything correctly loads from the second-level cache.

Thanks.

Environment

Java Hibernate 3.5.6_FINAL, Oracle 11g

Activity

Show:
B
December 27, 2010, 7:20 PM
Edited

Ok, so having played with this a bit more, this is what I have determined...

I created two test tables:

Table Person

Name

Type

Notes

id

number(30)

primary key

full_name

varchar(16)

state

varchar (2)

FK Billing(state, country)

country

varchar (3)

Table Billing

Name

Type

Notes

state

varchar (2)

primary composite key (state, country)

country

varchar (3)

price

number(16)

Then I ran Hibernate Tool 3.3.0, and it generated:

Person.java

Billing.java

BillingId.java

All classes are read-only and they all implement hashcode and equals based on their primary keys. There is not billing information for each state/country combination, so we don't want to throw an exception if that occurs, just return null.

The problem I am encountering is that when I do an HQL left fetch join on person.billing, the correct SQL is deployed and the Billing objects are loaded, but they are not put into the session/level2 cache. So immediately after launching the query, it iterates through all the people objects and does a single query to load each person's billing information (N + 1). I could potentially live with that, but when I set batchSize = 4 on the Billing object, it will correctly do the left join in SQL, then iterate through the results, and load 4 Billing objects at a time, but I see the same number of queries being sent. So in the batchSize = 1, I see (N + X) queries, and with batchSize = 4, I see (N + X) instead of the expected (N + (X/4)) queries, but for each X, it generates SQL that loads 4 at a time. Very peculiar. So I cannot seem to get around (N + 1) and my N can be in the thousands. Interestingly enough, if I execute the same HQL statement twice, it will correctly pull the Billing objects from the 2nd LvL Cache the second time, without the need to do (N + 1) again.

It's also odd, because the N SQL statements to load the N Billing objects occur within Hibernate somewhere, and are not invoked by my calling person.getBilling() (even though it's marked as lazy).

Right-joins work correctly (for those times I want to only see persons with billing information). One SQL statement will be executed and it will load the Billing objects at that point.

Thanks.

B
December 27, 2010, 10:53 PM

So, from stepping through the code, I can see that Hibernate finds the association and sees that it's associated by a BillingId object. It recognizes it as a primary key and loads the key values (State,Country), creates a BillingId Object, and puts it into the context cache. The problem seems to be that it never instantiates the Billing object so it is never put into the context cache. When all the loading from the original query happens, it iterates through each and every object in the context, looking for unitialized values. Every time it finds a Person address, it (correctly) sees that the Billing object has not been loaded yet, so it does the look-up with the BillingId object. So what looks like needs to happen is that, in the ManyToOne Entity loader, the Billing objects needs to be created, when a BillingId object is created, and the Billing objects needs to get put into the context cache.

Brett Meyer
April 7, 2014, 5:48 PM

In an effort to clean up, in bulk, tickets that are most likely out of date, we're transitioning all ORM 3 tickets to an "Awaiting Test Case" state. Please see http://in.relation.to/Bloggers/HibernateORMJIRAPoliciesAndCleanUpTactics for more information.

If this is still a legitimate bug in ORM 4, please provide either a test case that reproduces it or enough detail (entities, mappings, snippets, etc.) to show that it still fails on 4. If nothing is received within 3 months or so, we'll be automatically closing them.

Thank you!

Brett Meyer
July 8, 2014, 3:11 PM

Bulk rejecting stale issues. If this is still a legitimate issue on ORM 4, feel free to comment and attach a test case. I'll address responses case-by-case. Thanks!

Assignee

Unassigned

Reporter

B

Fix versions

None

Labels

None

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Components

Affects versions

Priority

Major
Configure