-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: Baffled: Oracle + Outer Join + Distinct not working.
PostPosted: Fri Mar 20, 2009 9:15 am 
Newbie

Joined: Thu Jun 03, 2004 2:10 am
Posts: 19
(Sorry for duplicate - site got stuck on "Submit")

I've been using Hibernate for a while, but i'm having a problem with something I usually take for granted.

(I've read the issues with Hibernate and distinct at http://www.hibernate.org/117.html#A12)

We recently moved from MySQL to Oracle and this problem started. It has to do with the infamous distinct / outer join scenario - which I understand, but this is still weird.

For starters, this works fine in MySQL.

The example invovles to the mapped objects (below) with a simple one to many relationship between them.
When i run a simple query:

Code:
   select
        distinct s
    from
        Song s
    inner join
        s.systemUser u
    left outer join
        s.songLinks l 
    where
        (
            s.dataStatus in (
                1
            )
        )
    order by
        s.artist asc,
        s.title asc


or SQL query:

Code:
   select
            *
        from
            ( select
                distinct song0_.song_id as song1_2_,
                song0_.system_user_id as system2_2_,
                song0_.title as title2_,
                song0_.artist as artist2_,
                song0_.album as album2_,
                song0_.genre as genre2_,
                song0_.year as year2_,
                song0_.sample_wav_path as sample8_2_,
                song0_.title_wav_path as title9_2_,
                song0_.artist_wav_path as artist10_2_,
                song0_.album_wav_path as album11_2_,
                song0_.original_file_name as original12_2_,
                song0_.date_created as date13_2_,
                song0_.data_status as data14_2_
            from
                song song0_
            inner join
                system_user systemuser1_
                    on song0_.system_user_id=systemuser1_.system_user_id
            left outer join
                song_link songlinks2_
                    on song0_.song_id=songlinks2_.song_id
            where
                song0_.data_status in (
                    1
                )
            order by
                song0_.artist asc,
                song0_.title asc )
        where
            rownum <= 25


I get duplicate Songs if the song has more than one SongLink in the code.
If I set up Hibernate logging and take the SQL query and run it on same Oracle - there are no duplicates!
If I take the HQL query and run it using the Hibernate Tools console using the same JDBC driver - there are no duplicates.

I need the outer join because there are more "where" items here, but I can reproduce the problem with this statement as well.

Only when run through the actual code there are duplicates. I'm baffled.
Any ideas? Is it because i'm using limits / paging?

Hibernate version:
3.3.1 GA

Mapping documents:

Code:
<hibernate-mapping package="myapp">

   <class name="Song" table="song">

        <meta attribute="implement-equals">true</meta>
        <meta attribute="generated-class">myapp.BaseSong</meta>

        <id name="songId"
            type="java.lang.Long"
            column="song_id">
            <meta attribute="use-in-equals">true</meta>
            <generator class="native">
                <param name="sequence">song_seq</param>
            </generator>
        </id>

        <many-to-one name="systemUser"
            class="SystemUser"
            column="system_user_id"
            cascade="lock,refresh"
            not-null="true"
            index="idx_song_system_user"
            foreign-key="fk_song_system_user">
        </many-to-one>

        <property name="title"
            column="title"
            type="string"
            length="200"
            not-null="false">
        </property>

        <property name="artist"
            column="artist"
            type="string"
            length="200"
            not-null="false">
        </property>

        <property name="album"
            column="album"
            type="string"
            length="200"
            not-null="false">
        </property>

        <property name="genre"
            column="genre"
            type="string"
            length="100"
            not-null="false">
        </property>

        <property name="year"
            column="year"
            type="integer"
            not-null="false">
        </property>

        <property name="sampleWavPath"
            column="sample_wav_path"
            type="string"
            length="500"
            not-null="false">
        </property>

        <property name="titleWavPath"
            column="title_wav_path"
            type="string"
            length="500"
            not-null="false">
        </property>

        <property name="artistWavPath"
            column="artist_wav_path"
            type="string"
            length="500"
            not-null="false">
        </property>

        <property name="albumWavPath"
            column="album_wav_path"
            type="string"
            length="500"
            not-null="false">
        </property>

        <property name="originalFileName"
            column="original_file_name"
            type="string"
            length="500"
            not-null="false"
            index="idx_song_original_file_name">
        </property>

        <property name="dateCreated"
            column="date_created"
            type="timestamp"
            not-null="true">
        </property>

        <property name="dataStatus"
            column="data_status"
            type="java.lang.Integer"
            not-null="true"
            index="idx_song_data_status">
        </property>

        <set name="songLinks" fetch="select" lazy="true" table="song_link" cascade="all-delete-orphan">
            <key column="song_id" />
            <one-to-many class="SongLink" />   
        </set>

    </class>
   
   <class name="SongLink" table="song_link">

        <meta attribute="implement-equals">true</meta>
        <meta attribute="generated-class">myapp.BaseSongLink</meta>

        <id name="songLinkId"
            type="java.lang.Long"
            column="song_link_id">
            <meta attribute="use-in-equals">true</meta>
            <generator class="native">
                <param name="sequence">song_link_seq</param>
            </generator>
        </id>

        <many-to-one name="song"
            class="Song"
            column="song_id"
            cascade="lock,refresh"
            not-null="true"
            index="idx_song_link_song"
            foreign-key="fk_song_link_song">
        </many-to-one>

        <many-to-one name="systemUser"
            class="SystemUser"
            column="system_user_id"
            cascade="lock,refresh"
            not-null="true"
            index="idx_song_link_system_user"
            foreign-key="fk_song_link_system_user">
        </many-to-one>

        <property name="systemPath"
            column="system_path"
            type="string"
            length="500"
            not-null="false">
        </property>

        <property name="format"
            column="format"
            type="java.lang.Integer"
            not-null="true"
            index="idx_song_link_format">
        </property>

        <property name="contentType"
            column="content_type"
            type="string"
            length="100"
            not-null="true"
            index="idx_song_link_content_type">
        </property>

        <property name="price"
            column="price"
            type="java.lang.Integer"
            not-null="false">
        </property>

        <property name="dateCreated"
            column="date_created"
            type="timestamp"
            not-null="true">
        </property>

        <property name="dataStatus"
            column="data_status"
            type="java.lang.Integer"
            not-null="true"
            index="idx_song_link_data_status">
        </property>
    </class>

</hibernate-mapping>



Name and version of the database you are using: Oracle 11g


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 20, 2009 12:59 pm 
Newbie

Joined: Thu Jun 03, 2004 2:10 am
Posts: 19
After testing, order by doesn't make any difference nor does limits/paging. It still shows duplicates.

I assumed duplicates where only for eager fetching (left join fetch)....

Any ideas would be great.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 20, 2009 1:26 pm 
Newbie

Joined: Thu Jun 03, 2004 2:10 am
Posts: 19
Another update...

I monitored the Oracle query history and the exact query Oracle gets does not contain duplicates when run!

What could explain them appearing in Hibernate?


Top
 Profile  
 
 Post subject:
PostPosted: Sat Mar 21, 2009 6:48 am 
Newbie

Joined: Thu Jun 03, 2004 2:10 am
Posts: 19
Another update (to myself i guess) is that if i use .iterate() instead of .list(), and save the entities in a ArrayList, there are no duplicates.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 23, 2009 5:03 am 
Newbie

Joined: Mon Oct 16, 2006 10:23 am
Posts: 8
A wild hunch, but does the object have proper hashCode() and equals() implementations?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 23, 2009 5:39 am 
Newbie

Joined: Thu Jun 03, 2004 2:10 am
Posts: 19
Yes, but thanks for the suggestion.

I actually found the problem. There is an injector setting oracle case-insensitivity before queries:

alter session set nls_comp=LINGUISTIC;
alter session set nls_sort=BINARY_CI;

What is crazy is that the combination of the case insensitivity + "outer joins" + selecting utf8 text data + order by in tables causes the duplication issue.

It took me quite a few hours to find that sneaky one (I assume this is a bug in Oracle....)

The minute i removed the case-insensitivity queries it works fine. Go figure.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.