-->
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: Problem persisting <map> elements (values vs. entities
PostPosted: Mon May 15, 2006 3:09 pm 
Newbie

Joined: Mon May 15, 2006 1:59 pm
Posts: 3
Version 3.1.3

When a Map field (data member) of a persisted object contains "value" elements, changes made to that map are not persisted, as opposite to the situation when a map containd entities elements (with cascade="all").

I'm not really sure if that it is a bug, but according to my understanding of "value objects" semantics things should work differently.

Lets start with:
Code:
abstract class AbstractObject {
    AbstractObject() {}
    Integer id;
    Map properties;
}

assuming that instance of an AbstractObject has unique id and a collection of properties (implemented as a map keyed by property name).
Here are two implementations of concrete classes (together with corresponding mappings):
Code:
//  properties - map of Strings
class ObjectA extends AbstractObject {
    ObjectA() {}
}

    <class name="ObjectA" table="OBJECT">
        <id name="id"
            column="ID"
            access="field"/>

        <map name="properties"
            table="PROPERTIES"
            lazy="false"
            inverse="true"
            cascade="all,delete-orphan"
            fetch="join"
            access="field">

            <key column="OBJECT"/>
            <map-key column="NAME" type="string"/>
            [b]<element column="VALUE" type="string"/>[/b]
        </map>
    </class>

In ObjectA properties are considered as Map<String, String> while in ObjectB properties will be interpreted as Map<String, Property>:
Code:
//  properties - map of Properties
class ObjectB extends AbstractObject {
    ObjectB() {}
}

class Property {
    Property() {}
    static class Id implements Serializable {
        Id() {}
        Integer object;
        String name;
    }
    Id id;
    String value;
}

    <class name="ObjectB" table="OBJECT">
        <id name="id"
            column="ID"
            access="field"/>

        <map name="properties"
            table="PROPERTIES"
            lazy="false"
            inverse="true"
            cascade="all,delete-orphan"
            fetch="join"
            access="field">

            <key column="OBJECT"/>
            <map-key column="NAME" type="string"/>
            [b]<one-to-many class="Property"/>[/b]
        </map>
    </class>

    <class name="Property" table="PROPERTIES">
        <composite-id
            name="id"
            class="Property$Id"
            access="field">

            <key-property
                name="object"
                type="integer"
                column="OBJECT"
                access="field"
            />
            <key-property
                name="name"
                type="string"
                column="NAME"
                access="field"
            />
        </composite-id>

        <property
            name="value"
            type="string"
            column="VALUE"
            access="field"
        />
    </class>

ObjectA and ObjectB provide two different views for the same table OBJECTS. The main difference here is that ObjectA maps content if the properties as <element column="VALUE" type="string"/> while ObjectB does so by <one-to-many class="Property"/>.

Here is where problems starts. Having:
Code:
        Integer objectId = new Integer(1);
        String name = "test";
        AbstractObject object = null;

Following code:
Code:
        object = new ObjectA();
        object.id = objectId;
        object.properties = new HashMap();
        String property = "abc";
        object.properties.put(name, property);
        session.save(object);

absolutely fails to persist the content of properties, while
Code:
        object = new ObjectB();
        object.id = objectId;
        object.properties = new HashMap();
        Property property = new Property();
        property.id = new Property.Id();
        property.id.object = objectId;
        property.id.name = name;
        property.value = "abc";
        object.properties.put(name, property);
        session.save(object);

works just fine.
Attempting to modify persisted object yields similar results:
Code:
        object = (AbstractObject)session.get(ObjectA.class, objectId);
        object.properties.put(name, "xyz");
        session.save(object);

has no effect on the db, when:
Code:
        object = (AbstractObject)session.get(ObjectB.class, objectId);
        Property property = (Property)object.properties.get(name);
        property.value = "xyz";
        object.properties.put(name, property);
        session.save(object);

does what it is supposed to do.

Here I include complete sources for java code:
Code:
import java.io.*;
import java.util.*;
import org.hibernate.*;
import org.hibernate.cfg.*;

abstract class AbstractObject {
    AbstractObject() {}
    Integer id;
    Map properties;
}

//properties - map of Strings
class ObjectA extends AbstractObject {
    ObjectA() {}
}

//properties - map of Properties
class ObjectB extends AbstractObject {
    ObjectB() {}
}

class Property {
    Property() {}
    static class Id implements Serializable {
        Id() {}
        Integer object;
        String name;
    }
    Id id;
    String value;
}

class Test {

    static final SessionFactory sessionFactory =
        new Configuration().configure().buildSessionFactory();

    public static void main(String[] args) throws Exception {
        Session session;

        Integer objectId = new Integer(1);
        String name = "test";
        AbstractObject object = null;
        Object property = null;

    //  CREATE
        session = sessionFactory.getCurrentSession();
        session.beginTransaction();

        //  creating object as ObjectA will not persist properties !!!
        object = new ObjectB();
        object.id = objectId;
        object.properties = new HashMap();

        if (object instanceof ObjectA) {
            property = "abc";
        }
        else
        if (object instanceof ObjectB) {
            Property p = new Property();
            p.id = new Property.Id();
            p.id.object = objectId;
            p.id.name = name;
            p.value = "abc";
            property = p;
        }
        object.properties.put(name, property);
        session.save(object);
        session.getTransaction().commit();

    //  UPDATE
        session = sessionFactory.getCurrentSession();
        session.beginTransaction();

        //  getting object as ObjectA will not persist changes !!!
        object = (AbstractObject)session.get(ObjectB.class, objectId);
        property = object.properties.get(name);
        System.out.print("old value: ");
        if (object instanceof ObjectA) {
            System.out.println(property);
            property = "xyz";
        }
        else
        if (object instanceof ObjectB) {
            System.out.println(((Property)property).value);
            ((Property)property).value = "xyz";
        }
        object.properties.put(name, property);
        session.save(object);
        session.getTransaction().commit();

    //  SELECT
        session = sessionFactory.getCurrentSession();
        session.beginTransaction();

        //  it does not matter if ObjectA or ObjectB is retrieved
        object = (AbstractObject)session.get(ObjectB.class, objectId);
        property = object.properties.get(name);
        System.out.print("new value: ");
        if (object instanceof ObjectA) {
            System.out.println(property);
        }
        else
        if (object instanceof ObjectB) {
            System.out.println(((Property)property).value);
        }
        session.getTransaction().commit();
    }
}

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

<hibernate-mapping>

    <class name="ObjectA" table="OBJECT">
        <id name="id"
            column="ID"
            access="field"/>

        <map name="properties"
            table="PROPERTIES"
            lazy="false"
            inverse="true"
            cascade="all,delete-orphan"
            fetch="join"
            access="field">

            <key column="OBJECT"/>
            <map-key column="NAME" type="string"/>
            <element column="VALUE" type="string"/>
        </map>
    </class>

    <class name="ObjectB" table="OBJECT">
        <id name="id"
            column="ID"
            access="field"/>

        <map name="properties"
            table="PROPERTIES"
            lazy="false"
            inverse="true"
            cascade="all,delete-orphan"
            fetch="join"
            access="field">

            <key column="OBJECT"/>
            <map-key column="NAME" type="string"/>
            <one-to-many class="Property"/>
        </map>
    </class>

    <class name="Property" table="PROPERTIES">
        <composite-id
            name="id"
            class="Property$Id"
            access="field">
   
            <key-property
                name="object"
                type="integer"
                column="OBJECT"
                access="field"
            />
            <key-property
                name="name"
                type="string"
                column="NAME"
                access="field"
            />
        </composite-id>

        <property
            name="value"
            type="string"
            column="VALUE"
            access="field"
        />
    </class>

</hibernate-mapping>


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.