-->
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.  [ 4 posts ] 
Author Message
 Post subject: Help with map-key-many-to-many for java.util.Map property
PostPosted: Fri Feb 24, 2006 7:15 am 
Newbie

Joined: Fri Feb 24, 2006 4:17 am
Posts: 4
I'm having problems trying to map a many-to-many relationship between two classes.

I have a class called domain.Category and I have a class called domain.Agent. Agents can earn points in a given category and so a property of domain.Agent is a java.util.Map<Category, Integer> that stores the points that agent has in the specified Category which serves as the key for the Map.

Here's a look at what the classes look like:
Code:
class Category {
    private Long id;
    private String name;
    public int hashCode() { return name.hashCode(); }
    public boolean equals(Object o) {
   Category c = (Category) o;
            return c.name.equals(this.name);
    }
}

class Agent {
    private Long id;
    private String name;
    private Map<Category, Integer> agentCategoryPoints;
    public int getPointsForCategory(final Category category) {
        if (category == null) {
            throw new IllegalArgumentException();
        }
        return agentCategoryPoints.get(category);
    }
}


My mapping file seems to be at least somewhat correct because it *is* saving the data to the database correctly.

However whenever I try to reload an Agent instance that has Category scores properly saved from the database the agentCategoryPoints field doesn't actually get initialized with any values. Upon closer inspection after I load the Agent instance I can see that agentCategoryPoints is of type org.hibernate.collection.PersistentMap however it's size is 0 and it's not null, but when I try to do agentCategoryPoints I get a NullPointerException on the line where I call from with Agent:

return agentCategoryPoints.get(category);

The peculiar thing is that neither the category object nor the agentCategoryPoints object is null. I can see this in the debugger. When it loads the instance of Agent which has data that corresponds correctly to data previously saved for agentCategoryPoints in the agent_category_points table the agentCategoryPoints member field is set to an instance of org.hibernate.collection.PersistentMap with a size of 0. Setting lazy="false" for the agentCategoryPoints field mapping doesn't seem to help.

Hibernate version:
3.1.2 in conjunction with SpringFramework 1.2.6

Mapping documents:

Code:
<hibernate-mapping default-access="field">
    <class name="domain.Category" table="category">
        <id name="id" column="category_id" unsaved-value="0">
            <generator class="native"/>
        </id>

        <property name="name" column="name" unique="true" length="50" not-null="true"/>

        <!-- many-to-many join table for related categories (which are optional) -->
        <set name="relatedCategories" table="related_tags" lazy="false">
            <key column="category_id"/>
            <many-to-many column="related_category_id" class="domain.Category"/>
        </set>
    </class>

    <class name="domain.Agent" table="agent">
        <id name="id" column="id" unsaved-value="0">
            <generator class="native"/>
        </id>

        <property name="name" column="name" length="50" not-null="true"/>

        <map name="agentCategoryPoints" cascade="all,delete-orphan" inverse="true" table="agent_category_pts" lazy="false" access="field">
            <key column="agent_id"/>
            <map-key-many-to-many class="domain.Category" column="category_id" />
            <element type="int" column="points" not-null="true"/>
        </map>
    </class>

</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():

I'm doing a simple find by Id for the agent (using the Spring hibernate template):

Code:
        final Agent agent = (Agent) getHibernateTemplate().get(AgentImpl.class, id);


Full stack trace of any exception that occurs:

Code:
java.lang.NullPointerException
   at domain.Agent.getPointsForCategory(Agent.java:108)
   at domain.UserPersistenceIntegrationTest.testCreateUserAndAgent(UserPersistenceIntegrationTest.java:183)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at com.intellij.rt.execution.junit2.JUnitStarter.main(JUnitStarter.java:32)


Name and version of the database you are using:

MySQL 5.0.18
Code:
Code:
Code:
Code:
Code:


Top
 Profile  
 
 Post subject:
PostPosted: Sun Feb 26, 2006 7:26 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
You haven't defined your initial map, the one that's checked before the Agent is saved. As you can see from your exception, hibernate itself isn't having any problem with the mapping: it's just the someone is called getPointsForFactory before hibernate has called setAgentCategoryPoints.

Change the declaration of Agent's Map to be
Code:
private Map<Category, Integer> agentCategoryPoints = new HashMap<Categyory, Integer>();
Obviously this is vital when you're setting up a new Agent object, but apparently it's also important when loading an existing one.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 20, 2006 10:10 am 
Newbie

Joined: Mon Mar 20, 2006 10:01 am
Posts: 1
Quote:
The peculiar thing is that neither the category object nor the agentCategoryPoints object is null. I can see this in the debugger. When it loads the instance of Agent which has data that corresponds correctly to data previously saved for agentCategoryPoints in the agent_category_points table the agentCategoryPoints member field is set to an instance of org.hibernate.collection.PersistentMap with a size of 0. Setting lazy="false" for the agentCategoryPoints field mapping doesn't seem to help.


My Problem with this MAP is, that the mapped values(here the Integer) in this PersistentMap are proxied objects(EnhancedByCGLib). The problem now is, i cannot check the equality of those Objects anymore...

When i overwrite hashCode() and equals(), i get the following error when accessing a proxied object:
Code:
20.03.2006 15:13:08 ERROR org.hibernate.LazyInitializationException - could not initialize proxy - the owning Session was closed
org.hibernate.LazyInitializationException: could not initialize proxy - the owning Session was closed
   at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:56)
   at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:98)
   at org.hibernate.proxy.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:158)
   at de.quelle.cms.core.user.Group$$EnhancerByCGLIB$$a6f3d382.getName(<generated>)
   at de.quelle.cms.core.user.swing.UserListCellRenderer.getListCellRendererComponent(UserListCellRenderer.java:34)
   at javax.swing.plaf.basic.BasicListUI.updateLayoutState(BasicListUI.java:1148)
   at javax.swing.plaf.basic.BasicListUI.maybeUpdateLayoutState(BasicListUI.java:1098)
   at javax.swing.plaf.basic.BasicListUI.paint(BasicListUI.java:234)
   at javax.swing.plaf.ComponentUI.update(ComponentUI.java:142)
   at javax.swing.JComponent.paintComponent(JComponent.java:742)
   at javax.swing.JComponent.paint(JComponent.java:1005)
   at javax.swing.JComponent.paintChildren(JComponent.java:842)
   at javax.swing.JComponent.paint(JComponent.java:1014)
   at javax.swing.JViewport.paint(JViewport.java:728)
   at javax.swing.JComponent.paintChildren(JComponent.java:842)
   at javax.swing.JComponent.paint(JComponent.java:1014)
   at javax.swing.JComponent.paintChildren(JComponent.java:842)
   at javax.swing.JComponent.paint(JComponent.java:1014)
   at javax.swing.JComponent.paintChildren(JComponent.java:842)
   at javax.swing.JComponent.paint(JComponent.java:1014)
   at javax.swing.JComponent.paintChildren(JComponent.java:842)
   at javax.swing.JComponent.paint(JComponent.java:1014)
   at javax.swing.JComponent.paintChildren(JComponent.java:842)
   at javax.swing.JComponent.paint(JComponent.java:1014)
   at javax.swing.JComponent.paintChildren(JComponent.java:842)
   at javax.swing.JComponent.paint(JComponent.java:1014)
   at javax.swing.JComponent.paintChildren(JComponent.java:842)
   at javax.swing.JComponent.paint(JComponent.java:1014)
   at javax.swing.JComponent.paintChildren(JComponent.java:842)
   at javax.swing.JComponent.paint(JComponent.java:1014)
   at javax.swing.JLayeredPane.paint(JLayeredPane.java:559)
   at javax.swing.JComponent.paintChildren(JComponent.java:842)
   at javax.swing.JComponent.paintWithOffscreenBuffer(JComponent.java:4970)
   at javax.swing.JComponent.paintDoubleBuffered(JComponent.java:4916)
   at javax.swing.JComponent.paint(JComponent.java:995)
   at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:21)
   at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:60)
   at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
   at java.awt.Container.paint(Container.java:1709)
   at sun.awt.RepaintArea.paintComponent(RepaintArea.java:248)
   at sun.awt.RepaintArea.paint(RepaintArea.java:224)
   at sun.awt.windows.WComponentPeer.handleEvent(WComponentPeer.java:254)
   at java.awt.Component.dispatchEventImpl(Component.java:4031)
   at java.awt.Container.dispatchEventImpl(Container.java:2024)
   at java.awt.Window.dispatchEventImpl(Window.java:1774)
   at java.awt.Component.dispatchEvent(Component.java:3803)
   at java.awt.EventQueue.dispatchEvent(EventQueue.java:463)
   at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:242)
   at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)
   at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
   at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
   at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)



When i dont overwrite hashCode() i can access the proxies without problems, seems to me a problem with hashCode-overwriting or something.
How can Hibernate be told to give me the real Objects and not proxies?

spacer


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 20, 2006 5:48 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
First, re: the issue with your hashCode/equals throwing an exception: this is because you're calling them after you've closed the session, but the methods are trying to access proxied contents. hashCode/equals methods on hibernate-mapped objects should use business keys (serial numbers, employee ids, any "unique identifier" that isn't the database identifier) and those generally shouldn't be proxied. In your case, I presume that the hashCode/equals that's being called is the one on Agent, and it's using values in the map to generate hashCode/equals values. While this is correct for pure java objects, it's (usually) not correct for hibernate mapped objects: the Agent's hashCode should be based solely off name (assuming that name is unique per Agent).

Now to your question: getting hibernate to get real objects rather than proxies depends on how you're getting the objects. If you're using Session.load/get, then the map won't be proxied, because you've specified lazy="false" in the mapping. If you're using HQL, then you have to set up your query to explicitly fetch the joined objects. See sections 14.3 (Associations and Joins) and 19.1 (Fetching Strategies) of the ref docs.

You might also consider adapting the Open Session In View pattern for swing. In fact, that's probably already been done, but I don't know where you might read up on it. The basic idea would be to open the session before starting your rendering, and not closing it until after the image is drawn. Of course, this only works if you're drawing static images, otherwise the session will be open for too long, but if you can find out more about using hibernate with swing, then you might be able to avoid all these sorts of issues completely.


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