-->
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: DOM4J proxy Elements do not load?
PostPosted: Thu Mar 16, 2006 6:08 pm 
Newbie

Joined: Thu Mar 16, 2006 4:48 pm
Posts: 5
Hibernate version: v3.1 stable and today's SVN

Mapping documents:
Code:
<hibernate-mapping>
    <class name="uk.co.spar.db.Organisation" table="ORGANISATION" schema="SPARDBA">
        <id name="id" type="int">
            <column name="ORG_ID" precision="10" scale="0" />
            <generator class="sequence">
            <param name="sequence">sparidentity</param>
         </generator>
        </id>
        <many-to-one name="organisationType" class="uk.co.spar.db.OrganisationType" fetch="select" >
            <column name="ORGANISATION_TYPE_ID" precision="10" scale="0" not-null="true" />
        </many-to-one>
        <many-to-one name="postAddress" class="uk.co.spar.db.PostAddress" fetch="select">
            <column name="ADDR_ID" precision="10" scale="0" />
        </many-to-one>
        <property name="name" type="string">
            <column name="ORG_NAME" length="40" not-null="true" />
        </property>
        <property name="lastUpdated" type="date">
            <column name="ORG_LAST_UPD" length="7" not-null="true" />
        </property>
        <property name="accountCode" type="string">
            <column name="ORG_ACCOUNT_CODE" length="10" />
        </property>
        <property name="nameUpper" type="string">
            <column name="ORG_NAME_UPPER" length="40" not-null="true" />
        </property>
        <property name="orgConstant" type="string">
            <column name="ORG_CONSTANT" length="40" />
        </property>
    </class>
</hibernate-mapping>

Code:
<hibernate-mapping>
    <class name="uk.co.spar.db.OrganisationType" table="ORGANISATION_TYPE" schema="SPARDBA">
        <id name="id" type="int">
            <column name="ORGANISATION_TYPE_ID" precision="10" scale="0" />
            <generator class="assigned" />
        </id>
        <property name="description" type="string">
            <column name="ORGANISATION_TYPE_DESC" length="40" not-null="true" />
        </property>
        <property name="organisationTypeConstant" type="string">
            <column name="ORGANISATION_TYPE_CONSTANT" length="40" />
        </property>
    </class>
</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():
Code:
         Session dom4jSession = session.getSession(EntityMode.DOM4J);
         Transaction tx = session.beginTransaction();

         List results = dom4jSession.createQuery("from Organisation o where o.id=:id")
            .setInteger("id", 17000)
            .list();
         Element root = (Element)results.get(0);
         Element orgType = root.element("organisationType");
         if (orgType != null) {
            Attribute attr = orgType.attribute("id");
            if (attr != null)
               System.out.println(attr.getText());
         }
         tx.commit();


Name and version of the database you are using:
RDBMS: Oracle, version: Oracle Database 10g Release 10.1.0.2.0 - Production
JDBC driver: Oracle JDBC driver, version: 10.1.0.2.0

Description of problem:

I'm a new Hibernate user so my apologies if I've missed something, but I've read everything I can find and I just can't come up with an answer.

I'm trying to use the XML mapping for Hibernate. In the above example, I have a table called ORGANISATION which has a foreign key onto ORGANISATION_TYPE.

I can instantiate and load Organisation (via dom4jSession.createQuery or by dom4jSession.load) and everything is there *except* the child element "organisationType" never loads properly. I say "properly" because the console shows the correct SQL to load the OrganisationType object, and in the debugger I can work down through the proxy to see that the real Element is there with the correct values.

In the above example, the debugger shows that the variable orgType is of class ElementWrapper and that orgType.element.li.target is my correctly populated Element with the expected attributes "id", "description", and "organisationTypeConstant", but the code:

Code:
            Attribute attr = orgType.attribute("id");


returns null. Worse, if I change the mapping file for Organsiation's organisationType property to add the node attribute, EG:

Code:
<many-to-one name="organisationType" class="uk.co.spar.db.OrganisationType" fetch="select" node="organisationType/@id">

then the tx.commit() raises the exception:

Code:
org.hibernate.PropertyValueException: not-null property references a null or transient value: uk.co.spar.db.Organisation.organisationType

I've debugged it and this is because Hibernate sees that organisationType.id is null - which is technically correct given that the element has not properly loaded.

Again, sorry if this is something I should have picked up on but I'm really stuck and any help would be much appreciated - and if it is a bug, give me some pointers and I'll try and track it down but this is only my first week with Hibernate!

Regards,
John


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 16, 2006 6:21 pm 
Newbie

Joined: Thu Mar 16, 2006 4:48 pm
Posts: 5
Oops, there was a typo there: in the Java example, the orgType local variable is actually of type org.dom4j.tree.DefaultElement and it's member field "content" is of type org.hibernate.tuple.ElementWrapper...it's orgType.content.element.li.target that contains the correctly populated Element.

John


Top
 Profile  
 
 Post subject: I think I've narrowed it down quite a bit...
PostPosted: Fri Mar 17, 2006 2:11 am 
Newbie

Joined: Thu Mar 16, 2006 4:48 pm
Posts: 5
This problem is now a lot simplied (I did get a little confused when writing the report, my bad). Please read on:

I started lookin at the differences between node="organisationType" and node="organisationType/@id" on the many to one, because it's this that breaks the tx.commit() call. First though, I wrote a little dumpXml method - it's pretty boring, just a knocked together pretty printer for XML:

Code:
   public Element testFunc(Session session) {
      Session dom4jSession = session.getSession(EntityMode.DOM4J);
      Transaction tx = session.beginTransaction();

      List results = dom4jSession.createQuery("from Organisation o where o.id=:id")
         .setInteger("id", 17000)
         .list();
      
      Element root = (Element)results.get(0);
      dumpXml(0, 3, root);
      
      tx.commit();
      return root;
   }

   public void dumpXml(int depth, int maxDepth, Element element) {
      PrintStream out = System.out;
      String prefix = "";
      for (int i=0; i < depth; i++)
         prefix += "  ";
      String endTag = prefix + "</" + element.getName() + ">";
      out.print(prefix + "<" + element.getName());
      List<Attribute> attrs = element.attributes();
      if (attrs != null)
         for (Attribute attr : attrs) {
            out.print(" ");
            out.print(attr.getName());
            out.print('=');
            out.print(attr.getValue());
         }
      out.println('>');
      String text = element.getTextTrim();
      if (text != null && text.length() > 0) {
         out.print("  " + prefix + "<![CDATA[");
         out.println(text + "]]>");
      }
      List<Element> children = element.elements();
      prefix += "  ";
      if (children != null && depth < maxDepth) {
         for (Element child : children) {
            dumpXml(depth + 1, maxDepth, child);
         }
      }
      out.println(endTag);
   }


Without using "node" attribute on <many-to-one> at all, I get this:

Code:
<Organisation>
  <id>
    <![CDATA[17000]]>
  </id>
  <organisationType>
    <id>
      <![CDATA[14]]>
    </id>
    <description>
      <![CDATA[Retailer]]>
    </description>
    <organisationTypeConstant>
      <![CDATA[orgtypRetailer]]>
    </organisationTypeConstant>
  </organisationType>
  <name>
    <![CDATA[SPAR Corfe Mullen]]>
  </name>
  <lastUpdated>
    <![CDATA[25 January 2006]]>
  </lastUpdated>
  <nameUpper>
    <![CDATA[SPAR CORFE MULLEN]]>
  </nameUpper>
</Organisation>


So far so good, everything as expected. So I added the node attribute (as per docs)

Code:
<many-to-one name="organisationType" class="uk.co.spar.db.OrganisationType" fetch="select" node="org-type/@id">


and re run the code. I get:
Code:
<Organisation>
  <id>
    <![CDATA[17000]]>
  </id>
  <org-type>
    <id>
      <id>
        <![CDATA[14]]>
      </id>
      <description>
        <![CDATA[Retailer]]>
      </description>
      <organisationTypeConstant>
        <![CDATA[orgtypRetailer]]>
      </organisationTypeConstant>
    </id>
  </org-type>
  <name>
    <![CDATA[SPAR Corfe Mullen]]>
  </name>
  <lastUpdated>
    <![CDATA[25 January 2006]]>
  </lastUpdated>
  <nameUpper>
    <![CDATA[SPAR CORFE MULLEN]]>
  </nameUpper>
</Organisation>


It seems that node="org-type/@id" causes the element to be called "org-type" but creates a sub-element called "id" (instead of an attribute), and in that "id" element is the contents of the correct element (ie "id", "description", etc elements).

If I change the "node" attribute to just "org-type", then the node is built correctly using the org-type tag name instead of organisationType, it's only when you add the "/@id" (as per the docs) that it falls appart and interprets this as an attempt to create a sub element.

One final test - changing the node to "org-type/my-id" causes this:
Code:
<Organisation>
  <id>
    <![CDATA[17000]]>
  </id>
  <org-type>
    <y-id>
      <id>
        <![CDATA[14]]>
      </id>
      <description>
        <![CDATA[Retailer]]>
      </description>
      <organisationTypeConstant>
        <![CDATA[orgtypRetailer]]>
      </organisationTypeConstant>
    </y-id>
  </org-type>
  <name>
    <![CDATA[SPAR Corfe Mullen]]>
  </name>
  <lastUpdated>
    <![CDATA[25 January 2006]]>
  </lastUpdated>
  <nameUpper>
    <![CDATA[SPAR CORFE MULLEN]]>
  </nameUpper>
</Organisation>


Note that it still creates a sub-element, although this time you'd expect it to because you've specified so("node='org-type/my-id'") but the first character of "my-id" is missing.

Summary
When using node and embed-xml="true" do not use 'node="sometag/@id", it gives a strange and non-reversable structure.
When embed-xml="false" you can use either 'node="sometag"' or 'node="sometag/@id"' - the first form puts the id in as a child text node, the second as an attribute of the child - but in either case they pass back though tx.commit() successfully.

I'm about to start debugging the part where the value of the node attribute is interpretted - any leg-ups on where to look would be real helpful.

Regards,
John


Top
 Profile  
 
 Post subject: Re: I think I've narrowed it down quite a bit...
PostPosted: Fri Mar 17, 2006 9:51 am 
Newbie

Joined: Thu Mar 16, 2006 4:48 pm
Posts: 5
Having looked at the code, the "bug" is debatable - either it's a problem with what the mapping file permits as a valid value for the "node" attribute, or it is a problem where the generated child Element should have an attribute created to match the node expression (it doesn't, which is what causes the exception during tx.commit()).

(Note that this is only when embed-xml="true" ... embed-xml="false" works as expected)

I would guess that the argument would be that if the child is embedded then the parent has no right in determining how it's contents are defined - doing so implies an inconsistent data format at least, never mind discussions about design purity. The only thing the parent can do is choose the child's tag name, and as it seems redundant to allow the parent to nest elements, neither a "/" or a "@" should never be present in a node attribute when embed-xml="true". This also happens to be the simplest fix! :)

I'll do the fix if someone will give me the go-ahead? I think it's in Dom4jAccessor.getSetter...

IMHO this leads onto another discussion of whether embed-xml is actually needed at all - if DOM4J and POJOs are a mechanism for exposing the OR data, then embed-xml is really about how that data is serialised into a non-relational format. The biggest failing of embed-xml is that different business functions will require the same object to be embedded (or not), depending on circumstances. IE it's a runtime decision, and not a pre-compiled decision.

What does everyone think?

John


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.