-->
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.  [ 32 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Update Bi-directional relationship???
PostPosted: Wed Nov 19, 2003 10:07 am 
Regular
Regular

Joined: Wed Aug 27, 2003 2:55 am
Posts: 90
Location: Sweden
Hi,

I think Hibernate is a great framework. It solved a lot of O/R mapping problems for my solutions.

I got one "problem". Isn't really a problem, more of a "how-to" question.

Here it goes:

I got a parent object A, and its children B. The relationship is bi-directional. Object A has a java.util.Set with references to B objects, and Each object B have a reference to its parent A object.

The hibernate mapping for object A uses inverse="true" in its relationship mapping and Apache Turbin JCS.

I load an object A from the database in one Session, modify a value in object A, call Session.update() in another Session and then reloads the object from the database in that Session.
The object A recieved have been changed, but nothing is changed in the database.

I can se three updates preformed:

UPDATE object A
UPDATE object B
UPDATE object A // Again, because of the bi-directional relationship I think

If I change my code to do the following (psuedo-code):

Code:
ObjectA a = Session.load("myId");
a.setProperty("newProperty");
// Suppose we just have one child object
ObjectB b = ((ObjectB) a.getBObjects().iterator().next()).getObjectA().setProperty("newProperty");
Session.update(a);


...then it gets saved to the database.

I would like to know how I can load my object A in one session, change it, and update it in another session and get it to be saved in the database?

Kind regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 10:13 am 
Regular
Regular

Joined: Mon Sep 08, 2003 4:53 am
Posts: 70
Location: Germany
Isn't it enough, to use
Code:
sessionTwo.update(a)

if sessionTwo is the new session?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 10:25 am 
Regular
Regular

Joined: Wed Aug 27, 2003 2:55 am
Posts: 90
Location: Sweden
No it is not.

By the way, I use the Spring framework as well.

This is what I do (better described):

Code:
class MyDao extends HibernateDaoSupport {

    public ObjectA load(String id) {
        return getHibernateTemplate().load(ObjectA.class, id);
    }

    public void update(ObjectA myObject) {
        getHibernateTemplate().update(myObject);
    }
}


Code:
class MyBusinessObject {

    public ObjectA load(String id) {
        return myDao.load(id);
    }

    public ObjectA changeSomething(ObjectA myObject) {
        TransactionTemplate tt = new TransactionTemplate(myTransactionManager);
        tt.execute(new TransactionCallback() {
            public void doInTransaction(TransactionStatus status) {
                myDao.update(myObject);
                return myDao.load(muObject.getId());
            }
        });
    }
}


Code:
class Client {
    public static void main(String[]args) {
        ObjectA a = myBusinessObject.load("someId");
        a.setProperty("newProperty");
        ObjectA newA = myBusinessObject.update(a);
    }
}


Doesn't work!

But this does:

Code:
class Client {
    public static void main(String[]args) {
        ObjectA a = myBusinessObject.load("someId");
        a.setProperty("newProperty");
        ((ObjectB) a.getBObjects().iterate().next()).getObjectA().setProperty("newProperty");
        ObjectA newA = myBusinessObject.update(a);
    }
}


Some more advices?

Kind regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 10:53 am 
Regular
Regular

Joined: Wed Aug 27, 2003 2:55 am
Posts: 90
Location: Sweden
Anyone, please?!


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 10:54 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
So you have cascade update enabled?

Do you commit the transaction?

what do you mean by "reload()"? You mean refresh(), right?

You havn't really given quite the right information....


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 11:00 am 
Regular
Regular

Joined: Wed Aug 27, 2003 2:55 am
Posts: 90
Location: Sweden
Hi and thanks for the response!

I'm in the situation where I can't submit log, mappings etc. due to confidentiality, sorry?!

Could you please tell me what to submit at the minimum for you to understand what's happening?

1. Yes, cascade="all"

2. By reload I mean that after I have updated the object I reload it from the database.

Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 11:01 am 
Regular
Regular

Joined: Wed Aug 27, 2003 2:55 am
Posts: 90
Location: Sweden
Sorry, missed a question.

I suppose that the Spring TransactionTemplate is managing my commit(), rollback() etc.

Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 11:14 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
You didn't answer 2 properly. Relaoad it by doing what? Starting a brand new session and calling load()? Calling refresh() on an exsiting session? what?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 11:28 am 
Regular
Regular

Joined: Wed Aug 27, 2003 2:55 am
Posts: 90
Location: Sweden
By reloading I mean the following:

Code:
public ObjectA doChanges(ObjectA myObject) {
    TransactionTemplate tt = new TransactionTemplate(myTransactionManager);
    return tt.execute(new TransactionCallback() {
        public Object doInTransaction(TransactionStatus status) {
            myDao.update(myObject); // Update my object
            return myDao.load(myObject.getId()); // THIS IS MY RELOAD
        }
    });
}


Sample code (somewhat pseudo) of the data access object is posted earlier in this thread.

The TransactionTemplate is from Spring framework.

The method returns an instance of ObjectA with the property set as I want, but it didnt change anything in the database.

Seems like the JCS have two different instances of ObjectA and when we traverse the object structure down to ObjectB from ObjectA we find a reference to ObjectB's parent. Then reference to ObjectA in ObjectB have the old values and these are being persisted to the database. That's wy (I think) I get three updates:

UPDATE ObjectA
UPDATE ObjectB
UPDATE ObjectA

The mapping in ObjectA is:

Code:
<set name="BObjects" inverse="true" cascade="all">
    <jcs-cache usage="read-write"/>
    <key column="object_a_id"/>
    <one-to-many class="ObjectB"/>
</set>


The mapping in ObjectB is:

Code:
<many-to-one name="objectA" class="ObjectA" column="object_a_id" not-null="true" cascade="all"/>


And my cache.ccf:

Code:
jcs.default=
jcs.default.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes
jcs.default.cacheattributes.MaxObjects=2000
jcs.default.cacheattributes.MemoryCacheName=org.apache.jcs.engine.memory.lru.LRUMemoryCache


Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 11:30 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Well, assuming that myDao.load() calls Session.load(), that doesn't reload anything. It just returns an object str8 out of the session cache.

Use refresh()


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 11:37 am 
Regular
Regular

Joined: Wed Aug 27, 2003 2:55 am
Posts: 90
Location: Sweden
But if I re-define my update method to return nothing, just doing an update. The changes made to the object should get persisted?

The issue is: the update isn't persisting what I want to the database!

When I preform the update I can se from the Spring/Hibernate logging that three updates is preformed:

UPDATE ObjectA // with new values
UPDATE ObjectB // 'cause cascade="all" I believe
UPDATE ObjectA // with OLD VALUES!

The last update is done because ObjectA have a relationship (many-to-one) back to it's parent, am I right? But, with the "old" ObjectA retrieved from JCS cache???

Do you understand what I mean? I'm not to good in explaining in english.. :-/

Kind regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 11:43 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Quote:
The last update is done because ObjectA have a relationship (many-to-one) back to it's parent, am I right? But, with the "old" ObjectA retrieved from JCS cache???


Nah hehe. The cache is well aware of changes made by other txns.


I really don't understand what you're doing.

How about you simplfy it down. get rid or your daos, spring, etc. Write it as a single main method that does the same thing in a single thread. I think you will see whats wrong once you simplify it like that. If not, post the main() method here and then we will understand.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 12:06 pm 
Regular
Regular

Joined: Mon Sep 08, 2003 4:53 am
Posts: 70
Location: Germany
andreas_eriksson wrote:

But this does:

Code:
class Client {
    public static void main(String[]args) {
        ObjectA a = myBusinessObject.load("someId");
        a.setProperty("newProperty");
        ((ObjectB) a.getBObjects().iterate().next()).getObjectA().setProperty("newProperty");
        ObjectA newA = myBusinessObject.update(a);
    }
}




You said, it works, if you do this.
Have you debugged this and checked, if
Quote:
object a

and
Quote:
((ObjectB) a.getBObjects().iterate().next()).getObjectA()

is the same object?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 12:12 pm 
Regular
Regular

Joined: Wed Aug 27, 2003 2:55 am
Posts: 90
Location: Sweden
Ok, heres my code as it is (somewhat simplified for this post):

Code:
// Uses Spring framework class HibernateDaoSupport

class MyDao extends HibernateDaoSupport {
    public ObjectA loadById(String id) {
        return getHibernateTemplate().load(ObjectA.class, id);
    }

    public void update(ObjectA myObject) {
        getHibernateTemplate().update(myObject);
    }
}


Code:
// Controller class does special thing for ObjectA in the object structure

class ObjectAController {

    private MyDao myDao;   

    public ObjectA loadById(String id) {
        return myDao.loadById(id);
    }

    public void update(ObjectA myObject) {
        myDao.update(myObject);
    }
}


Code:
// Service object uses Spring framework transaction management

class MyObjectAService {

    private PlatformTransactionManager myTransactionManager;
    private ObjectAController myController;

    public ObjectA load(String id) {
        return myController.loadById(id);
    }

    public ObjectA persistChanges(final ObjectA myObject) {
        TransactionTemplate tt = new TransactionTemplate(
            this.myTransactionManager);
        return tt.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus status) {
                myController.update(myObject);
                return myController.loadById(myObject.getId());
            }
        });
    }
}


Code:
class Client {
    public static void main(String[] args) {
        MyObjectService service = new MyObjectService();
        // The object we're loading exists in the db
        ObjectA oldA = service.load("someId");
        oldA.setProperty("newProperty");
        ObjectA newA = service.persistChanges(oldA);
       
        // newA have the property set to "newProperty",
        // but it's not persisted to db
    }
}


ObjectA mapping:

Code:
<hibernate-mapping default-cascade="all">
    <class name="ObjectA" table="A" dynamic-update="true" dynamic-insert="true">
        <jcs-cache usage="read-write"/>

        // Define id
        // Define properties

       <set name="BObjects" inverse="true" cascade="all">
           <jcs-cache usage="read-write"/>
           <key column="object_a_id"/>
           <one-to-many class="ObjectB"/>
       </set>
    </class>
</hibernate-mapping>


ObjectB mapping:

Code:
<hibernate-mapping default-cascade="all">
    <class name="ObjectB" table="B" dynamic-update="true" dynamic-insert="true">
        <jcs-cache usage="read-write"/>

        // Define id
        // Define properties

       <many-to-one name="objectA" column="object_a_id" not-null="true" cascade="all"/>
    </class>
</hibernate-mapping>


JCS config file

Code:
jcs.default=
jcs.default.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes
jcs.default.cacheattributes.MaxObjects=2000
jcs.default.cacheattributes.MemoryCacheName=org.apache.jcs.engine.memory.lru.LRUMemoryCache


Database does three updates when I call persistChanges():

Code:
UPDATE objectA // With new values set
UPDATE objectB
UPDATE objectA // With OLD values


It isn't more than that! Cannot se what's wrong with that. All I'm doing is to delegate a bunch of calls to different object within a Spring transaction managed method.

Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 19, 2003 12:15 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
I don't think you understand. I don't know anything about spring txns. So show me, as I requested, a simple main() that uses only Hibernate APIs.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 32 posts ]  Go to page 1, 2, 3  Next

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.