-->
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.  [ 13 posts ] 
Author Message
 Post subject: Cascade="all-delete-orphan" means I can't manipula
PostPosted: Thu Aug 04, 2005 6:16 pm 
Beginner
Beginner

Joined: Thu May 05, 2005 11:12 pm
Posts: 26
Moving on to the next problem with my resource and resource_cost relationship.

The resource costs have a lifecycle completely dependant on resource. If the resource is deleted, all the resource costs should be deleted. If I add a row to the list it should be persisted, remove it from the list deleted etc. According to the docs this means I should specify cascade="all-delete-orphan". So I did.

However the resourceCost list is time dependant. A person's costPerHour changes from $10, to $20 , to $30 etc. These changes can take effect at any time. The user sees an 'effective date'. Lets say the three costs become effective on 1/1/2002, 1/10/2003 and 1/6/2004 (in d/m/y format).

The data model holds this information as $10 from 1/1/2002 to 1/10/2003, $20 from 1/10/2003 to 1/6/2004, and $30 from 1/6/2004 to 31/12/9999 which is a date we picked that is obscenely in the future. The objective is to make locating the specific cost relevant on a particular date easy. I can invoke sql of the form 'where :targetDate between startDate and endDate' to locate the specific cost record relevant for :targetDate.

However it is equally concievable that the user could edit the last entry and change the effective date to 1/6/2003. This means I have to change all the date ranges to $10 from 1/1/2002 to 1/6/2003, $30 from 1/6/2003 to 1/10/2003, and $20 from 1/10/2003 to 31/12/9999.

This is quite easy to do. In java I sort the list by start date, and set the end dates of element n-1 to the start date of element n, and set the end date of the last element to 31/12/9999.

However Hibernate complains and says "Don't dereference a collection with cascade="all-delete-orphan": com.ilign.ppm.domain.Resource.resourceCosts"

I have tried doing this with a set, sorted set, and bag.

The problem with set is that to sort the list I have to create a new TreeSet with a comparator, add all the elements of my existing set to it, process and then assign my new TreeSet to the entity variable. Quite easy to see how Hibernate complains about that.

The problem with sortedSet (mapping defines as <set with a sort="comparatorClass") is that when the user changes the contents of a ResourceCost, the sorted set doesn't trigger a sort. And even if it did, the user may have changed the data such that the ResourceCost now 'equals' another one which violates the Set contract. So I have to sort the contents elsewhere which brings the same problem as above.

I thought bag would work. Bag works like a List. I can use Collections.sort on that and I thought that the list was sorted in place, but obviously is isn't because I still get the problem.

Does this mean I have to manufacture a lifecycle for ResourceCost and individually manage the saving etc of them purely because I need to be able to manipulate the contents of the list?

Hibernate version: 3

Mapping documents:
Code:
<hibernate-mapping package="com.ilign.ppm.domain">
  <class name="Resource" table="RESOURCE" lazy="true" dynamic-update="true">
    <property name="firstNames" column="FIRST_NAMES" type="string"/>
    .
    .
    .
    <bag name="resourceCosts" table="RESOURCE_COST" order-by="start_date" lazy="true" cascade="all-delete-orphan" inverse="true">
        <key column="RESOURCE_ID" />
        <one-to-many class="ResourceCost"/>
    </bag>
  </class>
  <class name="ResourceCost" table="RESOURCE_COST" lazy="true" dynamic-update="true">
      <many-to-one name="resource" column="RESOURCE_ID" />
      <property name = "startDate" column="START_DATE" type="com.ilign.ppm.common.PersistentDateTime"/>
      <property name = "endDate" column="END_DATE" type="com.ilign.ppm.common.PersistentDateTime"/>
      <component name="costPerHour" class="com.ilign.ppm.common.ForeignCurrency">
          <component name="baseMoney" class="com.ilign.ppm.common.Money">
              <property name="amount" column="COST_PER_HOUR" type="big_decimal" not-null="true"/>
              <property name="currencyCode" column="BASE_CURRENCY_CODE" />
          </component>
          <component name="srcMoney" class="com.ilign.ppm.common.Money">
              <property name="amount" column="SRC_COST" type="big_decimal"/>
              <property name="currencyCode" column="SRC_COST_CURRENCY_CODE" type="string"/>
          </component>
      </component>
      <component name="billableRate" class="com.ilign.ppm.common.ForeignCurrency">
          <component name="baseMoney" class="com.ilign.ppm.common.Money">
              <property name="amount" column="BILLABLE_RATE" type="big_decimal" not-null="true"/>
              <property name="currencyCode" column="BASE_CURRENCY_CODE" insert="false" update="false" />
          </component>
          <component name="srcMoney" class="com.ilign.ppm.common.Money">
              <property name="amount" column="SRC_BILLABLE" type="big_decimal"/>
              <property name="currencyCode" column="SRC_BILLABLE_CURRENCY_CODE" type="string"/>
          </component>
      </component>
  </class>
</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():
Basically the code that does the sort... It is using a bag (list) at the moment since that is where I've ended up.
Code:
Collections.sort( this.resourceCosts, new ResourceCostStartDateComparator() );

// Move over the elements and set the end date of element n to be 1 millisecond less than the start date of element n
ResourceCost previousResourceCost = null;
for ( ResourceCost aResourceCost : this.resourceCosts )
{
    if ( previousResourceCost == null )
    {
        previousResourceCost = aResourceCost;
        continue;
    }
    if ( aResourceCost.getStartDate().equals( previousResourceCost.getStartDate() ) )
    {
        throw new DuplicateCostException( "A ResourceCost was found that already starts on "
                    + aResourceCost.getStartDate() );
    }
    DateTime newEndDate = new DateTime( aResourceCost.getStartDate() ));
    previousResourceCost.setEndDate( newEndDate );
    previousResourceCost = aResourceCost;
}
// Set the end date of the last element to be the nominal end date and make the resource point to the new set
this.resourceCosts.get( this.resourceCosts.size() - 1 ).setEndDate( Util.NOMINAL_LATEST_DATE );


Full stack trace of any exception that occurs:
junit.framework.AssertionFailedError: Don't dereference a collection with cascade="all-delete-orphan": com.ilign.ppm.domain.Resource.resourceCosts
at com.ilign.ppm.dao.ResourceDaoHibernateTest.testAddResourceCostMaintainsSortOrder(ResourceDaoHibernateTest.java:113)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at com.intellij.rt.execution.junit2.JUnitStarter.main(JUnitStarter.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:86)

Name and version of the database you are using: Firebird

The generated SQL (show_sql=true):
2005-08-05 09:23:37,846 INFO [com.ilign.ppm.dao.ResourceDaoHibernateTest] Began transaction: transaction manager [org.springframework.orm.hibernate3.HibernateTransactionManager@1d38b87]; defaultRollback true
2005-08-05 09:23:37,862 DEBUG [org.hibernate.SQL] select resource0_.RESOURCE_ID as RESOURCE1_, resource0_.VSN as VSN17_, resource0_.FIRST_NAMES as FIRST3_17_, resource0_.LAST_NAME as LAST4_17_, resource0_.FULL_NAME as FULL5_17_, resource0_.USER_NAME as USER6_17_, resource0_.PW as PW17_, resource0_.PASSWORD_CHANGED as PASSWORD8_17_, resource0_.PASSWORD_EXPIRY as PASSWORD9_17_, resource0_.EMAIL_ADDRESS as EMAIL10_17_, resource0_.RESOURCE_NUMBER as RESOURCE11_17_, resource0_.SKILL_SET as SKILL12_17_, resource0_.PRIMARY_ADDRESS_ID as PRIMARY13_17_, resource0_.PRIMARY_PHONE_ID as PRIMARY14_17_, resource0_.START_DATE as START15_17_, resource0_.END_DATE as END16_17_, resource0_.ORG_NAME as ORG17_17_, resource0_.RESPONSIBILITY as RESPONS18_17_, resource0_.TIMESHEET_REQUIRED as TIMESHEET19_17_, resource0_.PRIMARY_VALIDATOR_ID as PRIMARY20_17_, resource0_.SECONDARY_VALIDATOR_ID as SECONDARY21_17_, resource0_.LOCATION as LOCATION17_, resource0_.RESOURCE_TYPE as RESOURCE23_17_ from RESOURCE resource0_ where resource0_.USER_NAME=?
2005-08-05 09:23:37,893 DEBUG [org.hibernate.SQL] select usersettin0_.RESOURCE_ID as RESOURCE1_0_, usersettin0_.VSN as VSN27_0_, usersettin0_.CREATE_START_MILESTONE as CREATE3_27_0_, usersettin0_.CREATE_END_MILESTONE as CREATE4_27_0_, usersettin0_.SHOW_PROJECT_MENU as SHOW5_27_0_, usersettin0_.DISPLAY_EXPIRED_ITEMS as DISPLAY6_27_0_, usersettin0_.LOCALE_STRING as LOCALE7_27_0_, usersettin0_.HOME_PAGE_CODE as HOME8_27_0_ from USER_SETTING usersettin0_ where usersettin0_.RESOURCE_ID=?
2005-08-05 09:23:37,940 DEBUG [org.hibernate.SQL] select resourceco0_.RESOURCE_ID as RESOURCE3_1_, resourceco0_.RESOURCE_COST_ID as RESOURCE1_1_, resourceco0_.RESOURCE_COST_ID as RESOURCE1_0_, resourceco0_.VSN as VSN20_0_, resourceco0_.RESOURCE_ID as RESOURCE3_20_0_, resourceco0_.START_DATE as START4_20_0_, resourceco0_.END_DATE as END5_20_0_, resourceco0_.COST_PER_HOUR as COST6_20_0_, resourceco0_.BASE_CURRENCY_CODE as BASE7_20_0_, resourceco0_.SRC_COST as SRC8_20_0_, resourceco0_.SRC_COST_CURRENCY_CODE as SRC9_20_0_, resourceco0_.BILLABLE_RATE as BILLABLE10_20_0_, resourceco0_.SRC_BILLABLE as SRC11_20_0_, resourceco0_.SRC_BILLABLE_CURRENCY_CODE as SRC12_20_0_ from RESOURCE_COST resourceco0_ where resourceco0_.RESOURCE_ID=? order by resourceco0_.start_date
2005-08-05 09:23:38,002 DEBUG [org.hibernate.SQL] select next_value from OBJECT_ID with lock
2005-08-05 09:23:38,018 DEBUG [org.hibernate.SQL] update OBJECT_ID set next_value = ? where next_value = ?
2005-08-05 09:23:38,049 DEBUG 2005-08-05 09:23:38,065 DEBUG [org.hibernate.SQL] select codedvalue0_.CODED_VALUE_ID as CODED1_0_, codedvalue0_.VSN as VSN4_0_, codedvalue0_.DESCRIPTION as DESCRIPT3_4_0_, codedvalue0_.MODIFIABLE as MODIFIABLE4_0_, codedvalue0_.DELETABLE as DELETABLE4_0_, codedvalue0_.SEQUENCE_NUMBER as SEQUENCE6_4_0_, codedvalue0_.START_DATE as START7_4_0_, codedvalue0_.END_DATE as END8_4_0_, codedvalue0_.CODE_TABLE_ID as CODE9_4_0_ from CODED_VALUE codedvalue0_ where codedvalue0_.CODED_VALUE_ID=?
2005-08-05 09:23:38,080 DEBUG [org.hibernate.SQL] select roles0_.RESOURCE_ID as RESOURCE1_1_, roles0_.ROLE_ID as ROLE2_1_, role1_.ROLE_ID as ROLE1_0_, role1_.VSN as VSN22_0_, role1_.NAME as NAME22_0_ from ROLE_RESOURCE roles0_ inner join ROLE_DEF role1_ on roles0_.ROLE_ID=role1_.ROLE_ID where roles0_.RESOURCE_ID=?
2005-08-05 09:23:38,080 DEBUG [org.hibernate.SQL] select projectrol0_.RESOURCE_ID as RESOURCE1_1_, projectrol0_.PROJECT_ROLE_ID as PROJECT2_1_, projectrol1_.PROJECT_ROLE_ID as PROJECT1_0_, projectrol1_.VSN as VSN15_0_, projectrol1_.NAME as NAME15_0_, projectrol1_.PROJECT_ID as PROJECT4_15_0_, projectrol1_.ROLE_TYPE as ROLE5_15_0_ from PROJECT_ROLE_RESOURCE projectrol0_ inner join PROJECT_ROLE projectrol1_ on projectrol0_.PROJECT_ROLE_ID=projectrol1_.PROJECT_ROLE_ID where projectrol0_.RESOURCE_ID=?
2005-08-05 09:23:38,112 INFO [com.ilign.ppm.dao.ResourceDaoHibernateTest] Rolled back transaction after test execution

Debug level Hibernate log excerpt:
Will provide if necessary but this is more an architectural question...


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 04, 2005 9:37 pm 
Senior
Senior

Joined: Sat Jul 17, 2004 5:16 pm
Posts: 143
Can you not use a List or some other ordered collection (e.g. not Set) and write your own sort algorithm which sorts in place?

Chris


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 04, 2005 9:52 pm 
Beginner
Beginner

Joined: Thu May 05, 2005 11:12 pm
Posts: 26
mchyzer wrote:
Can you not use a List or some other ordered collection (e.g. not Set) and write your own sort algorithm which sorts in place?

Chris


Well, yes, I suppose I could. But then what's the point of using toolsets and class libraries. I have business code to solve without having to re-code class libraries.

This isn't a terribly outrageous scenario I'm trying to deal with. I have a Resource object which manages as part of its data a collection of ResourceCost objects. Hibernate tells me that this scenario is best managed with cascade="all-delete-orphan". It also tells me that it can satisfy the sorting requirements by specifying a sort="Comparator". Management of that list cannot reasonably be expected to be limited to adding and removing from the list. Changing the data which could by definition change the sort order is not unreasonable.

Java supplies code that has been heavily researched and mathematically proven to achieve sorting for me. I'm not a mathematician. I expect to be able to use Collections.sort().


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 1:45 am 
Senior
Senior

Joined: Sat Jul 17, 2004 5:16 pm
Posts: 143
rouppe wrote:
Management of that list cannot reasonably be expected to be limited to adding and removing from the list. Changing the data which could by definition change the sort order is not unreasonable.


Are you using a SortedSet type? This will return a TreeSet. Collections only resort on changes to the collection not changes to the object, so maybe you need to copy all objects out of the set, and add them back in (thus sorting and not losing the same object reference). If you get a List from hibernate and call Collections.sort() with a Comparator, that will sort in place.

Chris


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 3:40 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
As far as I am aware, Collections.sort() sorts a collection in place, so the entire premise of your post seems to be mistaken. You would never need to dereference the collection.

Or course, if it were me, I just would do something like:

<set sort="MyComparatorClass">


Last edited by gavin on Fri Aug 05, 2005 3:49 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 3:42 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Quote:
The problem with set is that to sort the list I have to create a new TreeSet with a comparator, add all the elements of my existing set to it, process and then assign my new TreeSet to the entity variable. Quite easy to see how Hibernate complains about that.


This is of course not true. All you have to do is remove and re-add any element that was changed. At maximum, all you need to do is copy all elements, clear() the original instance, and addAll().


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 07, 2005 5:50 pm 
Beginner
Beginner

Joined: Thu May 05, 2005 11:12 pm
Posts: 26
mchyzer wrote:
Are you using a SortedSet type? This will return a TreeSet. Collections only resort on changes to the collection not changes to the object, so maybe you need to copy all objects out of the set, and add them back in (thus sorting and not losing the same object reference). If you get a List from hibernate and call Collections.sort() with a Comparator, that will sort in place.

Chris


Yes I tried that. I thought that would do the trick as well, but I still got the "don't defererence" error. However any type of Set has bigger problems in that the contract for Set is that there are no duplicates. It is possible for the user to change data such that it then becomes equals() to another existing one. The only ways I can think of to detect that are:
1) Iterate over the elements and test each one. However the behaviour of a SortedSet becomes undefined if the contents of the elements are changed to violate the "no duplicates" rule.
2) Add them all to another SortedSet. It will reject any that are duplicates so the sizes will be different. But I tried removeAll and addAll and still got the dereference error.

And so I ended up back where I started... :-)

Andrew


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 07, 2005 5:56 pm 
Beginner
Beginner

Joined: Thu May 05, 2005 11:12 pm
Posts: 26
gavin wrote:
As far as I am aware, Collections.sort() sorts a collection in place, so the entire premise of your post seems to be mistaken. You would never need to dereference the collection.

Or course, if it were me, I just would do something like:

<set sort="MyComparatorClass">


As regards Collections.sort() I agree, I didn't dereference the collection so it should be fine. So why do I continue to get the "don't dereference" error?

And as I said in my original post, I did try a definition of the form "<set with a sort="comparatorClass") ".

However Set's of any form have the more limiting feature of not allowing duplicates and it is possible for the user to change data such that one elements becomes "equals()" to another element.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 07, 2005 6:06 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
rouppe wrote:
As regards Collections.sort() I agree, I didn't dereference the collection so it should be fine. So why do I continue to get the "don't dereference" error?


'Cos you made a mistake somewhere, presumably. Use your debugger.

rouppe wrote:
However Set's of any form have the more limiting feature of not allowing duplicates and it is possible for the user to change data such that one elements becomes "equals()" to another element.


Correct relational data never contains duplicates.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 10, 2005 5:28 pm 
Beginner
Beginner

Joined: Thu May 05, 2005 11:12 pm
Posts: 26
gavin wrote:
Correct relational data never contains duplicates.


What a ridiculous statement. It is up to the application to reject data that is duplicate. That required validation.

The exercise I'm going through is the validation exercise, which involves sorting the data with other related time dependant data, retrieving information from other areas of the business, and making an assessment of its' suitability.

It is asinine to suggest that during the transition period between the user submitting data and the application accepting or rejecting it for persistence that data can never be at risk from being duplicate from a business perspective.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 10, 2005 5:34 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
"Ridiculous"??? "asinine"??

Look up the definition of "relation". Hint: a relation is a kind of set.

Buy a book on relational modelling and educate yourself before coming here and trying to insult people who actually understand this stuff.

Also try out some book on interpersonal relations.

If you continue in this vein, you will be banned immediately.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 10, 2005 5:50 pm 
Beginner
Beginner

Joined: Thu May 05, 2005 11:12 pm
Posts: 26
You made me mad by being rude.

I've made you mad by being rude.

Alright. Truce. Perhaps we can both learn something from this.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 10, 2005 6:24 pm 
Beginner
Beginner

Joined: Sun Sep 19, 2004 5:02 pm
Posts: 28
Location: Poland
There must be some primary key for table.

If no then we will not be able to refer to single row - will not be able to update, delete or find single row.

Second, primary key should be chosen so that it would never change. If there is no such beter use some autogenerated id.

_________________
Lmichasz


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