-->
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: problems with entity-mode="dynamic-map"
PostPosted: Wed Jun 18, 2008 7:54 am 
Newbie

Joined: Wed Jun 18, 2008 6:37 am
Posts: 2
Hello,

I would like to use hibernate as persistence-framework to access a database with special requirements:

- The database has many similar tables with the same structure: one "parent"-table and a "subentity"-table. The subentity has a foreign key to the parent to achieve a collection-mapping with an one-to-many association of the subentities.
- All couples of parent-/subentity tables have to be accessed by the same "physical" entity-class!

Hibernate has a feature called entity-mode="dynamic-map". This uses a java Map as entity-class with an individual entity-name.

I created two tests:

- "pojo" uses the standard pojo entity-mode with an own entity-class and is working correct.

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

<hibernate-mapping>

    <class name="hibernatetest.pojo.PojoParentEntity" table="PARENT_ENTITY">

        <id name="ID" column="PARENT_ID" type="long">
            <generator class="native" />
        </id>

        <property name="HNR" column="PARENT_HNR" type="long" />

        <property name="VARIANT" column="PARENT_VARIANT" type="string" />

        <property name="PARENT_VALUE" column="PARENT_VALUE" type="string" />

        <properties name="IDANDHNR" unique="true">
            <property name="ID" column="PARENT_ID" type="long" insert="false"
                update="false" />
            <property name="HNR" column="PARENT_HNR" type="long" insert="false"
                update="false" />
        </properties>

        <map name="SUBENTITIES" cascade="all" inverse="true" lazy="false">
            <key not-null="true" property-ref="IDANDHNR">
                <column name="SUB_PARENT_ID" not-null="true" />
                <column name="SUB_HNR" not-null="true" />
            </key>
            <map-key column="SUB_NAME" type="string" />
            <one-to-many class="hibernatetest.pojo.PojoSubEntity" />
        </map>

    </class>

    <class name="hibernatetest.pojo.PojoSubEntity" table="SUB_ENTITY">

        <id name="ID" column="SUB_ID" type="long">
            <generator class="native" />
        </id>

        <many-to-one name="PARENT" property-ref="IDANDHNR" lazy="false">
            <column name="SUB_PARENT_ID" />
            <column name="SUB_HNR" />
        </many-to-one>

        <property name="NAME" column="SUB_NAME" type="string" />

        <property name="SUB_VALUE" column="SUB_VALUE" type="string" />
    </class>
</hibernate-mapping>



- "map" tries to achieve the same behaviour with entity-mode="dynamic-map" but fails

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

<hibernate-mapping>
    <class entity-name="ParentEntity" table="PARENT_ENTITY" lazy="false">
        <tuplizer entity-mode="dynamic-map"
            class="hibernatetest.EntityTuplizer" />

        <id name="ID" column="PARENT_ID" type="long">
            <generator class="native" />
        </id>

        <property name="HNR" column="PARENT_HNR" type="long" />

        <property name="VARIANT" column="PARENT_VARIANT" type="string" />       

        <property name="PARENT_VALUE" column="PARENT_VALUE" type="string" />

        <properties name="IDANDHNR" unique="true">
            <property name="ID" column="PARENT_ID" type="long" insert="false"
                update="false" />
            <property name="HNR" column="PARENT_HNR" type="long" insert="false"
                update="false" />
        </properties>

        <map name="SUBENTITIES" cascade="all" inverse="true" lazy="false">
            <key not-null="true" property-ref="IDANDHNR" unique="true">
                <column name="SUB_PARENT_ID" not-null="true" />
                <column name="SUB_HNR" not-null="true" />
            </key>
            <map-key column="SUB_NAME" type="string" />
            <one-to-many entity-name="SubEntity" />
        </map>

    </class>

    <class entity-name="SubEntity" table="SUB_ENTITY">

        <id name="ID" column="SUB_ID" type="long">
            <generator class="native" />
        </id>

        <many-to-one name="PARENT" entity-name="ParentEntity"
            property-ref="IDANDHNR" lazy="false">
            <column name="SUB_PARENT_ID" />
            <column name="SUB_HNR" />
        </many-to-one>

        <property name="NAME" column="SUB_NAME" type="string" />

        <property name="SUB_VALUE" column="SUB_VALUE" type="string" />
    </class>

</hibernate-mapping>


MapParentEntity.java
Code:
package hibernatetest.map;

import static java.util.Collections.EMPTY_SET;
import hibernatetest.ParentEntity;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.hibernate.tuple.DynamicMapInstantiator;

public class MapParentEntity extends HashMap<String, Object> implements ParentEntity {

    private static final long   serialVersionUID  = 1L;

    private static final String SUBENTITY_TYPE    = "SubEntity";
    private static final String PARENTENTITY_TYPE = "ParentEntity";

    public MapParentEntity() {
        super();
        put(DynamicMapInstantiator.KEY, PARENTENTITY_TYPE);
    }

    // ---------------------
    // ParentEntity interface
    //

    public Object getProperty(String name) {
        return get(name);
    }

    public void setProperty(String name, Object value) {
        put(name, value);
    }

    public Object getSubPropertyValue(String name) {
        return getSUBENTITIES().get(name).get(SUB_VALUE);
    }

    @SuppressWarnings("unchecked")
    public void setSubPropertyValue(String name, String value) {
        Map<String, Map<String, Object>> subentities = getSUBENTITIES();

        Map<String, Object> subentity = subentities.get(name);

        if (subentity == null) {
            subentity = new HashMap<String, Object>();
            subentity.put(PARENT, this);
            subentity.put(NAME, name);
            subentity.put(SUB_VALUE, value);
            subentity.put(DynamicMapInstantiator.KEY, SUBENTITY_TYPE);

            subentities.put(name, subentity);
        } else {
            subentity.put(SUB_VALUE, value);
        }
    }

    @SuppressWarnings("unchecked")
    public Set<String> getSubPropertyNames() {

        Map<String, Map<String, Object>> temp = getSUBENTITIES();
        if (temp != null) {
            return temp.keySet();
        }

        return EMPTY_SET;
    }

    // ---------------------
    // private
    //

    @SuppressWarnings("unchecked")
    private Map<String, Map<String, Object>> getSUBENTITIES() {
        Map<String, Map<String, Object>> subentitiesMap = (Map<String, Map<String, Object>>) get(SUBENTITIES);

        if (subentitiesMap == null) {
            subentitiesMap = new HashMap<String, Map<String, Object>>();
            put(SUBENTITIES, subentitiesMap);
        }

        return subentitiesMap;
    }
}



Saving a parentEntity with subEntities throws a NullPointerException inside the MapAccessor class.

save exception:
Code:
java.lang.NullPointerException
   at org.hibernate.property.MapAccessor$MapGetter.get(MapAccessor.java:67)
   at org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue(AbstractComponentTuplizer.java:64)
   at org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValues(AbstractComponentTuplizer.java:70)
   at org.hibernate.type.ComponentType.getPropertyValues(ComponentType.java:353)
   at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:184)
   at org.hibernate.engine.CollectionKey.generateHashCode(CollectionKey.java:57)
   at org.hibernate.engine.CollectionKey.<init>(CollectionKey.java:45)
   at org.hibernate.engine.CollectionKey.<init>(CollectionKey.java:31)
   at org.hibernate.event.def.AbstractFlushingEventListener.postFlush(AbstractFlushingEventListener.java:342)
   at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:28)
   at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
   at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
   at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
   at hibernatetest.AbstractSaveTest.testSave(AbstractSaveTest.java:30)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:585)
   at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:163)
   at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:233)
   at org.springframework.test.context.junit4.SpringMethodRoadie$RunBeforesThenTestThenAfters.run(SpringMethodRoadie.java:333)
   at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:217)
   at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:197)
   at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:143)
   at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:142)
   at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
   at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
   at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
   at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
   at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)


Accessing the subEntities of a successfully loaded parentEntity throws a StackOverflowError

load exception:
Code:
java.lang.StackOverflowError
   at java.lang.Long.stringSize(Long.java:306)
   at java.lang.Long.toString(Long.java:242)
   at java.lang.Long.toString(Long.java:100)
   at java.lang.String.valueOf(String.java:2734)
   at java.lang.Long.toString(Long.java:733)
   at java.lang.String.valueOf(String.java:2615)
   at java.lang.StringBuffer.append(StringBuffer.java:220)
   at java.util.AbstractMap.toString(AbstractMap.java:598)
   at java.lang.String.valueOf(String.java:2615)
   at java.lang.StringBuffer.append(StringBuffer.java:220)
   at java.util.AbstractMap.toString(AbstractMap.java:598)
   at java.lang.String.valueOf(String.java:2615)
   at java.lang.StringBuffer.append(StringBuffer.java:220)
   at java.util.AbstractMap.toString(AbstractMap.java:598)
   at org.hibernate.collection.PersistentMap.toString(PersistentMap.java:252)
   at java.lang.String.valueOf(String.java:2615)
   at java.lang.StringBuffer.append(StringBuffer.java:220)
   at java.util.AbstractMap.toString(AbstractMap.java:598)
   at java.lang.String.valueOf(String.java:2615)
   at java.lang.StringBuffer.append(StringBuffer.java:220)
   at java.util.AbstractMap.toString(AbstractMap.java:598)
   at java.lang.String.valueOf(String.java:2615)
   at java.lang.StringBuffer.append(StringBuffer.java:220)
   at java.util.AbstractMap.toString(AbstractMap.java:598)
   at [...]


I don't know what is wrong. I read that dynamic-map is experimental. Maybe there are some bugs with "dynamic-map"?

If someone would like to show me, what is wrong with my test, I can send the whole test-project.

Yours sincerely
Lars


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 19, 2008 12:04 pm 
Newbie

Joined: Wed Jun 18, 2008 6:37 am
Posts: 2
Is it possible to add an attachment in this forum?

Lars


Top
 Profile  
 
 Post subject: Re: problems with entity-mode="dynamic-map"
PostPosted: Thu Dec 29, 2011 4:29 am 
Newbie

Joined: Thu Dec 29, 2011 3:26 am
Posts: 1
This is a classic problem with maps, and most collections. When using the MAP entity mode, if you try to call toString() on an entity (a map), and that map somehow contains another map which contains another map etc... which eventually refers to the original map, then you have an infinite recursion and kaboom.

You can boil down the problem to:

Code:
Map map = new HashMap();
Map map2 = new HashMap();
map.put("map2", map2);
map2.put("map", map);
System.out.println("Here is the map: " + map.toString()); // This blows up


If you may have loops in your object graph, don't call toString() on an entity! Instead, use a method that avoids recursion, e.g.:

Code:
public static String safeMapToString(Map map) {
    StringBuffer sb = new StringBuffer();
    for (Object key : map.keySet()) {
        if (sb.length() > 0)
            sb.append(",");
        sb.append(key + "=");
        Object value = map.get(key);
        if (value == null)
            sb.append("null");
        else if (value instanceof Collection)
            sb.append("[collection]");
        else if (value instanceof Map)
            sb.append("[object]");
        else
            sb.append(value.toString());
    }
    return sb.toString();
}


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.