-->
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.  [ 3 posts ] 
Author Message
 Post subject: MapKeyManyToMany: key entity not initialized when map loaded
PostPosted: Sat Mar 15, 2008 5:48 am 
Newbie

Joined: Fri Aug 29, 2003 3:28 am
Posts: 4
Hibernate version:
hibernate-3.2.5.ga
hibernate-annotations-3.3.0.ga

I have a Map<Entity, Entity> where the key entity uses a business key to implement equals()/hashCode() (as per http://www.hibernate.org/109.html best practices).

However, when the Map is initialized the key entity does not yet have its fields loaded (only the id is available) and thus the equals/hashCode methods return the same values for all keys resulting in a map that contains a single entry. If the id is used then the code works fine. Is this intended behaviour and what is the best approach if so?

Test Case:
Code:
@Entity
public class Reviewer implements Serializable {
   private Long id;
   private Map<Document, Review> reviews;

   @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }

   //@CollectionOfElements
   @ManyToMany
   @MapKeyManyToMany
   public Map<Document, Review> getReviews() { return reviews; }
   public void setReviews(Map<Document, Review> reviews) { this.reviews = reviews; }

   public void addReview(Review review) {
      if (reviews == null) reviews = new HashMap<Document, Review>();
      reviews.put(review.getDocument(), review);
   }
}

Code:
@Entity
public class Document implements Serializable {
   private Long id;
   private String url;

   public Document() {}
   public Document(String url) { this.url = url; }

   @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }

   public String getUrl() { return url; }
   public void setUrl(String url) { this.url = url; }

   @Override
   public boolean equals(Object obj) {
      if (!(obj instanceof Document)) return false;
      return new EqualsBuilder().append(url, ((Document) obj).url).isEquals();
      // works:
      //return new EqualsBuilder().append(id, ((Document) obj).id).isEquals();
   }

   @Override
   public int hashCode() {
      return new HashCodeBuilder().append(url).toHashCode();
      //return new HashCodeBuilder().append(id).toHashCode();
   }
}

Code:
@Entity
public class Review implements Serializable {
   private Long id;
   private Document document;

   public Review() {}
   public Review(Document document) {
      this.document = document;
   }

   @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }

   @ManyToOne
   public Document getDocument() { return document; }
   public void setDocument(Document document) { this.document = document; }
}

Code:
public class TestAddToMap extends TestCase {

   public void testAddToMap() {
      SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
      Session session = sessionFactory.getCurrentSession();
      session.beginTransaction();

      Document doc1 = new Document("http://myurl/document/1");
      session.save(doc1);
      Document doc2 = new Document("http://myurl/document/2");
      session.save(doc2);

      Reviewer reviewer = new Reviewer();
      session.save(reviewer);

      Review review = new Review(doc1);
      session.save(review);
      reviewer.addReview(review);
      review = new Review(doc2);
      session.save(review);
      reviewer.addReview(review);

      assertEquals(2, reviewer.getReviews().size());

      session.getTransaction().commit();
      session = sessionFactory.getCurrentSession();
      session.beginTransaction();

      Reviewer reviewer2 = (Reviewer) session.get(Reviewer.class, new Long(reviewer.getId()));
      assertEquals(2, reviewer2.getReviews().size());  // fails

      session.getTransaction().commit();
   }
}


Stack trace at point where hashCode() is called with only id available
Code:
TestAddToMap [JUnit]   
   org.eclipse.jdt.internal.junit.runner.RemoteTestRunner at localhost:3109   
      Thread [main] (Suspended (breakpoint at line 38 in Document))   
         Document.hashCode() line: 38   
         HashMap<K,V>.put(K, V) line: 418   
         PersistentMap.readFrom(ResultSet, CollectionPersister, CollectionAliases, Object) line: 259   
         BasicCollectionLoader(Loader).readCollectionElement(Object, Serializable, CollectionPersister, CollectionAliases, ResultSet, SessionImplementor) line: 1008   
         BasicCollectionLoader(Loader).readCollectionElements(Object[], ResultSet, SessionImplementor) line: 646   
         BasicCollectionLoader(Loader).getRowFromResultSet(ResultSet, SessionImplementor, QueryParameters, LockMode[], EntityKey, List, EntityKey[], boolean) line: 591   
         BasicCollectionLoader(Loader).doQuery(SessionImplementor, QueryParameters, boolean) line: 701   
         BasicCollectionLoader(Loader).doQueryAndInitializeNonLazyCollections(SessionImplementor, QueryParameters, boolean) line: 236   
         BasicCollectionLoader(Loader).loadCollection(SessionImplementor, Serializable, Type) line: 1994   
         BasicCollectionLoader(CollectionLoader).initialize(Serializable, SessionImplementor) line: 36   
         BasicCollectionPersister(AbstractCollectionPersister).initialize(Serializable, SessionImplementor) line: 565   
         DefaultInitializeCollectionEventListener.onInitializeCollection(InitializeCollectionEvent) line: 60   
         SessionImpl.initializeCollection(PersistentCollection, boolean) line: 1716   
         PersistentMap(AbstractPersistentCollection).initialize(boolean) line: 344   
         PersistentMap(AbstractPersistentCollection).read() line: 86   
         PersistentMap(AbstractPersistentCollection).readSize() line: 109   
         PersistentMap.size() line: 114   
         TestAddToMap.testAddToMap() line: 38   
         NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]   
         NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39   
         DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25   
         Method.invoke(Object, Object...) line: 585   
         TestAddToMap(TestCase).runTest() line: 164   
         TestAddToMap(TestCase).runBare() line: 130   
         TestResult$1.protect() line: 106   
         TestResult.runProtected(Test, Protectable) line: 124   
         TestResult.run(TestCase) line: 109   
         TestAddToMap(TestCase).run(TestResult) line: 120   
         TestSuite.runTest(Test, TestResult) line: 230   
         TestSuite.run(TestResult) line: 225   
         JUnit3TestReference.run(TestExecution) line: 128   
         TestExecution.run(ITestReference[]) line: 38   
         RemoteTestRunner.runTests(String[], String, TestExecution) line: 460   
         RemoteTestRunner.runTests(TestExecution) line: 673   
         RemoteTestRunner.run() line: 386   
         RemoteTestRunner.main(String[]) line: 196   


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 17, 2008 2:29 am 
Newbie

Joined: Fri Aug 29, 2003 3:28 am
Posts: 4
I should also add that the problem only occurs when entities are used for both the Map's key and value. If the value is a String then it works fine.

Seems strange that noone has encountered this issue before. Should I raise a bug in Jira?


Top
 Profile  
 
 Post subject: Re: MapKeyManyToMany: key entity not initialized when map loaded
PostPosted: Mon Aug 27, 2012 6:59 pm 
Newbie

Joined: Fri Jul 22, 2011 5:04 pm
Posts: 8
Sorry for reviving such an old thread, but did you ever find out the cause of this?
I am having almost exactly the same problem (but it is my map value entities that only have id available, and null for all other fields; the key entities are fully populated), using version 3.6.10, and jpa annotations:

Code:
    @ManyToMany
    @JoinTable(name="type_category",
        joinColumns=@JoinColumn(name="category_map_id", nullable=false),
        inverseJoinColumns=@JoinColumn(name="category_id", nullable=false))
    @MapKeyJoinColumn(name="type_id", nullable=false)
    public Map<Type, Category> getCategories() {
        return categories;
    }


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 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.