-->
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.  [ 12 posts ] 
Author Message
 Post subject: how to synchronize removal of items from list to db, clarif.
PostPosted: Tue Oct 26, 2004 9:28 am 
Senior
Senior

Joined: Fri Jun 18, 2004 10:17 am
Posts: 140
Hi, I am trying to understand how to synchronize removed items from colletions in detached objects is made to the db.

I have a web-based 5 step wizard that allows updating of a POF object. At the very start, the whole POF object graph is loaded from the db and then is detached.

As the user steps through the wizard, the detached POF is modified. When it comes to step 3, the user can choose to add or remove elements from the line items collection.

At the very last confirmation step, a call to hibernate is made to update the POF. All changes are made to the main POF, many-to-one associates and if items were ADDED to the line items collecton, these are also persisted. I think this all happens on save because of the cascade="all"

My issue is that if the user has removed line items, when update is called, the line item(s) that were removed in the detached object do not get removed from the database.

I have found several places online and in Hibernate in Action that allude to this but I don't think I am totally getting it. I now have a pof.removeLineItem method that sets the LineItem.pof to null to get rid of that end, and I then also remove the item from the lineItems collection.

I have tried cascade="all-delete-orphan" but this gives me an error You may not dereference a collection with cascade="all-delete-orphan"

and this error is caused by trying to load the POF as follows

Code:
Session session = HibernateUtil.getSession();
         Transaction tx = session.beginTransaction();
         
         Query q = session.createQuery(
                "from POF as p " +
                "   left outer join fetch p.lineItems " +
                "where p.id = :pofId"
            );
           
         q.setParameter("pofId", id);
           
            pof  = (POF) q.list().get(0);
           
            if (pof != null) {
               q = session.createQuery(
                  "from Action as a " +
                    "where a.purchaseOrderForm.id = :pofId"
               );
              
               q.setParameter("pofId", id);
              
               pof.setActionHistory(q.list());
            }
                                 
         tx.commit();
         HibernateUtil.closeSession(session);


so overall i am a little confused but on the edge of success if i could just get some tips :)

Hibernate version:
2.1.6

Mapping documents:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping schema="dbPOF.dbo">

    <class name="com.qas.newmedia.intranet.pof.dto.POF"
      table="tbl_POFs">

        <id name="id" type="int" column="pof_id" unsaved-value="0">
           <generator class="identity" />
       </id>
                        
        <property name="created" column="created" type="calendar" not-null="true" />
      <property name="status" column="status" not-null="true" />
      <property name="reference" column="reference" not-null="true" />
      <property name="externalId" column="external_id" not-null="false" />
      <property name="originator" column="originator" not-null="true" />
      <property name="originatorFullName" column="originator_full_name" not-null="true" />
      <property name="department" column="department" not-null="true" />
      <property name="departmentCode" column="department_code" not-null="true" />
      <property name="currencyCode" column="currency_code" not-null="true" />
      
      <property name="taxRate">
         <column name="tax_rate" not-null="true" sql-type="money" />
      </property>
      
      <property name="authoriser" column="authoriser" not-null="true" />
      <property name="authoriserFullName" column="authoriser_full_name" not-null="true" />
      <property name="appliesToMin" column="applies_to_min" type="calendar" not-null="true" />
      <property name="appliesToMax" column="applies_to_max" type="calendar" not-null="true" />

      <property name="netCost">
         <column name="net_cost" not-null="true" sql-type="money" />
      </property>
      
      <property name="discountCost">
         <column name="discount_cost" not-null="true" sql-type="money" />
      </property>
      
      <property name="carriageCost">
         <column name="carriage_cost" not-null="true" sql-type="money" />
      </property>            

      <property name="taxCost">
         <column name="tax_cost" not-null="true" sql-type="money" />
      </property>

      <property name="grossCost">
         <column name="gross_cost" not-null="true" sql-type="money" />
      </property>

      <property name="notes">
         <column name="notes" not-null="false" sql-type="varchar(4000)" />
      </property>

      <many-to-one name="supplier" column="supplier_id" cascade="all" unique="true" />
      <many-to-one name="deliveryAddress" column="delivery_address_id" cascade="all" unique="true" />

      <list name="lineItems" inverse="true" lazy="true" cascade="all">
            <key column="pof_id"/>
         <index column="index_col" />
            <one-to-many class="com.qas.newmedia.intranet.pof.dto.LineItem" /> 
        </list>

      <list name="actionHistory" inverse="true" lazy="true" cascade="all">
            <key column="pof_id"/>
         <index column="index_col" />
            <one-to-many class="com.qas.newmedia.intranet.pof.dto.Action" /> 
        </list>

    </class>

   <class name="com.qas.newmedia.intranet.pof.dto.LineItem"
      table="tbl_LineItems">

        <id name="id" type="int" column="line_item_id" unsaved-value="0">
           <generator class="identity" />
       </id>
      
      <property name="code" column="code" not-null="false" />
      <property name="description" column="description" not-null="true" />
      <property name="function" column="fn" not-null="false" />
      <property name="quantity" column="quantity" type="int" not-null="true" />

      <property name="unitPrice">
         <column name="unit_price" not-null="true" sql-type="money" />
      </property>

      <property name="taxType" column="tax_type" not-null="true" />

      <property name="netCost">
         <column name="net_cost" not-null="true" sql-type="money" />
      </property>

      <property name="notes">
         <column name="notes" not-null="false" sql-type="varchar(4000)" />
      </property>

      <many-to-one name="purchaseOrderForm" column="pof_id" not-null="true" />
      
      <property name="index" type="int" update="true" insert="true" column="index_col" />

   </class>
   
   <class name="com.qas.newmedia.intranet.pof.dto.Supplier"
      table="tbl_Suppliers">

        <id name="id" type="int" column="supplier_id" unsaved-value="0">
           <generator class="identity" />
       </id>

      <property name="code" column="code" not-null="true" />
      <many-to-one name="address" column="address_id" cascade="all" unique="true" />

   </class>

   <class name="com.qas.newmedia.intranet.pof.dto.IntlCompanyDetail"
      table="tbl_IntlCompanyDetails">

        <id name="id" type="int" column="intl_comp_det_id" unsaved-value="0">
           <generator class="identity" />
       </id>
      
      <property name="company" column="company" not-null="false" />
      <property name="contact" column="contact" not-null="false" />
      <property name="telephone" column="telephone" not-null="false" />
      <property name="fax" column="fax" not-null="false" />
      <property name="email" column="email" not-null="false" />
      <property name="web" column="web" not-null="false" />
      <property name="addressLine1" column="address_line_1" not-null="false" />
      <property name="addressLine2" column="address_line_2" not-null="false" />
      <property name="addressLine3" column="address_line_3" not-null="false" />
      <property name="addressLine4" column="address_line_4" not-null="false" />
      <property name="addressLine5" column="address_line_5" not-null="false" />
      <property name="addressLine6" column="address_line_6" not-null="false" />
      <property name="addressLine7" column="address_line_7" not-null="false" />

   </class>

   <class name="com.qas.newmedia.intranet.pof.dto.Action"
      table="tbl_Actions">

        <id name="id" type="int" column="action_id" unsaved-value="0">
           <generator class="identity" />
       </id>

      <property name="created" column="created" type="calendar" not-null="false" />
      <property name="username" column="username" not-null="false" />
      <property name="fullName" column="fullName" not-null="false" />
      <property name="department" column="department" not-null="false" />
      <property name="action" column="action" not-null="false" />

      <many-to-one name="purchaseOrderForm" column="pof_id" not-null="true" />
      
      <property name="index" type="int" update="true" insert="true" column="index_col" />
   </class>
   
</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():
N/a
Full stack trace of any exception that occurs:
N/a
Name and version of the database you are using:
SQL Server 2000
The generated SQL (show_sql=true):
N/a
Debug level Hibernate log excerpt:
N/a


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 26, 2004 4:46 pm 
Senior
Senior

Joined: Fri Jun 18, 2004 10:17 am
Posts: 140
would really appreciate some consideration of this question if someone out there knows about this :))


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 27, 2004 7:32 am 
Beginner
Beginner

Joined: Fri Oct 15, 2004 4:54 pm
Posts: 32
fwiw, i took the fact that you and i had the same problem to mean that something is goofy in HQL (i've found another HQL anomaly recently). i switched my misbehaving code to SQL yesterday and that seems to have solved the problem.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 27, 2004 7:39 am 
Senior
Senior

Joined: Fri Jun 18, 2004 10:17 am
Posts: 140
i thought someone would be able to help with this as i imagine it's quite common to want to remove items from an association and then update the parent object having that cascade the deletions into the database.

i don't really want to swap to SQL because i really like using pure hibernate, but sometimes things are not obvious from the docs.

anyone care to shed some light on this problem? :)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 27, 2004 7:40 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Quote:
i took the fact that you and i had the same problem to mean that something is goofy in HQL


There is of course nothing "goofy", except for the fact that you seem to find "you cannot dereference a collection with cascade="all-delete-orphan" somehow difficult to understand.

I'm really not sure what I can say. Don't dereference the collection if you have orphan delete enabled. I don't know how else to explain it.

Sorry for sounding rude, but I don't like people calling my work "goofy".


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 27, 2004 7:45 am 
Beginner
Beginner

Joined: Fri Oct 15, 2004 4:54 pm
Posts: 32
sorry, that obviously came out wrong. i am confused, however, as to why i get that error on a find, which i wouldn't expect to be dereferencing anything. i am obviously not the dereferencing guru that you are.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 27, 2004 7:48 am 
Senior
Senior

Joined: Fri Jun 18, 2004 10:17 am
Posts: 140
hey gavin,

I understand what the error is saying in general, but i don't know what it means precisely. From reading up on the all-delete-orphan cascade, it appears it would do the job, but my code below (which loads a POF parent object with one left join fetch for collection 1 and then manually goes to get the other collection) fails with this error and I am not sure which dereference it is referring to.

When all is said and done, I would like to call parent.update(o) and have the associations also updated including deleted elements from those. But I also need to load my parent object along with 2 collection associations.

At the moment it looks like I cannot and I am limited by my understanding of what is going on. I'd really appreciate some pointers on how to get these 2 cases working if it is possible :)

Thanks for your time.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 27, 2004 7:49 am 
Senior
Senior

Joined: Fri Jun 18, 2004 10:17 am
Posts: 140
Code:
Query q = session.createQuery(
                "from POF as p " +
                "   left outer join fetch p.lineItems " +
                "where p.id = :pofId"
            );
           
         q.setParameter("pofId", id);
           
            pof  = (POF) q.list().get(0);
           
            if (pof != null) {
               q = session.createQuery(
                  "from Action as a " +
                    "where a.purchaseOrderForm.id = :pofId"
               );
              
               q.setParameter("pofId", id);
              
               pof.setActionHistory(q.list());
            }


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 27, 2004 8:46 am 
Senior
Senior

Joined: Fri Jun 18, 2004 10:17 am
Posts: 140
ok, i reduced my code to the following to test why "You may not dereference a collection with cascade="all-delete-orphan"" was being thrown.

Query q = session.createQuery(
"from POF as p " +
"where p.id = :pofId"
);

q.setParameter("pofId", id);

pof = (POF) q.list().get(0);

pof.setLineItems(new ArrayList());
pof.setActionHistory(new ArrayList());


i.e I stopped fetching the collection in the first query, and i am no longer directly looking up the 2nd collection too.

it appears that i get the error message just by virtue of having an object with an association, period, when cascade="all-delete-orphan".

I don't understand this, because I thought this particular cascade was designed to cascade deletes to associations.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 27, 2004 8:50 am 
Senior
Senior

Joined: Fri Jun 18, 2004 10:17 am
Posts: 140
ok, this is turning into a running commentary isn't it lol. Every time i post an update i come up with something else to try. I have managed to at least prove my last comment false now.

I noticed that the lines

Code:
pof.setLineItems(new ArrayList());
pof.setActionHistory(new ArrayList());


were within the session transaction. I moved these out of the transaction so that they only got applied to the detached parent, and this works.

so, next step is to work out how on earth i get the 2 associations loaded whilst still being allowed to use the cascade.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 27, 2004 10:32 am 
Senior
Senior

Joined: Fri Jun 18, 2004 10:17 am
Posts: 140
right, i am getting somwhere i think. i now have cascade="all-delete-orphan" set on my collection and I have also found out a way of loading both collections when i need to into my parent object. The main collection is done with the fetch join when loading the parent, and the other is done separately using another query. Although I don't particularly understand why i cannot load more than one collection into my parent object, and i am sending lots of sql queries now, at least it's working.

What is still not working is removed collection elements are not being removed from the database when persisting my parent object which I was hoping what all-delete-orphan would do.

I followed the FAQ that says I have to implement a parent.removeChild(c) method to not only remove c from the collection, but also to call c.setParent(null) to ensure both "ends" are disassociated. I think this is a prerequisite for all-delete-orphan to work.

However, having done this I am getting a new error
not-null property references a null or transient value: com.qas.newmedia.intranet.pof.dto.LineItem.purchaseOrderForm

Now, I know what this error is, I've had it before, it's complaining that c.parent is null, but that's what the FAQ said I have to do to make the child an orphan.

Please if you can help, do :) Hair on my head is now a sparse resource.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 27, 2004 11:43 am 
Senior
Senior

Joined: Fri Jun 18, 2004 10:17 am
Posts: 140
oh, thank goodness! it works goddamn!!

here is my final solution.

My Order object has a collection called lineItems which is an indexed one to mant list. I have lots of Orders persisted. At some point i want to edit an order and remove items. So, I load an Order using Hibernate and then close the session. I now have what's called a transient instance of the Order, i.e it's in memory and any changes i make to it will not be persisted at this time. My application is a web app, so i send some JSPs back at the user and the Order of course and they can do some delete operations to remove items from the transient order.getLineItems collection.

My problem was that at the end of all this, I could not get Hibernate to delete rows that were no longer in the collection.

Finally, I have.

My mapping for the collection is this. Key are the inverse and cascade values.

<list name="lineItems" inverse="true" lazy="true" cascade="all-delete-orphan">
<key column="pof_id"/>
<index column="index_col" />
<one-to-many class="com.qas.newmedia.intranet.pof.dto.LineItem" />
</list>

and just a simple order.getLineItems().remove(index) to delete certain items is enough on the transient state. Before, I was setting each lineItem's reference to Order to null because I thought you had to totally disassociate the order from the item due to inverse="true" but this caused a null exception.


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