Hibernate + Map

Olá,

tenho o seguinte relacionamento:

Orcamento 1 : N FornecedorRecurso
FornecedorRecurso 1 : N Cargo
Cargo 1 : N Empresa/Fornecedor

Na classe Orcamento, gostaria de ter um Map assim:

Onde o MapKey fosse o id do Fornecedor que está dentro de Cargo que pertence a FornecedorRecurso

Como fazer isso? É possível?

Olá!

Depois de umas pesquisas, cheguei em algum lugar. Atualmente, o Hibernate não suporta por default um Map com List no value. Cheguei nesse blog: http://blog.xebia.com/2007/10/05/mapping-multimaps-with-hibernate/

O cara consegue solucionar o problema construindo uma classe que implementa a interface UserCollectionType do Hibernate.
Seguem as classes:

MultiMapType.java

package sigo.utils;

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 {
	 
    public boolean contains(Object collection, Object entity) {
        return ((MultiMap) collection).containsValue(entity);
    }
 
    public Iterator getElementsIterator(Object collection) {
        return ((MultiMap) collection).values().iterator();
    }
 
    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();
    }
 
    public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister) throws HibernateException {
        return new PersistentMultiMap(session);
    }
 
    public PersistentCollection wrap(SessionImplementor session, Object collection) {
        return new PersistentMultiMap(session, (MultiMap) collection);
    }
 
    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 arg0)
	{
		return new MultiHashMap();
	}
}

PersistentMultiMap.java

package sigo.utils;

import java.io.Serializable;
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;

@SuppressWarnings("deprecation")
public class PersistentMultiMap extends PersistentMap implements MultiMap
{
    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public PersistentMultiMap(SessionImplementor session, MultiMap map)
    {
        super(session, map);
    }
 
    public PersistentMultiMap(SessionImplementor session)
    {
        super(session);
    }
    
    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;
	    }
    }
    
    @Override
    public Iterator entries(CollectionPersister persister)
    {
        return new KeyValueCollectionIterator(super.entries(persister));
    }

    @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 map = new HashMap();
        for (Iterator iter = collection.iterator(); iter.hasNext();) {
            Object element = iter.next();
            map.put(element, element);
        }
        return map;
    }
 
    @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;
    }
}

class RemoveItem
{
	 
    private final Object key;

    private final Object item;

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

    public Object getAddedInstance() {
        return null;
    }

    public Object getOrphan() {
        return item;
    }

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

}

final class KeyValueCollectionIterator implements Iterator {
	 
    private final Iterator parent;

    private Object key;

    private Iterator current;

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

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

    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;
    }

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

}

Não estou conseguindo usá-las. Sempre explode esse erro:

Initial SessionFactory creation failed.org.hibernate.AnnotationException: Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements: sigo.model.Orcamento.fornecedorRecursos
Tentei assim:

@OneToMany(mappedBy="orcamento")
	@MapKey(columns={@Column(name="fk_fornecedorId")})
	public MultiMapType getFornecedorRecursos()
	{
		return fornecedorRecursos;
	}

	public void setFornecedorRecursos(
			MultiMapType fornecedorRecursos)
	{
		this.fornecedorRecursos = fornecedorRecursos;
	}

Alguém tem uma luz?

abs!