-->
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.  [ 7 posts ] 
Author Message
 Post subject: Hibernate3's UserCollectionType cannot support EMF/MOF
PostPosted: Thu Mar 31, 2005 2:38 pm 
Newbie

Joined: Thu Jan 06, 2005 11:56 am
Posts: 10
First, I hope I am wrong and that someone out there can show me the light.

In MDAs, you build a UML class diagram first and then generate EMF/MOF objects from it. Yes, you could generate POJOs too but then you have other problems like serializing to XMI, integration with filesystem resources, notification of file changes and leveraging a bunch of other Eclipse features. I don't want to use POJOs - I also want to be able to serialize these very same models with Hibernate.

Now, it is very important to note that there is a fundamental difference between POJOs and EMF/MOF objects. EMF/MOF objects are a manifestation of a UML object diagram in memory - POJOs have no such semantics.

For example, in UML terms, say class A contains a List of class B instances. The association between A and B uses "composition" in UML terms (containment in EMF terms). In Hibernate terms, this means that A references B and that there is a casade "all" on the property in A. Say you create an instance of class A with key "aaa" and an instance of class B with key "bbb". You add "bbb" to the collection property on "aaa". Now say you have another instance of class A with key "foo". Then add "bbb" to the collection property on "foo". With POJOs, both "aaa" and "foo" would contain an instance of "bbb". With EMF/MOF, the act of setting "bbb" in "foo"'s collection property will *automatically* remove "bbb" from "aaa" since containment means that an instance of the model object can only be contained by one parent.

Now with that background, it is easy to see that the collection property in EMF/MOF does not use the standard java.util.List (which knows nothing about UML models). The list must be allocated by the object owning the collection. In fact, in EMF/MOF does not support a setXXX(List) method at all since any List that you would set would be unlikely to be aware of the underlying model. They only support a "List getXXX()" method which never returns null and you can add objects to.

So, I am forced to use a UserCollectionType. Unfortunately, the methods in UserCollectionType that instantiate a new UserCollection do not seem to support retrieving the instance of the object that the collection is being instantiated for. In other words, I can't call the "List getXXX()" method that I mention above. These methods are:
- /**
* Instantiate an uninitialized instance of the collection wrapper
*/
public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister)
throws HibernateException;

/**
* Wrap an instance of a collection
*/
public PersistentCollection wrap(SessionImplementor session, Object collection);

/**
* Instantiate an empty instance of the "underlying" collection (not a wrapper)
*/
public Object instantiate();

I am not sure where to look next.
Any helpful hints?
Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 21, 2005 8:02 pm 
Newbie

Joined: Thu May 19, 2005 5:06 am
Posts: 11
Quote:
Then add "bbb" to the collection property on "foo". With POJOs, both "aaa" and "foo" would contain an instance of "bbb". With EMF/MOF, the act of setting "bbb" in "foo"'s collection property will *automatically* remove "bbb" from "aaa" since containment means that an instance of the model object can only be contained by one parent.


I'm dealing with the same problem, however, I decided to place my subclasses of sets as inner classes of the from-end in the association.

Basically this problem will dissapere if one don't use lazy loading. It would also appeare using bidirectional associations with no multiplicity.

I didn't try to see if this worked yet, but I guess that it will not. Greatfull for any help, thoughts and comments.

Consider this class diagram:
Code:
[Human]-- {2} +parent +child {0..*} --[Human]


And this is the code that I hope would represent that:
Code:
package se.snigel.silvertejp.test.hibernate;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.hibernate.HibernateException;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.collection.PersistentSet;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.usertype.UserCollectionType;

/**
* @hibernate.class lazy="true" table="Human"
*/
public class Human
{
    private java.lang.Long primaryKey = null;

    private se.snigel.silvertejp.test.hibernate.Human.ChildSet children = new ChildSet();
    private se.snigel.silvertejp.test.hibernate.Human.ParentSet parents = new ParentSet();

    /**
     * @hibernate.id column="Human_PK" type="long" unsaved-value="null"
     *               generator-class="native"
     */
    public java.lang.Long getPrimaryKey()
    {
        return this.primaryKey;
    }

    public void setPrimaryKey(java.lang.Long primaryKey)
    {
        this.primaryKey = primaryKey;
    }

    /**
     * @hibernate.set lazy="true" cascade="all"
     * @hibernate.many-to-many inverse="true" table="parentHuman_childHuman_"
     *                         column="parent_FK"
     *                         class="se.snigel.silvertejp.test.hibernate.Human"
     *                         name="children"
     */
    public ChildSet getChildren()
    {
        return this.getChildren();
    }

    public void setChildren(ChildSet children)
    {
        this.children = children;
    }

    /**
     * @hibernate.set lazy="true" cascade="all"
     * @hibernate.many-to-many inverse="true" table="parentHuman_childHuman_"
     *                         column="child_FK"
     *                         class="se.snigel.silvertejp.test.hibernate.Human"
     *                         name="parents"
     */
    public ParentSet getParents()
    {
        return this.getParents();
    }

    public void setParents(ParentSet parents)
    {
        this.parents = parents;
    }

    public boolean equals(java.lang.Object o)
    {
        if (o == null)
        {
            return false;
        }

        if (o == this)
        {
            return true;
        }

        if (this.getPrimaryKey() == null)
        {
            return false;
        }

        if (!o.getClass().equals(this.getClass()))
        {
            return false;
        }

        return this.getPrimaryKey().equals(((se.snigel.silvertejp.test.hibernate.Human) o).getPrimaryKey());
    }

    public int hashCode()
    {
        if (getPrimaryKey() != null)
        {
            return getPrimaryKey().hashCode();
        }
        else
        {
            return super.hashCode();
        }
    }

    public class ChildSetUserCollectionType implements UserCollectionType
    {
        /**
         * Instantiate an uninitialized instance of the collection wrapper
         */
        public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister) throws HibernateException
        {
            return new PersistentSet(session);
        }

        /**
         * Wrap an instance of a collection
         */
        public PersistentCollection wrap(SessionImplementor session, Object collection)
        {
            return new PersistentSet(session, (ChildSet) collection);
        }

        /**
         * Return an iterator over the elements of this collection - the passed
         * collection instance may or may not be a wrapper
         */
        public Iterator getElementsIterator(Object collection)
        {
            return ((ChildSet) collection).iterator();
        }

        /**
         * Optional operation. Does the collection contain the entity instance?
         */
        public boolean contains(Object collection, Object entity)
        {
            return ((ChildSet) collection).contains(entity);
        }

        /**
         * Optional operation. Return the index of the entity in the collection.
         */
        public Object indexOf(Object collection, Object entity)
        {
            return null;
        }

        /**
         * Replace the elements of a collection with the elements of another
         * collection
         */
        public void replaceElements(Object source, Object target, CollectionPersister persister, Object owner, Map copyCache, SessionImplementor session)
                throws HibernateException
        {
            Set castedTarget = (ChildSet) target;
            castedTarget.clear();
            castedTarget.addAll((ChildSet) source);
        }

        /**
         * Instantiate an empty instance of the "underlying" collection (not a
         * wrapper)
         */
        public Object instantiate()
        {
            return new ChildSet();
        }

    }

    public class ChildSet extends java.util.HashSet
    {
        public boolean add(java.lang.Object child)
        {
            se.snigel.silvertejp.test.hibernate.Human member = (se.snigel.silvertejp.test.hibernate.Human) child;
            boolean added = super.add(member);

            // link other end instance with this instance.
            if (added && !member.getParents().contains(se.snigel.silvertejp.test.hibernate.Human.this))
            {
                member.getParents().add(se.snigel.silvertejp.test.hibernate.Human.this);
            }

            return added;
        }

        public boolean remove(java.lang.Object child)
        {
            se.snigel.silvertejp.test.hibernate.Human member = (se.snigel.silvertejp.test.hibernate.Human) child;
            boolean removed = super.remove(member);

            // unlink reference in other end
            if (removed && member.getParents().contains(se.snigel.silvertejp.test.hibernate.Human.this))
            {
                member.getParents().remove(se.snigel.silvertejp.test.hibernate.Human.this);
            }

            return removed;
        }
    }

    public class ParentSetUserCollectionType implements UserCollectionType
    {
        /**
         * Instantiate an uninitialized instance of the collection wrapper
         */
        public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister) throws HibernateException
        {
            return new PersistentSet(session);
        }

        /**
         * Wrap an instance of a collection
         */
        public PersistentCollection wrap(SessionImplementor session, Object collection)
        {
            return new PersistentSet(session, (ParentSet) collection);
        }

        /**
         * Return an iterator over the elements of this collection - the passed
         * collection instance may or may not be a wrapper
         */
        public Iterator getElementsIterator(Object collection)
        {
            return ((ParentSet) collection).iterator();
        }

        /**
         * Optional operation. Does the collection contain the entity instance?
         */
        public boolean contains(Object collection, Object entity)
        {
            return ((ParentSet) collection).contains(entity);
        }

        /**
         * Optional operation. Return the index of the entity in the collection.
         */
        public Object indexOf(Object collection, Object entity)
        {
            return null;
        }

        /**
         * Replace the elements of a collection with the elements of another
         * collection
         */
        public void replaceElements(Object source, Object target, CollectionPersister persister, Object owner, Map copyCache, SessionImplementor session)
                throws HibernateException
        {
            Set castedTarget = (ParentSet) target;
            castedTarget.clear();
            castedTarget.addAll((ParentSet) source);
        }

        /**
         * Instantiate an empty instance of the "underlying" collection (not a
         * wrapper)
         */
        public Object instantiate()
        {
            return new ParentSet();
        }

    }

    public class ParentSet extends java.util.HashSet
    {
        public boolean add(java.lang.Object parent)
        {
            se.snigel.silvertejp.test.hibernate.Human member = (se.snigel.silvertejp.test.hibernate.Human) parent;

            if (this.size() >= 2)
            {
                throw new ArrayIndexOutOfBoundsException("Max size is set to 2.");
            }

            boolean added = super.add(member);

            // link other end instance with this instance.
            if (added && !member.getChildren().contains(se.snigel.silvertejp.test.hibernate.Human.this))
            {
                member.getChildren().add(se.snigel.silvertejp.test.hibernate.Human.this);
            }

            return added;
        }

        public boolean remove(java.lang.Object parent)
        {
            se.snigel.silvertejp.test.hibernate.Human member = (se.snigel.silvertejp.test.hibernate.Human) parent;
            boolean removed = super.remove(member);

            // unlink reference in other end
            if (removed && member.getChildren().contains(se.snigel.silvertejp.test.hibernate.Human.this))
            {
                member.getChildren().remove(se.snigel.silvertejp.test.hibernate.Human.this);
            }

            return removed;
        }
    }
}



Top
 Profile  
 
 Post subject:
PostPosted: Sat May 21, 2005 9:38 pm 
Newbie

Joined: Thu Jan 06, 2005 11:56 am
Posts: 10
Hi,
Thanks for the reply.
I have made some progress in determining the sticky points in getting this to work.
They are:

1. UserCollectionType needs the following method signature changed to
PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key)
in order to get the correct owning EMF object instance for the collection.
This is a trivial change to Hibernate (I wish none had to be made).

2. Lazy loading of collections is extremely difficult to implement
(I haven't got lazy or non-lazy done yet).
It would likely take an EMF and Hibernate expert
(I am neither and I think it may be hard to find someone who is).
EMF supports lazy loading from a ResourceSet.
An EMF URIConverter takes a URI reference and resolves the InputStream from it.
What we are talking about is lazy loading from a Hibernate Session.
Hibernate sessions return java objects and I am not sure if EMF would support a converter working on instantiated objects instead of InputStreams (it very well may).
I just am not sure how EMF proxy uris and objects would integrate with Hibernate's versions of the same thing.
This is critical to implement.

3. EMF does not support set methods for collection attributes on their generated model objects.
This makes sense because you can't use any old collection but an EList implementation that has knowledge of the model.
Hibernate supports overriding the default Setter (which is good) but instead of using the Setter.set() method everywhere
in the Hibernate internals, they also use the Setter.getMethod() - not sure where though. Any use of this method will likely not work with EMF.

Your example has the following issues (correct me if I am wrong).
- It is not using EMF since the generated model object (Human) is a POJO instead of an EObject.
- Only supports a limited subset of UML as you described.
- The UserCollectionType names (ParentSet and ChildSet) seems really tied to the model (or a very small subset of UML)
- Uses HashSets which will lose ordering (perhaps not important to the sample but would only apply to a small subset of UML).

Thanks again.


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 21, 2005 10:39 pm 
Newbie

Joined: Thu May 19, 2005 5:06 am
Posts: 11
mkanaley wrote:
Your example has the following issues (correct me if I am wrong).
- It is not using EMF since the generated model object (Human) is a POJO instead of an EObject.
- Only supports a limited subset of UML as you described.
- The UserCollectionType names (ParentSet and ChildSet) seems really tied to the model (or a very small subset of UML)
- Uses HashSets which will lose ordering (perhaps not important to the sample but would only apply to a small subset of UML).


Let me explain the code. It's generated from my own tool, Silvertejp, an ASCII UML class diagram interpreter and code generator. That was really just a small example to show the problem with automatically bounded binary associations: if an instance of human is bound as child, the parent should be autmatically set in that child.

The generator allows composite/shared aggregated and qualified binary associations, with and without association classes, generalizations and realizations. Future implementation will also accept ordered sets.
If you're interested, Silvertejp is available in the CVS repository of sourceforge.

I don not work with EMF, the class diagram metamodel is my own.

Basically Silvertejp is all about ad-hoc, thus everything is as you point out very tied to the model. But that is ok. Thats the plan.

I'll be working some with the Hibernate code tomorrow and see if I can't get it to instanceate the UserCollectionType as an inner class. That would fix both our problems.


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 21, 2005 10:42 pm 
Newbie

Joined: Thu May 19, 2005 5:06 am
Posts: 11
mkanaley wrote:
1. UserCollectionType needs the following method signature changed to
PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key)
in order to get the correct owning EMF object instance for the collection.
This is a trivial change to Hibernate (I wish none had to be made).


key is not needed. An inner class can find the way to the outer class. It is enough if Hibernate knows the instance of the outer class when creating the instance of the inner class. And hibernate does know of an instance of the outer class.

As I menthoned in my last post, I'll patch hibernate to fix that.


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 23, 2005 4:08 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
Hi guys,

Is it really necessary for the collection to be bound to it's "parent" on construction time ?

Is it not possible to do that after creation time ?

/max

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 26, 2005 10:20 am 
Newbie

Joined: Thu May 19, 2005 5:06 am
Posts: 11
max wrote:
Hi guys,

Is it really necessary for the collection to be bound to it's "parent" on construction time ?

Is it not possible to do that after creation time ?

/max


I really want to use non static inner classes for visibility features.

If you have the time, please dry code an example of how you would solve it with linking the owner ("parent") after construction.

--

karl


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