-->
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.  [ 4 posts ] 
Author Message
 Post subject: Mapping XML Nodes - possible limitation? With simple example
PostPosted: Tue Jun 08, 2010 3:54 pm 
Newbie

Joined: Tue Jun 08, 2010 3:08 pm
Posts: 5
Location: Rio de Janeiro, Brasil
Hi guys.

This is my first post.
Imagine this example XML that I want to map to the database, using the new XML Mapping feature of Hibernate:
Code:
<example>
   <transp>
      <reboque>
         <placa>aaa</placa>
      </reboque>
      <reboque>
         <placa>bbb</placa>
      </reboque>
      <vol>
         <pesoB>yyy</pesoB>
      </vol>
      <vol>
         <pesoB>xxx</pesoB>
      </vol>
   </transp>
</example>


This XML have two collections (reboque, vol) with the same parent node (transp). So, the mapping that should work is this one:

Code:
<class entity-name="Example" table="example" node="example">
   <id name="ExampleId" column="SQ_EXAMPLE" type="long">
      <generator class="sequence">
         <param name="sequence">SQ_EXAMPLE</param>
      </generator>
   </id>
   <component name="transp" node="transp">
      <bag name="reboqueBag" node="." embed-xml="true" cascade="all">
         <key column="SQ_NFE_NOTA" />
         <one-to-many entity-name="reboque" node="reboque" />
      </bag>
      <bag name="volumeBag" node="." embed-xml="true" cascade="all">
         <key column="SQ_NFE_NOTA" />
         <one-to-many entity-name="vol" node="vol" />
      </bag>
   </component>
</class>


The mapping to the "reboque" and "vol" entities is trivial and I am not posting here.

As you can see from the mapping above, the bags have a node attribute set to ".", and each bag´s <one-to-many> have it´s own node attribute set to the actual XML node name.

The point is: Hibernate does not let me have the two bags (or sets or whatever collection) with the same parent! It gives me the exception:
Code:
org.hibernate.HibernateException: Found shared references to a collection: Example.transp.vol.NFeVolume


If I comment any of the <bag> tags, it works wonderfully.

I already tried a number of workarounds. Examples:
- If I put the actual node on the "node" attribute of the <bag>, it does not gives any error but do not persist anything on the database table ether.
- Changing the "node" attribute of the two tags (<bag> and <one-to-many>) to various "should work" XPATH values don´t work. Only the combination on the example mapping above works.

Anyone knows if this is a limitation or a bug on the Hibernate implementation? I already tried a number of versions and jars of Hibernate.

Please, help! :)


Top
 Profile  
 
 Post subject: Re: Mapping XML Nodes - possible limitation? With simple example
PostPosted: Wed Jun 09, 2010 8:55 am 
Newbie

Joined: Tue Jun 08, 2010 3:08 pm
Posts: 5
Location: Rio de Janeiro, Brasil
Hi! Anybody can help?
I searched the forum, but it´s difficult having the words "XML" and "Mapping" forbidden when you want to know about ... XML Mapping!

:)


Top
 Profile  
 
 Post subject: Re: Mapping XML Nodes - possible limitation? With simple example
PostPosted: Thu Jun 10, 2010 1:41 pm 
Newbie

Joined: Tue Jun 08, 2010 3:08 pm
Posts: 5
Location: Rio de Janeiro, Brasil
I found another post here in this forum, with the same exact problem.

I resolved the problem modifying Hibernate code. The point is, on my scenario, I only have to map the XML file to the database - never the opposite. I don´t know if I broke it or not.

My solution was modify two classes of the original Hibernate code. The classes are listed below, with the addiction of a helper class to hold the common code. The mapping now is like this:
Code:
         <bag name="SomeName" node="theCollectionNode" embed-xml="true" cascade="all">
            <key column="THE_FK_COLUMN" />
            <one-to-many entity-name="SomeEntityName" />
         </bag>

The difference is that the <bag> node is the actual collectionNode and the one-to-many does not need the node attribute to be set. Works like a charm!

The classes:
Code:
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA  02110-1301  USA
*
*/
package org.hibernate.collection;

import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Element;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.HibernateXMLFix;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.type.Type;
import org.hibernate.util.CollectionHelper;

/**
* A persistent wrapper for an XML element
*
* @author Gavin King
*/
public class PersistentElementHolder extends AbstractPersistentCollection {
   protected Element element;
   
   public PersistentElementHolder(SessionImplementor session, Element element) {
      super(session);
      this.element = element;
      setInitialized();
   }

   public Serializable getSnapshot(CollectionPersister persister)
   throws HibernateException {
      
      final Type elementType = persister.getElementType();
      List elements = HibernateXMLFix.getElementList(element, persister.getElementNodeName());
      
      ArrayList snapshot = new ArrayList( elements.size() );
      for ( int i=0; i<elements.size(); i++ ) {
         Element elem = (Element) elements.get(i);
         Object value = elementType.fromXMLNode( elem, persister.getFactory() );
         Object copy = elementType.deepCopy(value , getSession().getEntityMode(), persister.getFactory() );
         snapshot.add(copy);
      }
      return snapshot;
      
   }

   public Collection getOrphans(Serializable snapshot, String entityName)
   throws HibernateException {
      //orphan delete not supported for EntityMode.DOM4J
      return CollectionHelper.EMPTY_COLLECTION;
   }

   public PersistentElementHolder(SessionImplementor session, CollectionPersister persister, Serializable key)
   throws HibernateException {
      super(session);
      Element owner = (Element) session.getPersistenceContext().getCollectionOwner(key, persister);
      if (owner==null) throw new AssertionFailure("null owner");
      //element = XMLHelper.generateDom4jElement( persister.getNodeName() );
      final String nodeName = persister.getNodeName();
      if ( ".".equals(nodeName) ) {
         element = owner;
      }
      else {
         element = owner.element( nodeName );
         if (element==null) element = owner.addElement( nodeName );
      }
   }

   public boolean isWrapper(Object collection) {
      return element==collection;
   }

   public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
      Type elementType = persister.getElementType();
      
      ArrayList snapshot = (ArrayList) getSnapshot();
      List elements = HibernateXMLFix.getElementList(element, persister.getElementNodeName());
      if ( snapshot.size()!= elements.size() ) return false;
      for ( int i=0; i<snapshot.size(); i++ ) {
         Object old = snapshot.get(i);
         Element elem = (Element) elements.get(i);
         Object current = elementType.fromXMLNode( elem, persister.getFactory() );
         if ( elementType.isDirty( old, current, getSession() ) ) return false;
      }
      return true;
   }

   public boolean isSnapshotEmpty(Serializable snapshot) {
      return ( (Collection) snapshot ).isEmpty();
   }
   
   public boolean empty() {
      return !element.elementIterator().hasNext();
   }

   public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
   throws HibernateException, SQLException {
      Object object = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
      final Type elementType = persister.getElementType();
      Element subelement = element.addElement( persister.getElementNodeName() );
      elementType.setToXMLNode( subelement, object, persister.getFactory() );
      return object;
   }

   public Iterator entries(CollectionPersister persister) {
      
      final Type elementType = persister.getElementType();
      List elements =  HibernateXMLFix.getElementList(element, persister.getElementNodeName());
      int length = elements.size();
      List result = new ArrayList(length);
      for ( int i=0; i<length; i++ ) {
         Element elem = (Element) elements.get(i);
         Object object = elementType.fromXMLNode( elem, persister.getFactory() );
         result.add(object);
      }
      return result.iterator();
   }

   public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {}

   public boolean isDirectlyAccessible() {
      return true;
   }

   public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
   throws HibernateException {
      
      Type elementType = persister.getElementType();
      Serializable[] cached = (Serializable[]) disassembled;
      for ( int i=0; i<cached.length; i++ ) {
         Object object = elementType.assemble( cached[i], getSession(), owner );
         Element subelement = element.addElement( persister.getElementNodeName() );
         elementType.setToXMLNode( subelement, object, persister.getFactory() );
      }
      
   }

   public Serializable disassemble(CollectionPersister persister) throws HibernateException {
      
      Type elementType = persister.getElementType();
      List elements =  HibernateXMLFix.getElementList(element, persister.getElementNodeName());
      int length = elements.size();
      Serializable[] result = new Serializable[length];
      for ( int i=0; i<length; i++ ) {
         Element elem = (Element) elements.get(i);
         Object object = elementType.fromXMLNode( elem, persister.getFactory() );
         result[i] = elementType.disassemble( object, getSession(), null );
      }
      return result;
   }

   public Object getValue() {
      return element;
   }

   public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula)
   throws HibernateException {
      
      Type elementType = persister.getElementType();
      ArrayList snapshot = (ArrayList) getSnapshot();
      List elements = HibernateXMLFix.getElementList(element, persister.getElementNodeName());
      ArrayList result = new ArrayList();
      for ( int i=0; i<snapshot.size(); i++ ) {
         Object old = snapshot.get(i);
         if ( i >= elements.size() ) {
            result.add(old);
         }
         else {
            Element elem = (Element) elements.get(i);
            Object object = elementType.fromXMLNode( elem, persister.getFactory() );
            if ( elementType.isDirty( old, object, getSession() ) ) result.add(old);
         }
      }
      return result.iterator();
      
   }

   public boolean needsInserting(Object entry, int i, Type elementType)
   throws HibernateException {
      ArrayList snapshot = (ArrayList) getSnapshot();
      return i>=snapshot.size() || elementType.isDirty( snapshot.get(i), entry, getSession() );
   }

   public boolean needsUpdating(Object entry, int i, Type elementType)
   throws HibernateException {
      return false;
   }

   public Object getIndex(Object entry, int i, CollectionPersister persister) {
      throw new UnsupportedOperationException();
   }

   public Object getElement(Object entry) {
      return entry;
   }

   public Object getSnapshotElement(Object entry, int i) {
      throw new UnsupportedOperationException();
   }

   public boolean entryExists(Object entry, int i) {
      return entry!=null;
   }

}


Code:
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA  02110-1301  USA
*
*/
package org.hibernate.type;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Element;
import org.dom4j.Node;

import org.hibernate.EntityMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.HibernateXMLFix;
import org.hibernate.MappingException;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.engine.CollectionKey;
import org.hibernate.engine.EntityEntry;
import org.hibernate.engine.Mapping;
import org.hibernate.engine.PersistenceContext;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.util.ArrayHelper;
import org.hibernate.util.MarkerObject;

/**
* A type that handles Hibernate <tt>PersistentCollection</tt>s (including arrays).
*
* @author Gavin King
*/
public abstract class CollectionType extends AbstractType implements AssociationType {

   private static final Object NOT_NULL_COLLECTION = new MarkerObject( "NOT NULL COLLECTION" );
   public static final Object UNFETCHED_COLLECTION = new MarkerObject( "UNFETCHED COLLECTION" );

   private final String role;
   private final String foreignKeyPropertyName;
   private final boolean isEmbeddedInXML;

   public CollectionType(String role, String foreignKeyPropertyName, boolean isEmbeddedInXML) {
      this.role = role;
      this.foreignKeyPropertyName = foreignKeyPropertyName;
      this.isEmbeddedInXML = isEmbeddedInXML;
   }
   
   public boolean isEmbeddedInXML() {
      return isEmbeddedInXML;
   }

   public String getRole() {
      return role;
   }

   public Object indexOf(Object collection, Object element) {
      throw new UnsupportedOperationException( "generic collections don't have indexes" );
   }

   public boolean contains(Object collection, Object childObject, SessionImplementor session) {
      // we do not have to worry about queued additions to uninitialized
      // collections, since they can only occur for inverse collections!
      Iterator elems = getElementsIterator( collection, session );
      while ( elems.hasNext() ) {
         Object element = elems.next();
         // worrying about proxies is perhaps a little bit of overkill here...
         if ( element instanceof HibernateProxy ) {
            LazyInitializer li = ( (HibernateProxy) element ).getHibernateLazyInitializer();
            if ( !li.isUninitialized() ) element = li.getImplementation();
         }
         if ( element == childObject ) return true;
      }
      return false;
   }

   public boolean isCollectionType() {
      return true;
   }

   public final boolean isEqual(Object x, Object y, EntityMode entityMode) {
      return x == y
         || ( x instanceof PersistentCollection && ( (PersistentCollection) x ).isWrapper( y ) )
         || ( y instanceof PersistentCollection && ( (PersistentCollection) y ).isWrapper( x ) );
   }

   public int compare(Object x, Object y, EntityMode entityMode) {
      return 0; // collections cannot be compared
   }

   public int getHashCode(Object x, EntityMode entityMode) {
      throw new UnsupportedOperationException( "cannot doAfterTransactionCompletion lookups on collections" );
   }

   /**
    * Instantiate an uninitialized collection wrapper or holder. Callers MUST add the holder to the
    * persistence context!
    *
    * @param session The session from which the request is originating.
    * @param persister The underlying collection persister (metadata)
    * @param key The owner key.
    * @return The instantiated collection.
    */
   public abstract PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key);

   public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner) throws SQLException {
      return nullSafeGet( rs, new String[] { name }, session, owner );
   }

   public Object nullSafeGet(ResultSet rs, String[] name, SessionImplementor session, Object owner)
         throws HibernateException, SQLException {
      return resolve( null, session, owner );
   }

   public final void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable,
         SessionImplementor session) throws HibernateException, SQLException {
      //NOOP
   }

   public void nullSafeSet(PreparedStatement st, Object value, int index,
         SessionImplementor session) throws HibernateException, SQLException {
   }

   public int[] sqlTypes(Mapping session) throws MappingException {
      return ArrayHelper.EMPTY_INT_ARRAY;
   }

   public int getColumnSpan(Mapping session) throws MappingException {
      return 0;
   }

   public String toLoggableString(Object value, SessionFactoryImplementor factory)
         throws HibernateException {
      if ( value == null ) {
         return "null";
      }
      else if ( !Hibernate.isInitialized( value ) ) {
         return "<uninitialized>";
      }
      else {
         return renderLoggableString( value, factory );
      }
   }

   protected String renderLoggableString(Object value, SessionFactoryImplementor factory)
         throws HibernateException {
      if ( Element.class.isInstance( value ) ) {
         // for DOM4J "collections" only
         // TODO: it would be better if this was done at the higher level by Printer
         return ( ( Element ) value ).asXML();
      }
      else {
         List list = new ArrayList();
         Type elemType = getElementType( factory );
         Iterator iter = getElementsIterator( value );
         while ( iter.hasNext() ) {
            list.add( elemType.toLoggableString( iter.next(), factory ) );
         }
         return list.toString();
      }
   }

   public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory)
         throws HibernateException {
      return value;
   }

   public String getName() {
      return getReturnedClass().getName() + '(' + getRole() + ')';
   }

   /**
    * Get an iterator over the element set of the collection, which may not yet be wrapped
    *
    * @param collection The collection to be iterated
    * @param session The session from which the request is originating.
    * @return The iterator.
    */
   public Iterator getElementsIterator(Object collection, SessionImplementor session) {
      if ( session.getEntityMode()==EntityMode.DOM4J ) {
         final SessionFactoryImplementor factory = session.getFactory();
         final CollectionPersister persister = factory.getCollectionPersister( getRole() );
         final Type elementType = persister.getElementType();

         List elements = HibernateXMLFix.getElementList((Element) collection, persister.getElementNodeName());
         
         ArrayList results = new ArrayList();
         for ( int i=0; i<elements.size(); i++ ) {
            Element value = (Element) elements.get(i);
            results.add( elementType.fromXMLNode( value, factory ) );
         }
         return results.iterator();
      }
      else {
         return getElementsIterator(collection);
      }
   }

   /**
    * Get an iterator over the element set of the collection in POJO mode
    *
    * @param collection The collection to be iterated
    * @return The iterator.
    */
   protected Iterator getElementsIterator(Object collection) {
      return ( (Collection) collection ).iterator();
   }

   public boolean isMutable() {
      return false;
   }

   public Serializable disassemble(Object value, SessionImplementor session, Object owner)
         throws HibernateException {
      //remember the uk value
      
      //This solution would allow us to eliminate the owner arg to disassemble(), but
      //what if the collection was null, and then later had elements added? seems unsafe
      //session.getPersistenceContext().getCollectionEntry( (PersistentCollection) value ).getKey();
      
      final Serializable key = getKeyOfOwner(owner, session);
      if (key==null) {
         return null;
      }
      else {
         return getPersister(session)
               .getKeyType()
               .disassemble( key, session, owner );
      }
   }

   public Object assemble(Serializable cached, SessionImplementor session, Object owner)
         throws HibernateException {
      //we must use the "remembered" uk value, since it is
      //not available from the EntityEntry during assembly
      if (cached==null) {
         return null;
      }
      else {
         final Serializable key = (Serializable) getPersister(session)
               .getKeyType()
               .assemble( cached, session, owner);
         return resolveKey( key, session, owner );
      }
   }

   /**
    * Is the owning entity versioned?
    *
    * @param session The session from which the request is originating.
    * @return True if the collection owner is versioned; false otherwise.
    * @throws org.hibernate.MappingException Indicates our persister could not be located.
    */
   private boolean isOwnerVersioned(SessionImplementor session) throws MappingException {
      return getPersister( session ).getOwnerEntityPersister().isVersioned();
   }

   /**
    * Get our underlying collection persister (using the session to access the
    * factory).
    *
    * @param session The session from which the request is originating.
    * @return The underlying collection persister
    */
   private CollectionPersister getPersister(SessionImplementor session) {
      return session.getFactory().getCollectionPersister( role );
   }

   public boolean isDirty(Object old, Object current, SessionImplementor session)
         throws HibernateException {

      // collections don't dirty an unversioned parent entity

      // TODO: I don't really like this implementation; it would be better if
      // this was handled by searchForDirtyCollections()
      return isOwnerVersioned( session ) && super.isDirty( old, current, session );
      // return false;

   }

   public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session)
         throws HibernateException {
      return isDirty(old, current, session);
   }

   /**
    * Wrap the naked collection instance in a wrapper, or instantiate a
    * holder. Callers <b>MUST</b> add the holder to the persistence context!
    *
    * @param session The session from which the request is originating.
    * @param collection The bare collection to be wrapped.
    * @return The wrapped collection.
    */
   public abstract PersistentCollection wrap(SessionImplementor session, Object collection);

   /**
    * Note: return true because this type is castable to <tt>AssociationType</tt>. Not because
    * all collections are associations.
    */
   public boolean isAssociationType() {
      return true;
   }

   public ForeignKeyDirection getForeignKeyDirection() {
      return ForeignKeyDirection.FOREIGN_KEY_TO_PARENT;
   }

   /**
    * Get the key value from the owning entity instance, usually the identifier, but might be some
    * other unique key, in the case of property-ref
    *
    * @param owner The collection owner
    * @param session The session from which the request is originating.
    * @return The collection owner's key
    */
   public Serializable getKeyOfOwner(Object owner, SessionImplementor session) {
      
      EntityEntry entityEntry = session.getPersistenceContext().getEntry( owner );
      if ( entityEntry == null ) return null; // This just handles a particular case of component
                             // projection, perhaps get rid of it and throw an exception
      
      if ( foreignKeyPropertyName == null ) {
         return entityEntry.getId();
      }
      else {
         // TODO: at the point where we are resolving collection references, we don't
         // know if the uk value has been resolved (depends if it was earlier or
         // later in the mapping document) - now, we could try and use e.getStatus()
         // to decide to semiResolve(), trouble is that initializeEntity() reuses
         // the same array for resolved and hydrated values
         Object id;
         if ( entityEntry.getLoadedState() != null ) {
            id = entityEntry.getLoadedValue( foreignKeyPropertyName );
         }
         else {
            id = entityEntry.getPersister().getPropertyValue( owner, foreignKeyPropertyName, session.getEntityMode() );
         }

         // NOTE VERY HACKISH WORKAROUND!!
         // TODO: Fix this so it will work for non-POJO entity mode
         Type keyType = getPersister( session ).getKeyType();
         if ( !keyType.getReturnedClass().isInstance( id ) ) {
            id = (Serializable) keyType.semiResolve(
                  entityEntry.getLoadedValue( foreignKeyPropertyName ),
                  session,
                  owner
               );
         }

         return (Serializable) id;
      }
   }

   /**
    * Get the id value from the owning entity key, usually the same as the key, but might be some
    * other property, in the case of property-ref
    *
    * @param key The collection owner key
    * @param session The session from which the request is originating.
    * @return The collection owner's id, if it can be obtained from the key;
    * otherwise, null is returned
    */
   public Serializable getIdOfOwnerOrNull(Serializable key, SessionImplementor session) {
      Serializable ownerId = null;
      if ( foreignKeyPropertyName == null ) {
         ownerId = key;
      }
      else {
         Type keyType = getPersister( session ).getKeyType();
         EntityPersister ownerPersister = getPersister( session ).getOwnerEntityPersister();
         // TODO: Fix this so it will work for non-POJO entity mode
         Class ownerMappedClass = ownerPersister.getMappedClass( session.getEntityMode() );
         if ( ownerMappedClass.isAssignableFrom( keyType.getReturnedClass() ) &&
               keyType.getReturnedClass().isInstance( key ) ) {
            // the key is the owning entity itself, so get the ID from the key
            ownerId = ownerPersister.getIdentifier( key, session );
         }
         else {
            // TODO: check if key contains the owner ID
         }
      }
      return ownerId;
   }

   public Object hydrate(ResultSet rs, String[] name, SessionImplementor session, Object owner) {
      // can't just return null here, since that would
      // cause an owning component to become null
      return NOT_NULL_COLLECTION;
   }

   public Object resolve(Object value, SessionImplementor session, Object owner)
         throws HibernateException {
      
      return resolveKey( getKeyOfOwner( owner, session ), session, owner );
   }
   
   private Object resolveKey(Serializable key, SessionImplementor session, Object owner) {
      // if (key==null) throw new AssertionFailure("owner identifier unknown when re-assembling
      // collection reference");
      return key == null ? null : // TODO: can this case really occur??
         getCollection( key, session, owner );
   }

   public Object semiResolve(Object value, SessionImplementor session, Object owner)
         throws HibernateException {
      throw new UnsupportedOperationException(
         "collection mappings may not form part of a property-ref" );
   }

   public boolean isArrayType() {
      return false;
   }

   public boolean useLHSPrimaryKey() {
      return foreignKeyPropertyName == null;
   }

   public String getRHSUniqueKeyPropertyName() {
      return null;
   }

   public Joinable getAssociatedJoinable(SessionFactoryImplementor factory)
         throws MappingException {
      return (Joinable) factory.getCollectionPersister( role );
   }

   public boolean isModified(Object old, Object current, boolean[] checkable, SessionImplementor session) throws HibernateException {
      return false;
   }

   public String getAssociatedEntityName(SessionFactoryImplementor factory)
         throws MappingException {
      try {
         
         QueryableCollection collectionPersister = (QueryableCollection) factory
               .getCollectionPersister( role );
         
         if ( !collectionPersister.getElementType().isEntityType() ) {
            throw new MappingException(
                  "collection was not an association: " +
                  collectionPersister.getRole()
               );
         }
         
         return collectionPersister.getElementPersister().getEntityName();
         
      }
      catch (ClassCastException cce) {
         throw new MappingException( "collection role is not queryable " + role );
      }
   }

   /**
    * Replace the elements of a collection with the elements of another collection.
    *
    * @param original The 'source' of the replacement elements (where we copy from)
    * @param target The target of the replacement elements (where we copy to)
    * @param owner The owner of the collection being merged
    * @param copyCache The map of elements already replaced.
    * @param session The session from which the merge event originated.
    * @return The merged collection.
    */
   public Object replaceElements(
         Object original,
         Object target,
         Object owner,
         Map copyCache,
         SessionImplementor session) {
      // TODO: does not work for EntityMode.DOM4J yet!
      java.util.Collection result = ( java.util.Collection ) target;
      result.clear();

      // copy elements into newly empty target collection
      Type elemType = getElementType( session.getFactory() );
      Iterator iter = ( (java.util.Collection) original ).iterator();
      while ( iter.hasNext() ) {
         result.add( elemType.replace( iter.next(), null, session, owner, copyCache ) );
      }

      // if the original is a PersistentCollection, and that original
      // was not flagged as dirty, then reset the target's dirty flag
      // here after the copy operation.
      // </p>
      // One thing to be careful of here is a "bare" original collection
      // in which case we should never ever ever reset the dirty flag
      // on the target because we simply do not know...
      if ( original instanceof PersistentCollection ) {
         if ( result instanceof PersistentCollection ) {
            if ( ! ( ( PersistentCollection ) original ).isDirty() ) {
               ( ( PersistentCollection ) result ).clearDirty();
            }
         }
      }

      return result;
   }

   /**
    * Instantiate a new "underlying" collection exhibiting the same capacity
    * charactersitcs and the passed "original".
    *
    * @param original The original collection.
    * @return The newly instantiated collection.
    */
   protected Object instantiateResult(Object original) {
      // by default just use an unanticipated capacity since we don't
      // know how to extract the capacity to use from original here...
      return instantiate( -1 );
   }

   /**
    * Instantiate an empty instance of the "underlying" collection (not a wrapper),
    * but with the given anticipated size (i.e. accounting for initial capacity
    * and perhaps load factor).
    *
    * @param anticipatedSize The anticipated size of the instaniated collection
    * after we are done populating it.
    * @return A newly instantiated collection to be wrapped.
    */
   public abstract Object instantiate(int anticipatedSize);

   /**
    * {@inheritDoc}
    */
   public Object replace(
         final Object original,
         final Object target,
         final SessionImplementor session,
         final Object owner,
         final Map copyCache) throws HibernateException {
      if ( original == null ) {
         return null;
      }
      if ( !Hibernate.isInitialized( original ) ) {
         return target;
      }

      // for a null target, or a target which is the same as the original, we
      // need to put the merged elements in a new collection
      Object result = target == null || target == original ? instantiateResult( original ) : target;
      
      //for arrays, replaceElements() may return a different reference, since
      //the array length might not match
      result = replaceElements( original, result, owner, copyCache, session );

      if ( original == target ) {
         // get the elements back into the target making sure to handle dirty flag
         boolean wasClean = PersistentCollection.class.isInstance( target ) && !( ( PersistentCollection ) target ).isDirty();
         //TODO: this is a little inefficient, don't need to do a whole
         //      deep replaceElements() call
         replaceElements( result, target, owner, copyCache, session );
         if ( wasClean ) {
            ( ( PersistentCollection ) target ).clearDirty();
         }
         result = target;
      }

      return result;
   }

   /**
    * Get the Hibernate type of the collection elements
    *
    * @param factory The session factory.
    * @return The type of the collection elements
    * @throws MappingException Indicates the underlying persister could not be located.
    */
   public final Type getElementType(SessionFactoryImplementor factory) throws MappingException {
      return factory.getCollectionPersister( getRole() ).getElementType();
   }

   public String toString() {
      return getClass().getName() + '(' + getRole() + ')';
   }

   public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
         throws MappingException {
      return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters );
   }

   /**
    * instantiate a collection wrapper (called when loading an object)
    *
    * @param key The collection owner key
    * @param session The session from which the request is originating.
    * @param owner The collection owner
    * @return The collection
    */
   public Object getCollection(Serializable key, SessionImplementor session, Object owner) {

      CollectionPersister persister = getPersister( session );
      final PersistenceContext persistenceContext = session.getPersistenceContext();
      final EntityMode entityMode = session.getEntityMode();

      if (entityMode==EntityMode.DOM4J && !isEmbeddedInXML) {
         return UNFETCHED_COLLECTION;
      }
      
      // check if collection is currently being loaded
      PersistentCollection collection = persistenceContext.getLoadContexts().locateLoadingCollection( persister, key );
      
      if ( collection == null ) {
         
         // check if it is already completely loaded, but unowned
         collection = persistenceContext.useUnownedCollection( new CollectionKey(persister, key, entityMode) );
         
         if ( collection == null ) {
            // create a new collection wrapper, to be initialized later
            collection = instantiate( session, persister, key );
            collection.setOwner(owner);
   
            persistenceContext.addUninitializedCollection( persister, collection, key );
   
            // some collections are not lazy:
            if ( initializeImmediately( entityMode ) ) {
               session.initializeCollection( collection, false );
            }
            else if ( !persister.isLazy() ) {
               persistenceContext.addNonLazyCollection( collection );
            }
   
            if ( hasHolder( entityMode ) ) {
               session.getPersistenceContext().addCollectionHolder( collection );
            }
            
         }
         
      }
      
      collection.setOwner(owner);

      return collection.getValue();
   }

   public boolean hasHolder(EntityMode entityMode) {
      return entityMode == EntityMode.DOM4J;
   }

   protected boolean initializeImmediately(EntityMode entityMode) {
      return entityMode == EntityMode.DOM4J;
   }

   public String getLHSPropertyName() {
      return foreignKeyPropertyName;
   }

   public boolean isXMLElement() {
      return true;
   }

   public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
      return xml;
   }

   public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory)
   throws HibernateException {
      if ( !isEmbeddedInXML ) {
         node.detach();
      }
      else {
         replaceNode( node, (Element) value );
      }
   }
   
   /**
    * We always need to dirty check the collection because we sometimes
    * need to incremement version number of owner and also because of
    * how assemble/disassemble is implemented for uks
    */
   public boolean isAlwaysDirtyChecked() {
      return true;
   }

   public boolean[] toColumnNullness(Object value, Mapping mapping) {
      return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
   }
}


Code:
package org.hibernate;

import java.util.List;

import org.dom4j.Element;

public class HibernateXMLFix {
   
   @SuppressWarnings("unchecked")
   public static final List getElementList(Element element, String elementNodeName) {
      
      List elements = element.elements( elementNodeName );

      //this is the new code
      if (elements == null || elements.isEmpty()) {
         if (elementNodeName.equals(".") || element.getName().equals(elementNodeName)) {
            elements = element.elements();
         }
      }
      
      return elements;
      
   }
   
}


Top
 Profile  
 
 Post subject: Re: Mapping XML Nodes - possible limitation? With simple example
PostPosted: Wed Jun 30, 2010 2:42 pm 
Newbie

Joined: Tue Jun 08, 2010 3:08 pm
Posts: 5
Location: Rio de Janeiro, Brasil
Hi guys.

My solution DID NOT WORK as intended. Please do not consider it a solution.
There is actually a bug opened on Hibernate´s Jira:

http://opensource.atlassian.com/project ... e/HHH-2656

registered since 06/Jun/07!!!!

Seems like they do not care about it at all. :(


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