-->
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: Serialization issue with many-to-many data structure.
PostPosted: Wed Oct 05, 2005 6:06 pm 
Expert
Expert

Joined: Sat Jan 17, 2004 2:57 pm
Posts: 329
Location: In the basement in my underwear
This isn't really a Hibernate code base issue but I am hoping that someone might be able to shed some insight on the following issue. While I don't necessariliy think that a person would want to do this I still didn't get the results that I had initially expected.

Given a many-to-many data structure of DataObjectXref1 that contains a set of DataObjectXref2 and where DataObjectXref2 contains a set of DataObjectXref1 I encounter an issue when sending the object through a simple cloning via Serialization.

The issue is that after the serialization some of the HashSets are broken as they get populated with objects that aren't fully initialized yet. So the object is added to the HashSet, the object is hashed then the property the hashcode depends is populated, changing the overall hashcode.

Refer to the following unit test as an example.

Code:
import junit.framework.TestCase;
import org.hibernate.util.SerializationHelper;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class TestSerialize extends TestCase implements Serializable {
    public void testSerializeManyToMany() throws Exception {
        DataObjectXref1 dataObjectXref1 = new DataObjectXref1(new Integer(100));
        DataObjectXref2 dataObjectXref2 = new DataObjectXref2(new Integer(200));

        dataObjectXref1.addXref2(dataObjectXref2);
        dataObjectXref2.addXref1(dataObjectXref1);

        assertTrue(dataObjectXref1.getXref2s().contains(dataObjectXref2));
        assertTrue(dataObjectXref2.getXref1s().contains(dataObjectXref1));

        //pull the xref2 from the set, it's the same reference but it's to illustrate a point further down
        DataObjectXref2 xref2ReferenceFromXref1Set = ((DataObjectXref2) dataObjectXref1.getXref2s().toArray()[0]);
        assertTrue(xref2ReferenceFromXref1Set.getXref1s().contains(dataObjectXref1));

        //clone the first xref object
        DataObjectXref1 clonedDataObjectXref1 = (DataObjectXref1) SerializationHelper.clone(dataObjectXref1);

        //see if the second xref object is still contained in the first's xref2 set
        assertTrue(clonedDataObjectXref1.getXref2s().contains(dataObjectXref2));

        //pull the cloned xref2 reference out of the collection of the cloned xref1
        xref2ReferenceFromXref1Set = ((DataObjectXref2) clonedDataObjectXref1.getXref2s().toArray()[0]);

        //the cloned xref2 reference cannot find the xref1 object by use of contains (nor will it via add() or remove())
        assertFalse(xref2ReferenceFromXref1Set.getXref1s().contains(clonedDataObjectXref1));

        //verify that the xref1 reference actually does exist in the set
        //by using a List it iterates through the list and calls equals() on each element
        List itReallyDoesExist = new ArrayList(xref2ReferenceFromXref1Set.getXref1s());
        assertTrue(itReallyDoesExist.contains(clonedDataObjectXref1));
    }


    private class DataObjectXref1 implements Serializable {
        private Integer someBusinessKey;
        private Set xref2s;

        public DataObjectXref1(Integer key) {
            this.someBusinessKey = key;
            xref2s = new HashSet();
        }

        public void addXref2(DataObjectXref2 xref2) {
            xref2s.add(xref2);
        }

        public Set getXref2s() {
            return xref2s;
        }

        public boolean equals(Object value) {
            if (this == value) {
                return true;
            }
            if (!(value instanceof DataObjectXref1)) {
                return false;
            }

            final DataObjectXref1 dataObjectXref1 = (DataObjectXref1) value;

            if (someBusinessKey != null ? !someBusinessKey.equals(dataObjectXref1.someBusinessKey) : dataObjectXref1.someBusinessKey != null) {
                return false;
            }

            return true;
        }

        public int hashCode() {
            int hashCode = (someBusinessKey != null ? someBusinessKey.hashCode() : 0);
            return hashCode;
        }
    }

    private class DataObjectXref2 implements Serializable {
        private Integer someBusinessKey;
        private Set xref1s;

        public DataObjectXref2(Integer key) {
            this.someBusinessKey = key;
            this.xref1s = new HashSet();
        }

        public void addXref1(DataObjectXref1 xref1) {
            xref1s.add(xref1);
        }

        public Set getXref1s() {
            return xref1s;
        }

        public boolean equals(Object value) {
            if (this == value) {
                return true;
            }
            if (!(value instanceof DataObjectXref2)) {
                return false;
            }

            final DataObjectXref2 dataObjectXref2 = (DataObjectXref2) value;

            if (someBusinessKey != null ? !someBusinessKey.equals(dataObjectXref2.someBusinessKey) : dataObjectXref2.someBusinessKey != null) {
                return false;
            }

            return true;
        }

        public int hashCode() {
            int hashCode = (someBusinessKey != null ? someBusinessKey.hashCode() : 0);
            return hashCode;
        }
    }
}


In the end, depending on how I walk my object graph I will get differing results. For example, I can find either the Xref1 and Xref2 objects in the sets of each other before serializing. After serializing Xref1, I can't find it from traversing down the graph and coming back up, even though it is in the set. (since the underlying HashMap of the Xref1 set on Xref2 is hashed off the Xref1 that wasn't fully populated yet).

Is this a simple case of "don't do that" or am I missing something fundamentally simple here?

Note, that we also encountered the same type of issue with key many-to-one elements where the objects were dropped into one-to-many relationships (again using Set). We still have the same issue but are slowly changing any composite primary keys into surrogate keys.


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.