-->
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: Hibernate Multimaps problem
PostPosted: Thu Jun 27, 2013 9:29 am 
Newbie

Joined: Wed Jun 26, 2013 10:05 am
Posts: 3
Hi,

I am trying to use hibernate to map a Map of Sets value type. In order to do that I used the UserCollectionType described in
http://blog.xebia.com/2007/10/05/mapping-multimaps-with-hibernate/

The 2 classes are as follows:

MultiMapType.java
Code:

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.collections.MultiHashMap;
import org.apache.commons.collections.MultiMap;
import org.hibernate.HibernateException;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.usertype.UserCollectionType;

public class MultiMapType implements UserCollectionType {

    @Override
    public boolean contains(Object collection, Object entity) {
        return ((MultiMap) collection).containsValue(entity);
    }

    @Override
    public Iterator getElementsIterator(Object collection) {
        return ((MultiMap) collection).values().iterator();
    }

    @Override
    public Object indexOf(Object collection, Object entity) {
        for (Iterator i = ((MultiMap) collection).entrySet().iterator(); i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            Collection value = (Collection) entry.getValue();
            if (value.contains(entity)) {
                return entry.getKey();
            }
        }
        return null;
    }

    public Object instantiate() {
        return new MultiHashMap();
    }

    @Override
    public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister) throws HibernateException {
        return new PersistentMultiMap(session);
    }

    @Override
    public PersistentCollection wrap(SessionImplementor session, Object collection) {
        return new PersistentMultiMap(session, (MultiMap) collection);
    }

    @Override
    public Object replaceElements(Object original, Object target, CollectionPersister persister, Object owner, Map copyCache, SessionImplementor session) throws HibernateException {

        MultiMap result = (MultiMap) target;
        result.clear();

        Iterator iter = ((java.util.Map) original).entrySet().iterator();
        while (iter.hasNext()) {
            java.util.Map.Entry me = (java.util.Map.Entry) iter.next();
            Object key = persister.getIndexType().replace(me.getKey(), null, session, owner, copyCache);
            Collection collection = (Collection) me.getValue();
            for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
                Object value = persister.getElementType().replace(iterator.next(), null, session, owner, copyCache);
                result.put(key, value);
            }
        }

        return result;
    }

    @Override
    public Object instantiate(int i) {

        if (i == -1) {
            return new MultiHashMap();
        }
        return new MultiHashMap(i);
    }
}

,

PersistentMultiMap.java

Code:

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.commons.collections.DefaultMapEntry;
import org.apache.commons.collections.MultiHashMap;
import org.apache.commons.collections.MultiMap;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.collection.PersistentMap;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.type.Type;

public class PersistentMultiMap extends PersistentMap implements MultiMap {

    public PersistentMultiMap(SessionImplementor session, MultiMap map) {
        super(session, map);
    }

    public PersistentMultiMap(SessionImplementor session) {
        super(session);
    }

    @Override
    public Object remove(Object key, Object item) {
        Object old = isPutQueueEnabled() ? readElementByIndex(key) : UNKNOWN;
        if (old == UNKNOWN) {
            write();
            return ((MultiMap) map).remove(key, item);
        } else {
            queueOperation(new RemoveItem(key, item));
            return old;
        }
    }

    private class RemoveItem implements DelayedOperation {

        private final Object key;
        private final Object item;

        private RemoveItem(Object key, Object item) {
            this.key = key;
            this.item = item;
        }

        @Override
        public Object getAddedInstance() {
            return null;
        }

        @Override
        public Object getOrphan() {
            return item;
        }

        @Override
        public void operate() {
            ((MultiMap) map).remove(key, item);
        }
    }

    @Override
    public Iterator entries(CollectionPersister persister) {
       
        return new KeyValueCollectionIterator(super.entries(persister));
    }

    private final static class KeyValueCollectionIterator implements Iterator {

        private final Iterator parent;
        private Object key;
        private Iterator current;

        private KeyValueCollectionIterator(Iterator parent) {
            this.parent = parent;
            move();
        }

        @Override
        public boolean hasNext() {
            return key != null;
        }

        @Override
        public Object next() {
            if (key == null) {
                throw new NoSuchElementException();
            } else {
                DefaultMapEntry result = new DefaultMapEntry(key, current.next());
                if (!current.hasNext()) {
                    move();
                }
                return result;
            }
        }

        private void move() {
            while (this.parent.hasNext()) {
                Map.Entry entry = (Entry) this.parent.next();
                key = entry.getKey();
                current = ((Collection) entry.getValue()).iterator();
                if (current.hasNext()) {
                    return;
                }
            }
            key = null;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    @Override
    public Serializable getSnapshot(CollectionPersister persister) throws HibernateException {
        EntityMode entityMode = getSession().getEntityMode();

        MultiHashMap clonedMap = new MultiHashMap(map.size());
        Iterator iter = map.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry e = (Map.Entry) iter.next();
            Collection collection = (Collection) e.getValue();
            for (Iterator i = collection.iterator(); i.hasNext();) {
                final Object copy = persister.getElementType().deepCopy(i.next(), entityMode, persister.getFactory());
                clonedMap.put(e.getKey(), copy);
            }
        }
        return clonedMap;
    }

    @Override
    public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
        Map sn = (Map) getSnapshot();
        if (sn.size() != map.size()) {
            return false;
        }
        Type elemType = persister.getElementType();
        for (Iterator i = sn.entrySet().iterator(); i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            Map oldState = getCollectionAsIdentityMap((Collection) entry.getValue());
            Collection newState = (Collection) map.get(entry.getKey());
            for (Iterator iter = newState.iterator(); iter.hasNext();) {
                Object newValue = iter.next();
                Object oldValue = oldState.get(newValue);
                if (newValue != null && oldValue != null && elemType.isDirty(oldValue, newValue, getSession())) {
                    return false;
                }
            }
        }
        return true;
    }

    private Map getCollectionAsIdentityMap(Collection collection) {
        Map mp = new HashMap();
        for (Iterator iter = collection.iterator(); iter.hasNext();) {
            Object element = iter.next();
            mp.put(element, element);
        }
        return mp;
    }

     @Override
    public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula) throws HibernateException {
     
        Set result = new HashSet();
        Map sn = (Map) getSnapshot();
        for (Iterator i = sn.entrySet().iterator(); i.hasNext();) {
            Map.Entry entry = (Entry) i.next();
            Collection oldState = (Collection) entry.getValue();
            Collection newState = (Collection) map.get(entry.getKey());
            for (Iterator j = oldState.iterator(); j.hasNext();) {
                Object element = j.next();
                if (!(newState.contains(element))) {
                    result.add(element);
                }
            }
        }
        return result.iterator();
    }

    @Override
    public boolean needsInserting(Object entry, int i, Type elemType) throws HibernateException {

       
        Map.Entry e = (Entry) entry;
        Map sn = (Map) getSnapshot();
        Collection oldState = (Collection) sn.get(e.getKey());
        return oldState == null || !oldState.contains(e.getValue());
    }

    @Override
    public boolean needsUpdating(Object entry, int i, Type elemType) throws HibernateException {
       
        Map.Entry e = (Entry) entry;
        Map sn = (Map) getSnapshot();
        Collection collection = (Collection) sn.get(e.getKey());
        if (collection == null) {
            return false;
        }
        for (Iterator iter = collection.iterator(); iter.hasNext();) {
            Object oldValue = iter.next();
            if (oldValue != null && oldValue.equals(e.getValue())) {
                return e.getValue() != null && elemType.isDirty(oldValue, e.getValue(), getSession());
            }
        }
        return false;
    }
}


and the mapping is:

Code:
<map name="testMap"  table="tbl_test" collection-type="MultiMapType" >
      <key column="fk_person_id"/>
      <composite-map-key class="TestKey">
        <key-property name="languageId" type="int" column="fk_language_id"></key-property>
        <key-property name="roleId" type="int" column="fk_role_id"></key-property>
      </composite-map-key>
      <composite-element class="TestValue">
        <property column="fld_level" name="level" not-null="true"/>
        <property column="fld_info" name="info" not-null="true"/>
      </composite-element>
    </map>


The 2 classes TestKey and TestValue are:

TestKey.java

Code:
public class TestKey implements java.io.Serializable {

    private int languageId;
    private int roleId;

    public TestKey() {
    }

    public TestKey(int languageId, int roleId) {
        this.languageId = languageId;
        this.roleId = roleId;
    }

    @Override
    public int hashCode() {

        int hash = 7;
        hash = 89 * hash + this.languageId;
        hash = 89 * hash + this.roleId;

        return hash;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof TestKey)) {
            return false;
        }

        final TestKey val = (TestKey) o;
        if (this.getLanguageId() != val.getLanguageId()) {
            return false;
        }
        return (this.getRoleId() == val.getRoleId());

    }

    @Override
    public String toString() {
        return "language: '" + getLanguageId() + "', "
                + "role: '" + getRoleId() + "'";
    }

    public int getLanguageId() {
        return languageId;
    }

    public void setLanguageId(int languageId) {
        this.languageId = languageId;
    }

    public int getRoleId() {
        return roleId;
    }

    public void setRoleId(int roleId) {
        this.roleId = roleId;
    }
}


TestValue.java

Code:
public class TestValue implements java.io.Serializable {

    private int level;
    private int info;

    public TestValue() {
    }

    public TestValue(int level, int info) {
        this.level = level;
        this.info = info;
    }

    @Override
    public int hashCode() {

        int hash = 7;
        hash = 89 * hash + this.level;
        hash = 89 * hash + this.info;

        return hash;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof TestValue)) {
            return false;
        }

        final TestValue val = (TestValue) o;
        if (this.getLevel() != val.getLevel()) {
            return false;
        }
        return (this.getInfo() == val.getInfo());

    }

    @Override
    public String toString() {
        return "level: '" + getLevel() + "', "
                + "info: '" + getInfo() + "'";
    }

    public int getInfo() {
        return info;
    }

    public void setInfo(int info) {
        this.info = info;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }
}


Finally, I add the org.apache.commons.collections.MultiMap property testMap in the containing entity class (Person.java) with the
appropriate getter/setter methods .

Under this setting I can retrieve the database values in testMap and add new key-value pairs in a hibernate session the following way:

Code:
TestKey key = new TestKey(1,1);
TestValue value = new TestValue(9,9);
user.getTestMap().put(key, value);


The first problem is that when I try to add a second value to the same key, for example

Code:
TestValue value1 = new TestValue(10,10);
user.getTestMap().put(key, value1);

nothing happens.
Also, when I try to clear the map
Code:
user.getTestMap().clear()

or delete a key
Code:
user.getTestMap().remove(key)


I get the following error
Code:
Exception in thread "main" java.lang.NullPointerException
        at PersistentMultiMap.getDeletes(PersistentMultiMap.java:185)
        at org.hibernate.persister.collection.AbstractCollectionPersister.deleteRows(AbstractCollectionPersister.java:1214)
        at org.hibernate.action.CollectionUpdateAction.execute(CollectionUpdateAction.java:54)
        at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        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)


Hope someone could help

Thanks in advance,

lgs


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.