Issue Details (XML | Word | Printable)

Key: HHH-2667
Type: Bug Bug
Status: Closed Closed
Resolution: Rejected
Priority: Major Major
Assignee: Unassigned
Reporter: Martin Kouba
Votes: 18
Watchers: 21
Operations

If you were logged in you would be able to see more operations.
Hibernate Core

'illegal attempt to dereference collection' when using auto-join

Created: 11/Jun/07 07:51 AM   Updated: 03/Oct/07 02:07 AM
Component/s: None
Affects Version/s: 3.2.3, 3.2.4, 3.2.4.sp1
Fix Version/s: None

Time Tracking:
Not Specified

Environment:
I have found that behaviour in version 3.2.4.sp1 and 3.2.3 (I didn't test 3.2.4 pre sp1)
Database: ORACLE 10g
Issue Links:
Relates


 Description  « Hide
After upgrading to the latest Hibernate version I got this error.

I try to use a statement like this
from cat c where c.mate.id = 13

the expected resulting SQL should be something like
SELECT * FROM CAT C WHERE C.MATE_ID = 13

which is much more performant than making a join
from cat c join c.mate m where m.id = 13

which would result in something like that
SELECT * FROM CAT C INNER JOIN MATE M ON C.MATE_ID = M.ID WHERE M.ID = 13

This works as expected in 3.1.3, 3.2.0 and 3.2.2

It doesn't work with version 3.2.3 and with 3.2.4.sp1.

 All   Comments   Work Log   Change History   FishEye      Sort Order: Ascending order - Click to sort in descending order
Carsten Behring added a comment - 11/Jun/07 11:52 AM
I have the same problem with the Criteria API, the code is something like this:

Conjunction conjuntion= Expression.conjunction();
conjuntion.add(Expression.isNotNull("cat.age"));
     conjuntion.add(Expression.isNull("cat.mate.age"));

I works with 3.1.3, and 3.2.2
I doesn't work with 3.2.3

Jeff Pae-Corr added a comment - 19/Jul/07 09:37 PM
I provide support for a web-app that uses Hibernate 3.x. At this time I am not able to provide the version number of Hibernate as those were removed from the jar names, but I get the same behavior. Per the opening description I changed this:

List assignments = find("from Assignment a where a.assigner.locations.name = ?",schoolName);

to

List assignments = find("from Assignment a join a.assigner.locations s where s.name = ?",schoolName);

to attempt to resolve the issue. The problem is that next I am trying to run deleteAll(assignments) but it fails on a java.lang.ClassCastException: [Ljava.lang.Object;

I believe this is probably due to the fact that properties from objects other than Assignment are also being pulled into the List since I am using join. The original query worked fine before the Hibernate update.


Jeff Pae-Corr added a comment - 19/Jul/07 09:37 PM
By the way, this was on Server '03, MySQL 4.1 DB.

Nick Folder added a comment - 20/Jul/07 02:56 AM
Which files are requiered for a complete test case?

BTW:
Why do you need a test case, it's obvious that auto-join with collections is broken since 3.2.3?

Jeff Pae-Corr added a comment - 20/Jul/07 09:26 AM
I have confirmed the versions stated in the original entry are correct. The ablility to auto join collections works as expected in 3.1.3, 3.2.0 and 3.2.2 and It doesn't work with versions 3.2.3 and with 3.2.4.sp1. I have also tested this with 3.2.1 which is the version I used prior to updating to 3.2.3 and it funcitoned the way it should.



Steve Ebersole added a comment - 20/Jul/07 11:09 AM
I dont know how many times i need to close cases with almost the same exact description.

This was never supported syntax, and was never supposed to work. It was simply a regression that it happened to work (somewhat) in certain releases.

From your example, locations (whatever type of collection it is) has no property named 'name'.

Steve Ebersole added a comment - 20/Jul/07 11:10 AM
Yes, requesting a test case was not the right thing here. because this is expected not to work.

Nick Folder added a comment - 20/Jul/07 11:38 AM
Well, it worked in any Hibernate realease I know. It's ok to change such "dirty" stuff . But it's not ok to change it in a service realease (3.2.2 -> 3.2.3). Never had such problems with Hibernate before. Anyway, thanks for your great work.

Jeff Pae-Corr added a comment - 20/Jul/07 11:54 AM
The "name" property exists for locations, I have also figured out a workaround so I dont have to go through and modify ump-teen queries.

Thank you for work on the Hibernate project.

Since you say that this was never an intended feature, Is this something that could be added through a feature request?


Chris Bredesen added a comment - 20/Jul/07 11:58 AM
Just to alleviate future confusion -- the original example implies that implicit joins across a many-to-one are broken and this is not the case. If Cat.mate were a collection, you'd get this error which is expected behavior and aptly named.

Martin Kouba added a comment - 20/Jul/07 01:19 PM
Would you kindly not close an issue related to a bug and could you kindly reopen this issue.

This is not a misuse or something. I guess I have described the situation clear enough in my original statement. Also according to the current manual at

14.17. Components
eg. "select p.name from from Person p"
eg. "from Person p order by p.name.first"
(I don't know why the manual says from from but this is a different story)

you can find examples that will not work without fixing this bug.

So if you plan to remove this feature and make it necessary to work aroung such cases using native SQL than please state that in the manual that this is no longer supported.

Chris Bredesen added a comment - 20/Jul/07 01:24 PM
Kindly attach a runnable test case which shows this and it will get looked at. Dereferencing a collection is misuse. Implicit join across a single-valued association is not.

Christian Bauer added a comment - 20/Jul/07 01:53 PM
And do so by opening a new issue (WITH a runable testcase). This one is obviously just a bunch of people guessing without any hard evidence such as mapping files etc, and has now been closed.

Christian Bauer added a comment - 20/Jul/07 01:53 PM
And finally: The "collection dereference" change has been noted in the migration guide for months for Hibernate 3.2.

Jeff Pae-Corr added a comment - 20/Jul/07 09:59 PM
Thank you Christian. btw, great job on your Hibernate books with Manning, I have and use them both.

I unfortunately cannot provide any files as they are because they are not mine to publicly post. I will try to genericize the hbm files along with their corresponding pojos for review.

Nick Folder added a comment - 22/Jul/07 04:00 PM
From the Hibernate 3.2 Migration Guide:

"Query language changes
Implicit joins are more deterministic

With such mapping:

<class name='Currency' lazy='false'>
   ....
</class>

<class name='Asset'>
  <id name='id' ... </id>
  <many-to-one name='currency' class='Currency' fetch='select'/>
</class>

and such HQL query:

select a.id, a.currency from Asset a

Since Hibernate 3.2.2 this query generates an inner join on Asset with Currency which means that Assets with a NULL currency are not returned by the query anymore.

This makes implicit joins more deterministic.

To get Assets with a NULL currency just use

select a.id, c from Asset a left join a.currency as c

Path expressions joining across a collection throw exception

The always illegal statement:

select f.bars.baz from Foo f

Now throws an exception. Despite the always present warning in the documentation that this is an illegal path expression if bars is a collection, users found this to be working in some cases with Hibernate 3.1. This is no longer the case, an error is thrown. As always, the correct syntax is an explicit join:

select b.baz from Foo f join f.bars b"

Carsten Behring added a comment - 01/Aug/07 11:57 AM
Thanks Nick, for a solution.

But how do I need to change my code which is using the Criteria API (see my first comment on this ) ?

Christian Bauer added a comment - 01/Aug/07 12:07 PM
Discuss usage questions on the forum.

Nick Folder added a comment - 19/Aug/07 03:01 PM
"But how do I need to change my code which is using the Criteria API (see my first comment on this ) ?"

From the Reference Guide (anyway it sucks ;-) ...

15.4. Associations

You may easily specify constraints upon related entities by navigating associations using createCriteria().
List cats = sess.createCriteria(Cat.class)
    .add( Restrictions.like("name", "F%") )
    .createCriteria("kittens")
        .add( Restrictions.like("name", "F%") )
    .list();

note that the second createCriteria() returns a new instance of Criteria, which refers to the elements of the kittens collection.

The following, alternate form is useful in certain circumstances.
List cats = sess.createCriteria(Cat.class)
    .createAlias("kittens", "kt")
    .createAlias("mate", "mt")
    .add( Restrictions.eqProperty("kt.name", "mt.name") )
    .list();

(createAlias() does not create a new instance of Criteria.)

Note that the kittens collections held by the Cat instances returned by the previous two queries are not pre-filtered by the criteria! If you wish to retrieve just the kittens that match the criteria, you must use a ResultTransformer.
List cats = sess.createCriteria(Cat.class)
    .createCriteria("kittens", "kt")
        .add( Restrictions.eq("name", "F%") )
    .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
    .list();
Iterator iter = cats.iterator();
while ( iter.hasNext() ) {
    Map map = (Map) iter.next();
    Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
    Cat kitten = (Cat) map.get("kt");
}