-->
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.  [ 1 post ] 
Author Message
 Post subject: Issue with join fetched entity keys of a map
PostPosted: Tue Nov 18, 2008 1:37 pm 
Newbie

Joined: Mon Nov 15, 2004 1:52 pm
Posts: 7
Hi all,

I am having an issue doing a HQL query with a map using entities as keys, when join fetching those keys : the returned map has only 1 entry while there are several !equals() keys in the persistence.


A quick description of the example below :
class Root has a Map<Key, Integer>.
Key has a property on which the business hashcode() and equals() are based.

With 3 key-value pairs in the database, the following HQL query returns a map with only 1 key-value pair :
Code:
from Root f
   left join fetch f.map map
   join fetch map.index

The SQL looks good (see below), fetching the property of Key.

Without the join fetch on the key ("from Root f left join fetch f.map"), it works, but I want to avoid the n+1 selects.


From what I saw with the debugger, here is what I understood :
- the key entities are Map.put() in the map "hydrated", but before being "materialized"
- as they are join fetched, they are put in the map not as proxies but as real instances of Key
- so their business hashcode & equals are used
- but as they are not materialized, they all are in the same state (no-arg constructor)
=> each key-value pair overwrites the last one as the keys are all the same, and materialisation will only find 1 entry.

Perhaps I made a mistake somewhere, or perhaps join fetching map keys is not supposed to be done (as I used a rather obscure CollectionPropertyNames.COLLECTION_INDEX="index" pseudo-property to refer to the keys in the from).
But I am stuck for a few days now, searching the jira/forums and debugging, so any help is welcome.
Thanks in advance.

Hibernate version:
3.3.1.GA (and 3.2.6.GA)

Mapping documents:
Code:
<class name="Root" table="Root">
   <id name="id" access="field">
      <generator class="native" />
   </id>
   <map name="map" table="Root_map" access="field">
      <key column="root_id" />
      <map-key-many-to-many class="Key"  column="key_id"/>
      <element type="java.lang.Integer" column="value" />
   </map>
</class>
   
<class name="Key" table="Key">
   <id name="id" access="field">
      <generator class="native" />
   </id>
   <property name="info" column="info"/>
</class>


SQL (oracle)
Code:
CREATE TABLE Root (
  id INTEGER
);

CREATE TABLE Root_map (
  root_id INTEGER,
  key_id integer,
  value integer
);

CREATE TABLE Key (
  id INTEGER,
  info VARCHAR2(4000)
);

insert into Key (info, id) values ('i0', 0);
insert into Key (info, id) values ('i1', 1);
insert into Key (info, id) values ('i2', 2);
insert into Root (id) values (0);
insert into Root_map (root_id, key_id, value) values (0, 0, 0);
insert into Root_map (root_id, key_id, value) values (0, 1, -1);
insert into Root_map (root_id, key_id, value) values (0, 2, -2);


Java POJO
Code:
public class Root {
   public static final String PROP_MAP = "map";

   @SuppressWarnings("unused")
   private Long id;
   private Map<Key, Integer> map = new HashMap<Key, Integer>();

   public Map<Key, Integer> getMap() {
      return map;
   }
   public Long getId() {
      return id;
   }
}

and
Code:
public class Key {
   public static final String PROP_INFO = "info";
   private Long id;
   private String info;

   public Key() {
   }

   public Key(String info) {
      this.info = info;
   }

   public Long getId() {
      return id;
   }
   
   public String getInfo() {
      return info;
   }
   public void setInfo(String info) {
      this.info = info;
   }
   
   @Override
   public int hashCode() {
      final int PRIME = 31;
      int result = 1;
      result = PRIME * result + ((info == null) ? 0 : info.hashCode());
      return result;
   }
   @Override
   public boolean equals(Object obj) {
      if (this == obj)
         return true;
      if (obj == null)
         return false;
      if (getClass() != obj.getClass())
         return false;
      final Key other = (Key) obj;
      if (info == null) {
         if (other.info != null)
            return false;
      }
      else if (!info.equals(other.info))
         return false;
      return true;
   }
}


Code between sessionFactory.openSession() and session.close():
Code:
Query q = session.createQuery(
   "from " + Root.class.getName() + " f "
   + " left join fetch f." + Root.PROP_MAP + " map"
   + " join fetch map." + CollectionPropertyNames.COLLECTION_INDEX
   );

List<Root> list = q.list();

// database is initialized by SQL with 3 entries
assertEquals(3, list.get(0).getMap().size());


Full stack trace of any exception that occurs:
Code:
junit.framework.AssertionFailedError: expected:<3> but was:<1>


Name and version of the database you are using:
Oracle 9.2.0.1.0

The generated SQL (show_sql=true):
Code:
SELECT
  root0_.id as id3_0_,
  key2_.id as id5_1_,
  map1_.root_id as root1_0__,
  map1_.value as value0__,
  map1_.key_id as key3_0__,
  key2_.info as info5_1_
FROM
  Root root0_,
  Root_map map1_,
  Key key2_
WHERE
  root0_.id=map1_.root_id(+) and
  map1_.key_id=key2_.id



Debug level Hibernate log excerpt:
Beginning at q.list() :
Code:
17:23:30,763 DEBUG AbstractBatcher:410 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
17:23:30,763 DEBUG ConnectionManager:444 - opening JDBC connection
17:23:30,779 DEBUG SQL:111 - select root0_.id as id3_0_, key2_.id as id5_1_, map1_.root_id as root1_0__, map1_.value as value0__, map1_.key_id as key3_0__, key2_.info as info5_1_ from Root root0_, Root_map map1_, Key key2_ where root0_.id=map1_.root_id(+) and map1_.key_id=key2_.id
17:23:30,795 DEBUG AbstractBatcher:426 - about to open ResultSet (open ResultSets: 0, globally: 0)
17:23:30,810 DEBUG Loader:1197 - result row: EntityKey[Root#0], EntityKey[Key#0]
17:23:30,857 DEBUG Loader:1008 - found row of collection: [Root.map#0]
17:23:30,920 DEBUG Loader:1197 - result row: EntityKey[Root#0], EntityKey[Key#1]
17:23:30,920 DEBUG Loader:1008 - found row of collection: [Root.map#0]
17:23:30,935 DEBUG Loader:1197 - result row: EntityKey[Root#0], EntityKey[Key#2]
17:23:30,935 DEBUG Loader:1008 - found row of collection: [Root.map#0]
17:23:30,935 DEBUG AbstractBatcher:433 - about to close ResultSet (open ResultSets: 1, globally: 1)
17:23:30,951 DEBUG AbstractBatcher:418 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
17:23:30,951 DEBUG TwoPhaseLoad:130 - resolving associations for [Root#0]
17:23:30,966 DEBUG TwoPhaseLoad:226 - done materializing entity [Root#0]
17:23:30,982 DEBUG TwoPhaseLoad:130 - resolving associations for [Key#0]
17:23:30,982 DEBUG TwoPhaseLoad:226 - done materializing entity [Key#0]
17:23:30,998 DEBUG TwoPhaseLoad:130 - resolving associations for [Key#1]
17:23:30,998 DEBUG TwoPhaseLoad:226 - done materializing entity [Key#1]
17:23:30,998 DEBUG TwoPhaseLoad:130 - resolving associations for [Key#2]
17:23:31,013 DEBUG TwoPhaseLoad:226 - done materializing entity [Key#2]
17:23:31,013 DEBUG CollectionLoadContext:240 - 1 collections were found in result set for role: Root.map
17:23:31,045 DEBUG CollectionLoadContext:283 - collection fully initialized: [Root.map#0]
17:23:31,045 DEBUG CollectionLoadContext:249 - 1 collections initialized for role: Root.map
17:23:31,060 DEBUG StatefulPersistenceContext:860 - initializing non-lazy collections
17:23:31,060 DEBUG ConnectionManager:325 - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!


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

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.