-->
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: Col alias not unique using multiple LEFT JOIN FETCHs
PostPosted: Thu Jul 12, 2007 10:30 pm 
Newbie

Joined: Tue Mar 27, 2007 10:45 am
Posts: 5
Hibernate version: 3.2.4.sp1

We have an HQL statement in which we are trying to LEFT JOIN FETCH several (small) items. If we LEFT JOIN FETCH entity sets, everything is fine. However if we add in a set of (integer) values:
Code:
   <set name="zones" table="ApprovalZone">
      <key>
         <column name="ApprovalID" />
      </key>
      <element column="ZoneID" type="int" />
   </set>


the generated SQL becomes invalid:
Code:
select
...
zones2_.ApprovalID as ApprovalID1__,
zones2_.ZoneID as ZoneID1__,
...
zones2_.ApprovalID as ApprovalID1__,
zones2_.ZoneID as ZoneID1__,
...

from
  blah.Approval approval0_
  left outer join blah.ApprovalZone zones2_ on approval0_.ApprovalID=zones2_.ApprovalID


and the dB engine (SQLAnywhere 9.0.2.3342) throws an exception:
Quote:
SEVERE: ASA Error -830: Alias 'ApprovalID1__' is not unique
com.insei.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not execute query



Is this a bug or a problem w/ our mapping/query?


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 14, 2007 5:30 pm 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
I don't think you've given enough information. A simple test with a LEFT JOIN FETCH to a set of integers works fine (3.2.4sp1 hsqldb & mysql). SQL you posted looks wrong so probably not a database specific issue.

Mike

ZoneContainer.java
Code:
package test.mdlandon;

import java.util.Set;

public class ZonesContainer {
   private Long id;
   private Set zones;
   public Long getId() {
      return id;
   }
   public void setId(Long id) {
      this.id = id;
   }
   public Set getZones() {
      return zones;
   }
   public void setZones(Set zones) {
      this.zones = zones;
   }
   
}


ZoneContainer.hbm.xml
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping package="test.mdlandon">
   <class name="ZonesContainer" table="mdlandon_zones_container">

      <id name="id" column="id">
         <generator class="increment" />
      </id>

      <set name="zones" table="mdlandon_approval_zone">
         <key column="approval_id" />
         <element column="zone_id" type="int"/>
      </set>

   </class>
</hibernate-mapping>


TestIt.java
Code:
package test.mdlandon;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import junit.framework.TestCase;

import org.hibernate.Query;
import org.hibernate.Session;

public class TestIt extends TestCase {
   
   public void testIt() {
      Long id = createZonesContainerInDatabase();
      
      Session s = HibernateUtil.getSession();
      Query q = s.createQuery("from ZonesContainer as zc left join fetch zc.zones");
      List result = q.list();
      System.out.println("result size="+result.size());
      ZonesContainer zc = (ZonesContainer)result.get(0);
      System.out.println(""+zc.getZones());
      s.close();
   }

   private Long createZonesContainerInDatabase() {
      Session s = HibernateUtil.getSession();
      s.beginTransaction();
      
      Set<Integer> zones = new HashSet<Integer>();
      zones.add(new Integer(1));
      zones.add(new Integer(2));
      
      ZonesContainer zc = new ZonesContainer();
      zc.setZones(zones);
      Long id = (Long)s.save(zc);
      
      s.getTransaction().commit();
      s.close();
      return id;
   }

}


Console
Code:
Hibernate: select max(id) from mdlandon_zones_container
Hibernate: insert into mdlandon_zones_container (id) values (?)
Hibernate: insert into mdlandon_approval_zone (approval_id, zone_id) values (?, ?)
Hibernate: insert into mdlandon_approval_zone (approval_id, zone_id) values (?, ?)
Hibernate: select zonesconta0_.id as id0_, zones1_.approval_id as approval1_0__, zones1_.zone_id as zone2_0__ from mdlandon_zones_container zonesconta0_ left outer join mdlandon_approval_zone zones1_ on zonesconta0_.id=zones1_.approval_id
result size=2
[1, 2]


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 16, 2007 11:38 am 
Newbie

Joined: Tue Mar 27, 2007 10:45 am
Posts: 5
Sorry, I was in a hurry the other night and was hoping to get away w/ being lazy. :-)

Your testcase is almost complete...it's just missing an additional one-to-many entity set from the base table (included in the LEFT JOIN FETCH).

Below is a full description that reproduces the failure.

SQL
Code:
create table mdlandon_zones_container
(
    ZoneID               integer                        not null default autoincrement,
    ZoneName             varchar(64)                    not null,
    primary key (ZoneID)
);

create table mdlandon_approval_zone
(
    ZoneID               integer                        not null,
    ApprovalID           integer                        not null,
    primary key (ZoneID, ApprovalID)
);

alter table mdlandon_approval_zone
   add foreign key FK_1 (ZoneID)
      references mdlandon_zones_container (ZoneID)
      on update restrict
      on delete restrict;

create table mdlandon_other_container
(
    OtherID              integer                        not null default autoincrement,
    ZoneID               integer                        not null,
    OtherName            varchar(64)                    not null,
    primary key (OtherID)
);

alter table mdlandon_other_container
   add foreign key FK_2 (ZoneID)
      references mdlandon_zones_container (ZoneID)
      on update restrict
      on delete restrict;

insert into mdlandon_zones_container(ZoneID, ZoneName) values (1, 'testing');
insert into mdlandon_zones_container(ZoneID, ZoneName) values (2, 'testing2');

insert into mdlandon_approval_zone(ZoneID, ApprovalID) values (1, 33);
insert into mdlandon_approval_zone(ZoneID, ApprovalID) values (1, 34);
insert into mdlandon_approval_zone(ZoneID, ApprovalID) values (1, 35);
insert into mdlandon_approval_zone(ZoneID, ApprovalID) values (2, 133);
insert into mdlandon_approval_zone(ZoneID, ApprovalID) values (2, 234);
insert into mdlandon_approval_zone(ZoneID, ApprovalID) values (2, 335);

insert into mdlandon_other_container(OtherID, ZoneID, OtherName) values (1, 1, 'other1-1');
insert into mdlandon_other_container(OtherID, ZoneID, OtherName) values (2, 1, 'other1-2');
insert into mdlandon_other_container(OtherID, ZoneID, OtherName) values (3, 1, 'other1-3');
insert into mdlandon_other_container(OtherID, ZoneID, OtherName) values (4, 2, 'other2-1');
insert into mdlandon_other_container(OtherID, ZoneID, OtherName) values (5, 2, 'other2-2');
insert into mdlandon_other_container(OtherID, ZoneID, OtherName) values (6, 2, 'other2-3');



ZonesContainer.java
Code:
package test.mdlandon;

import java.util.Set;

public class ZonesContainer {
   private Long zoneId;
   private String name;
   private Set zones;
   private Set others;
   public Long getZoneId() {
      return zoneId;
   }
   public void setZoneId(Long zoneId) {
      this.zoneId = zoneId;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public Set getZones() {
      return zones;
   }
   public void setZones(Set zones) {
      this.zones = zones;
   }
   public Set getOthers() {
      return others;
   }
   public void setOthers(Set others) {
      this.others = others;
   }
   
}



ZonesContainer.hbm.xml

Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping package="test.mdlandon">
   <class name="ZonesContainer" table="mdlandon_zones_container">
 
      <id name="zoneId" column="ZoneID">
         <generator class="native" />
      </id>

      <property name="name" type="string">
         <column name="ZoneName" length="64" not-null="true" />
      </property>

      <set name="zones" table="mdlandon_approval_zone">
         <key column="ApprovalID" />
         <element column="ZoneID" type="int"/>
      </set>

      <set name="others" inverse="true" cascade="save-update" fetch="subselect">
         <key>
            <column name="OtherID" not-null="true" />
         </key>
         <one-to-many class="test.mdlandon.OtherContainer" />
      </set>
   </class>
</hibernate-mapping>



OtherContainer.java
Code:
package test.mdlandon;

public class OtherContainer {
   private Long otherId;
   private String name;
   private ZonesContainer zone;
   public Long getOtherId() {
      return otherId;
   }
   public void setOtherId(Long otherId) {
      this.otherId = otherId;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public ZonesContainer getZone() {
      return zone;
   }
   public void setZone(ZonesContainer zone) {
      this.zone = zone;
   }
   
}



OtherContainer.hbm.xml
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping package="test.mdlandon">
   <class name="OtherContainer" table="mdlandon_other_container">
 
      <id name="otherId" column="OtherID">
         <generator class="native" />
      </id>

      <many-to-one name="zone" class="test.mdlandon.ZonesContainer">
         <column name="ZoneID" />
      </many-to-one>

      <property name="name" type="string">
         <column name="OtherName" length="64" not-null="true" />
      </property>

   </class>
</hibernate-mapping>



TestIt.java
Code:
package test.mdlandon;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.Query;
import org.hibernate.Session;

public class TestIt {
   
   public void testIt() throws Exception {
      Session s = HibernateUtil.getSession();
      Query q = s.createQuery("from ZonesContainer as zc left join fetch zc.others left join fetch zc.zones");
      List result = q.list();
      System.out.println("result size="+result.size());
      ZonesContainer zc = (ZonesContainer)result.get(0);
      System.out.println(""+zc.getZones());
      System.out.println(""+zc.getOthers());
      s.close();
   }

   public static void main (String args[]) throws Exception {
       TestIt it = new TestIt();
       it.testIt();
   }

}



Console SQL
Code:
Hibernate: select zonesconta0_.ZoneID as ZoneID0_0_, others1_.OtherID as OtherID2_1_, zonesconta0_.ZoneName as ZoneName0_0_, zones2_.ApprovalID as ApprovalID1__, zones2_.ZoneID as ZoneID1__, others1_.ZoneID as ZoneID2_1_, others1_.OtherName as OtherName2_1_, others1_.OtherID as OtherID0__, zones2_.ApprovalID as ApprovalID1__, zones2_.ZoneID as ZoneID1__ from mdlandon_zones_container zonesconta0_ left outer join mdlandon_other_container others1_ on zonesconta0_.ZoneID=others1_.OtherID left outer join mdlandon_approval_zone zones2_ on zonesconta0_.ZoneID=zones2_.ApprovalID



Console Error
Code:
SEVERE: ASA Error -830: Alias 'ApprovalID1__' is not unique
org.hibernate.exception.SQLGrammarException: could not execute query
   at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
   at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
   at org.hibernate.loader.Loader.doList(Loader.java:2223)
   at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2104)
   at org.hibernate.loader.Loader.list(Loader.java:2099)
   at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:378)
   at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:338)
   at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:172)
   at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1121)
   at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
   at test.mdlandon.TestIt.testIt(Unknown Source)
   at test.mdlandon.TestIt.main(Unknown Source)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 16, 2007 4:43 pm 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Well, I haven't gotten to the bottom of this yet but I do have some useful information for you.

You can get this query to execute correctly by switching the order of the joins:

Instead of:
from ZonesContainer as zc left join fetch zc.others left join fetch zc.zones

which produces sql:
Code:
Hibernate: select zonesconta0_.ZoneID as ZoneID0_0_, others1_.OtherID as OtherID2_1_, zonesconta0_.ZoneName as ZoneName0_0_, zones2_.ZoneID as ZoneID1__, zones2_.ApprovalID as ApprovalID1__, others1_.ZoneID as ZoneID2_1_, others1_.OtherName as OtherName2_1_, others1_.ZoneID as ZoneID0__, others1_.OtherID as OtherID0__, zones2_.ZoneID as ZoneID1__, zones2_.ApprovalID as ApprovalID1__ from mdlandon_zones_container zonesconta0_ left outer join mdlandon_other_container others1_ on zonesconta0_.ZoneID=others1_.ZoneID left outer join mdlandon_approval_zone zones2_ on zonesconta0_.ZoneID=zones2_.ZoneID


If you switch to:
from ZonesContainer as zc left join fetch zc.zones left join fetch zc.others

It produces sql:
Code:
Hibernate: select zonesconta0_.ZoneID as ZoneID0_0_, others2_.OtherID as OtherID2_1_, zonesconta0_.ZoneName as ZoneName0_0_, zones1_.ZoneID as ZoneID0__, zones1_.ApprovalID as ApprovalID0__, others2_.ZoneID as ZoneID2_1_, others2_.OtherName as OtherName2_1_, others2_.ZoneID as ZoneID1__, others2_.OtherID as OtherID1__ from mdlandon_zones_container zonesconta0_ left outer join mdlandon_approval_zone zones1_ on zonesconta0_.ZoneID=zones1_.ZoneID left outer join mdlandon_other_container others2_ on zonesconta0_.ZoneID=others2_.ZoneID


To me, the fact that this can be fixed by changing the order in which the joins are specified points to a bug.

Also there are a couple of errors in your mapping files. I was hopeful that these were the cause of the strange SQL but apparently not. I corrected them but ended up with the same result for the query...

Your sets are using the wrong key column. The key column should refer to the place you store the primary key of the _parent_, not the primary key column of the child entity.

Code:
<set name="others" inverse="true" cascade="save-update" fetch="subselect">
  <key>
    <column name="OtherID" not-null="true" />
  </key>
  <one-to-many class="test.mdlandon.OtherContainer" />
</set>


The key colum should refer to the parent ID:
Code:
<key>
  <column name="ZoneID" not-null="true" />
</key>


Same applies to the ApprovalZone:
Code:
<set name="zones" table="mdlandon_approval_zone">
  <key column="ApprovalID" />
  <element column="ZoneID" type="int"/>
</set>


Key should refer to parent, element to the ApprovalID.
Code:
<key column="ZoneID" />
  <element column="ApprovalID" type="int"/>
</set>


I discovered this when I tried to programatically insert data into this schema - it was causing foreign key constraint violations.

The hibernate documentation has examples of this type of bidirectional relationship:

From http://www.hibernate.org/hib_docs/v3/reference/en/html_single/
Code:
...
The underlying cause of this behaviour is that the link (the foreign key parent_id) from p to c is not considered part of the state of the Child object and is therefore not created in the INSERT. So the solution is to make the link part of the Child mapping.

<many-to-one name="parent" column="parent_id" not-null="true"/>
(We also need to add the parent property to the Child class.)

Now that the Child entity is managing the state of the link, we tell the collection not to update the link. We use the inverse attribute.

<set name="children" inverse="true">
    <key column="parent_id"/>
    <one-to-many class="Child"/>
</set>


I'm off to Spain for a week but will try to pick this up on my return.

Mike


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 17, 2007 4:05 pm 
Newbie

Joined: Tue Mar 27, 2007 10:45 am
Posts: 5
Yes, got the mapping files mixed up in my hurry to create a working testcase...thanks for correcting.

We also found that switching the order of the joins fixes the problem...however our production situation is more complicated & switching order is not possible (i.e., our LEFT FETCH JOINs are actually off of a sub-entity, thus we cannot place them first).

Any help you can provide would be beneficial. Hope you enjoyed your trip to Spain.

Michael


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 26, 2007 10:53 am 
Newbie

Joined: Tue Mar 27, 2007 10:45 am
Posts: 5
Curious if any further progress has been made on this issue? We have been unable to rearrange our LEFT OUTER JOINs to fix the problem, so we're stuck again.


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.