-->
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.  [ 15 posts ] 
Author Message
 Post subject: session.evict generates update statements!
PostPosted: Thu Jul 29, 2004 1:27 am 
Regular
Regular

Joined: Thu Apr 29, 2004 5:08 pm
Posts: 56
Location: Montreal, Quebec, Canada
I'm trying to evict objects from the session cache.

I have a Folder and a FolderVersion entity. Both are proxied (lazy=true attribute in the <class> element)
They are linked as a Parent/Child relation:

from Folder mapping...

Code:
<set
   name="versions"
   table="Folder_Version"
   lazy="true"
   cascade="all-delete-orphan"
   inverse="true">
         
   <key
      column="Folder_Id"/>
         
   <one-to-many
      class="FolderVersion"/>
                  
</set>


from FolderVersion mapping...

Code:
<many-to-one
   name="folder"
   class="Folder"
   column="Folder_Id"
   cascade="save-update"/>


In my application, I load a FolderVersion. // no select, but proxy class created. OK

Then I access it:

Code:
folder = folderVersion.getFolder() // this force a select on the Folder_Version table and no other select (remember both are proxy). OK


Then I access the folder:

Code:
folder.getReference(); // this force a select on the Folder table. OK


Then, I remove it form the session cache

Code:
session.evict( folder )


When the transaction is committed, I hibernate generate this:

Quote:
Hibernate: update Folder set Reference=? where Folder_Id=?


Why?

If I remove the evict() call - or - change the mapping of FolderVersion to :

Code:
<many-to-one
   name="folder"
   class="Folder"
   column="Folder_Id"
   cascade="none"/> // cascade now none


The unnecessary update is gone.... But of course I want cascading...

Also, I thougt that after this evict(), if I would do folderVersion.getFolder().getReference(), it would force a select to happen again on the folder table. But it doesn't. Just like if it would have found it in cache......

Hope someone will help me on that one. I'm losing alot of precious time at debugging it.

Thank you,

Here is the Hibernate trace portion when I evict the folder:

DEBUG - evicting entity from memory cache: com.convera.repository.model.Folder@e20ef8
DEBUG - evicting [com.convera.repository.model.Folder]
DEBUG - evicting collection: [com.convera.repository.model.Folder.versions#443]
DEBUG - processing cascades for: com.convera.repository.model.Folder
DEBUG - cascading to collection: com.convera.repository.model.Folder.versions
DEBUG - done processing cascades for: com.convera.repository.model.Folder

And when the transaction is commited:

DEBUG - commit
DEBUG - flushing session
DEBUG - processing cascades for: com.convera.repository.model.ComponentVersion
DEBUG - cascading to collection: com.convera.repository.model.ComponentVersion.parameters
DEBUG - cascading to collection: com.convera.repository.model.ComponentVersion.languages
DEBUG - done processing cascades for: com.convera.repository.model.ComponentVersion
DEBUG - processing cascades for: com.convera.repository.model.FolderVersion
DEBUG - cascading to saveOrUpdate()
DEBUG - id unsaved-value strategy NULL
DEBUG - saveOrUpdate() previously saved instance with id: 443
DEBUG - updating [com.convera.repository.model.Folder#443]
DEBUG - processing cascades for: com.convera.repository.model.Folder
DEBUG - cascading to collection: com.convera.repository.model.Folder.versions
DEBUG - done processing cascades for: com.convera.repository.model.Folder
DEBUG - cascading to saveOrUpdate()
DEBUG - cascading to collection: com.convera.repository.model.FolderVersion.children
DEBUG - cascading to collection: com.convera.repository.model.FolderVersion.names
DEBUG - cascading to saveOrUpdate()
DEBUG - saveOrUpdate() persistent instance
DEBUG - cascading to saveOrUpdate()
DEBUG - saveOrUpdate() persistent instance
DEBUG - done processing cascades for: com.convera.repository.model.FolderVersion
DEBUG - Flushing entities and processing referenced collections
DEBUG - Collection found: [com.convera.repository.model.ComponentVersion.parameters#7], was: [com.convera.repository.model.ComponentVersion.parameters#7]
DEBUG - Collection found: [com.convera.repository.model.ComponentVersion.languages#7], was: [com.convera.repository.model.ComponentVersion.languages#7]
DEBUG - Collection found: [com.convera.repository.model.ComponentVersion.dependencies#7], was: [com.convera.repository.model.ComponentVersion.dependencies#7]
DEBUG - Collection found: [com.convera.repository.model.FolderVersion.children#360], was: [com.convera.repository.model.FolderVersion.children#360]
DEBUG - Collection found: [com.convera.repository.model.FolderVersion.names#360], was: [com.convera.repository.model.FolderVersion.names#360]
DEBUG - Updating entity: [com.convera.repository.model.Folder#443]
DEBUG - Collection found: [com.convera.repository.model.Folder.versions#443], was: [com.convera.repository.model.Folder.versions#443]
DEBUG - Processing unreferenced collections
DEBUG - Scheduling collection removes/(re)creates/updates
DEBUG - Flushed: 0 insertions, 1 updates, 0 deletions to 7 objects
DEBUG - Flushed: 0 (re)creations, 0 updates, 0 removals to 6 collections
DEBUG - listing entities:
DEBUG - com.convera.repository.model.FolderNameVersion{folderVersion=FolderVersion#360, updateCount=0, language=Language#2, name=french, id=296}
DEBUG - com.convera.repository.model.FolderNameVersion{folderVersion=FolderVersion#360, updateCount=0, language=Language#1, name=test, id=290}
DEBUG - com.convera.repository.model.ComponentVersion{component=Component#HRC, dependencies=uninitialized, parameters=uninitialized, description=H&R Classification 1.0, creationDate=01 January 1900 00:00:00, state=VersionState#1, lastUpdatedDate=null, languages=uninitialized, name=1.0, id=7}
DEBUG - com.convera.repository.model.Language{code=en, description=English, id=1}
DEBUG - com.convera.repository.model.FolderVersion{rule=RuleVersion#393, updateCount=2, names=[FolderNameVersion#290, FolderNameVersion#296], componentVersion=ComponentVersion#7, folder=Folder#443, children=uninitialized, parent=null, id=360}
DEBUG - com.convera.repository.model.Language{code=fr, description=French, id=2}
DEBUG - com.convera.repository.model.Folder{versions=uninitialized, id=443, reference=}
DEBUG - executing flush
DEBUG - Updating entity: [com.convera.repository.model.Folder#443]
DEBUG - about to open: 0 open PreparedStatements, 0 open ResultSets
DEBUG - update Folder set Reference=? where Folder_Id=?
Hibernate: update Folder set Reference=? where Folder_Id=?
DEBUG - preparing statement
DEBUG - Dehydrating entity: [com.convera.repository.model.Folder#443]
DEBUG - binding '' to parameter: 1
DEBUG - binding '443' to parameter: 2
DEBUG - done closing: 0 open PreparedStatements, 0 open ResultSets
DEBUG - closing statement
DEBUG - post flush
DEBUG - transaction completion
DEBUG - re-enabling autocommit

_________________
- Frank


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 29, 2004 5:19 am 
Regular
Regular

Joined: Thu Apr 29, 2004 5:08 pm
Posts: 56
Location: Montreal, Quebec, Canada
OK, I have investigated alot (traced in hibernate code) and here are what I discovered:

The call to evict( folder ) is indeed evicting the folder

Code:
 
object is a Hibernate Proxy
    object is initialized
   remove object from entitiesByKey  [ removeEntity(key) ]
   remove object from entityEntries [ removeEntity(entity) ]
   doEvict(persister, entity)


Then, cascading begin. Folder has a relation of type Set pointing to FolderVersion entities. The cascade style is all-delete-orphan. According to the documentation, evict should cascade to this style (and the delete style, also).

However, as you can see in the hibernate tracing I posted, we have, in the evict portion of the trace:

DEBUG - cascading to collection: com.convera.repository.model.Folder.versions
DEBUG - done processing cascades for: com.convera.repository.model.Folder

Tracing the code, I saw that nothing is actually done [cascadableChildrenIterator.hasNext() returns false]

I was expecting the FolderVersion (see previous post) would be evicted too, since it is associated from Folder with a all-delete-orphan cascade style.

So the evict() method return. The state of entities are:

- FolderVersion still is in the session cache
- Folder has been evicted, so not in the session cache

When the commit() happens, well, the FolderVersion entity is treated. There's a save-update cascade style between it and a Folder.

When cascading this save-update relation, hibernate find that a Folder (the one im talking about) is associated. It checks its state and see that it is not associated to the session. It consider it as transient.

Then, since we are cascading, hibernate wants to save or update this entity. It checks its unsaved-value (for id) and see that it actually have an id (not null). It then decides this entity should be updated.

What's happening upon commit is OK. It just follow the cascades and does what it has to do, the problem is when doing the evict.

My question is:

When I evict the folder, why does the folderVersion of its versions collection is not being evicted too (by cascade) even do it has a all-delete-orphan cascading style? This is the problem.

Thank you

_________________
- Frank


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 29, 2004 6:32 am 
Expert
Expert

Joined: Fri Feb 06, 2004 7:49 am
Posts: 255
Location: Moscow, Russia
on http://forum.hibernate.org/viewtopic.php?t=932987
christian wrote:
Session.evict() cascades to associations if they are mapped with cascade="all" or cascade="all-delete-orphan".

All objects from Folder.versions should be cascade evicted, you can check it with Session.contains:
Code:
version = (FolderVersion) folder.getVersions().get(0);
session.evict(folder);
assert (false == session.contains(version));


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 29, 2004 7:00 am 
Regular
Regular

Joined: Thu Apr 29, 2004 5:08 pm
Posts: 56
Location: Montreal, Quebec, Canada
Ya,

I know. And the documentation is clear. Just trying to understand why it doesn't occur.

Another thing. If an object instance is wrapped by a proxy, if I evict it and check session.contains, it returns true (even if it has been correctly evicted (traced code).

If the instance isn't wrapped by a proxy, I receive false after the evict.

It seems evict doesn`t clear the proxies. Just the entities they wrap
However in the session.evict code, there is a proxiesByKey.remove( ... ) line which is executed.

The way session.contains check for an entity which is wrap by a proxy doesn't seem to rely on this proxiesByKey collection.

If I initialize a entity with a proxy, then evict it and finally reaccess it from another entity, there is no sql performed.

So I'm trying to understand this and why evict() doesn't follow the all-delete-orphan cascade style.

_________________
- Frank


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 29, 2004 9:21 am 
Regular
Regular

Joined: Thu Apr 29, 2004 5:08 pm
Posts: 56
Location: Montreal, Quebec, Canada
OK, simple.

Since I accessed FolderVersion directly with a load, it is not in the Folder.versions collection. In fact the collection isn't initialized yet and that's why evict() doesn't cascade to elements in it (there are no).

Fixed everything now, thank you for your interest,

_________________
- Frank


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 14, 2005 11:24 pm 
Beginner
Beginner

Joined: Tue Jun 22, 2004 3:16 pm
Posts: 35
Frank,

I am having similar problem. The evict() seems to evict the object and the collection but not the element in the collection. Even I tried to initialize the collection before calling evict(), also I use cascade= "all". I had to manually set element's id = null to force save() to do "insert.." not "update...". What was your solution? I know it's been a while, hope you are still reading the thread.

Thanks,
J


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 15, 2005 10:31 am 
Regular
Regular

Joined: Thu Apr 29, 2004 5:08 pm
Posts: 56
Location: Montreal, Quebec, Canada
jzhang,

I had a watch on that thread so yes I'm steel reading it ;)

Ok, first, you have entity A having a collection (one-to-many to entities) of B.

You want to evict entity A and have all the B evicted also, as described in the documentation.

First, make sure the A`s collection of Bs is initialized. If not, the collection isn`t cascaded.

Second, if B has a many-to-one to A, the following may happen:

save B -> cascade to A -> cascade to Bs

This is mainly what I remember from the experience I had. I would need more details on what you are trying to do. Just wanted to post to let you know I'm watching it.

_________________
- Frank


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 15, 2005 5:01 pm 
Beginner
Beginner

Joined: Tue Jun 22, 2004 3:16 pm
Posts: 35
Thanks, Frank. You have a good memory -- it's been a year now. I understand your approach. Here is what I did:

I have two POJOs, Question and Answer. One question can have multiple answers.

So, for Question:
Public class Question implements Serializable {
private Integer id;
private String name;
private Set answers = new HashSet();
......
public Set getAnswers() {
return answers;
}

public void setAnswers(Set answers) {
this.answers = answers;
}
}

And Answer:

public class Answer implements Serializable {
private Integer id;
private String name;
private Question question;
......
}

Question.hbm.xml
.....
<set inverse="true" name="answers" cascade="all">
<key column="FK_QUESTION_ID" />
<one-to-many class="com.mytest.model.Answer" />
</set>

Answer.hbm.xml
.....
<many-to-one
class="com.mytest.model.Question"
name="question"
>
<column name="FK_QUESTION_ID" />
</many-to-one>

In order to initialize all Answers from Question, I use fetch join (Hibernate 3.x comes with lazy loading by default).

<query name="findQuestion">
<![CDATA[from Question q
inner join fetch q.answers
where q.questionId = :id
]]>
</query>

In my dao:

session().evict(q);
session().save(q);

There is a new Question record in the database with a new primary key and all other attributes (it does copy!). But in Answer table, it updated the record not create. When I look at the debug, I see there is "insert..." to Question table, but "update..." to Answer table.

Myt question is that the evict() might not evict the Answer object in answers collection. Or there is something I did wrong. Please advise.

Thanks,
J


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 16, 2005 11:44 am 
Regular
Regular

Joined: Thu Apr 29, 2004 5:08 pm
Posts: 56
Location: Montreal, Quebec, Canada
Here are my observations:

First of all, evict cascades just like delete does. You have a one-to-many (a set) between questions and answers.

When you session.delete a question, you will realize that you will end-up with a FK constraint violation. This is because the cascade style for your Question.answers colletion is set to cascade="all" instead of cascade="all-delete-orphans". Orphans being the answers having no parent (question) anymore. For more information about the difference with the "all" and "all-delete-orphans" cascading style, see section "22.3. Cascading lifecycle" of HB3 documentation.

Ok, all this to say that the links between question and answers are removed now, not the answers theirself. I would make a new try with evict using this new cascading style.

Second,

Quote:
In my dao:

session().evict(q);
session().save(q);


I don't understand why you would evict an entity just before saving it. Evict removes it from the cache, and remove associated entities with cascading style "all" and "all-delete-orphans".

My guess is this:

You evict q, since you have cascading style "all" and not "all-delete-orphans", the answers aren`t evicted. You end up having answers in the session cache, pointing to an entity which isn`t in the cache anymore (question).

Now you save q, first of all, the object is put in the session cache! You just got it out, why? Anyway, now, the session reassociate q. Somewhere you flush or commit (or you use an native id generator so Hibernate has to save the entity immediatly) and this is what happens.

Remember q isn`t in cache before the call to save, however, answers are. You save q, you flush the session.

- q is associated to session (put in cache)
- q cascades to answers (cascading style "all" or "all-delete-orphans" includes "save-update").
- answers are found in cache, hibernate update them.

Why? I don`t exactly remember but the reason is defendable.

However the whole point here is why do you evict just before saving? Why do you evict at all? What is your use case? You use a single session for the whole life of your application and you manage its cache with evict?

Anyway, try to change the cascading style to "all-delete-orphans" and to evict after you save, not before.

Frank

_________________
- Frank


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 16, 2005 7:17 pm 
Beginner
Beginner

Joined: Tue Jun 22, 2004 3:16 pm
Posts: 35
Thanks, Frank. Here is the use case -- I want to duplicate or copy a perisitent object to a new one. That means I retrieve a Question (by id) from the database, copy it to a noew Question object and save it to the database. So the database table will have two "identical" Questions but different primary key. The Answer collection will be duplicated too. In Answer table, there will be a new sets of answers that associated with that Question.

I simplified the sample code I posted. I am using Spring framework. So:

session().evict(q);
session().save(q);

is really are:

getHibernateTemplate().evict(q);
getHibernateTemplate().save(q);

They are in different session. I hope I evict "q" object and "a" in the collection and calling save() will duplicate q and a.

I changed cascade="a" to "all-delete-ophan" and get:

Don't change the reference to a collection with cascade="all-delete-orphan" exception.

Thanks,

J


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 17, 2005 11:13 am 
Regular
Regular

Joined: Thu Apr 29, 2004 5:08 pm
Posts: 56
Location: Montreal, Quebec, Canada
I still don`t understand why you would evict an object you are about to save.

However, when you copy the object, make sure the collection of answer isn`t shared (same instance) between the original and copied question object.

You cannot simply do

Code:
questionCopy.setAnswers( question.getAnswers() )

_________________
- Frank


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 17, 2005 2:06 pm 
Beginner
Beginner

Joined: Tue Jun 22, 2004 3:16 pm
Posts: 35
Thanks again Frank. The reason I am evict() and save() is trying to make a copy of an exsiting object that is persistent. Say I have a Question Q in db, I want to duplicate is as Q1 and save it back to db. So I evict() it and save it later. Thus the db will have an exact same record except for the oprimary key. I searched in this forum, people are talking about 'copy" or "clone" but have not found any good example.

Here are two I think are interesting:

1) this one says can not evict() collection element
http://forum.hibernate.org/viewtopic.ph ... collection

2) this one says it could
http://forum.hibernate.org/viewtopic.ph ... ct+cascade

They are all from Hibernate team members.

J


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 17, 2005 2:24 pm 
Regular
Regular

Joined: Thu Apr 29, 2004 5:08 pm
Posts: 56
Location: Montreal, Quebec, Canada
Oh ok, now I get the point.

You want a copy in the database, but not in the object model (you are re-using an instance).

Code:
session.evict( q ) // remove from session
// change primary key in some way (or set it back to initial unsaved-value)
session.save( q );

First, evict doesn't reset ids automatically, so you have to do it for the question, and their answers.

Second, I suspect the collection itself to be cached in the second level cache or something.

I personally implemented duplicate method that was creating a new instance, and duplicating the instance just like I wanted. Of course, this works fine.

I would like do some basic tests to see what happens in your case. I see the set of answers in question isn`t lazy right? This means that no collection interceptor is used by hibernate. The problem is many details of configuration may affect what,s happening (unsaved-value, shared collection, cached collection in second level cache, proxied collections) so I'm not sure I could reproduce the exact same scenario.

Anyway, if critical, simply create a method that copy your objects.

[code]
Question duplicateQuestion( Question q )
{
Question newQuestion = new Question(); // create a new answer collection instance
// copy question properties

for each q.answers
newQuestion.addAnswer( duplicateAnswer( Answer a ) );

}
[/code]

But be sure I understand the value of reusing the instance. However you must know that even if you evict, hibernate may still have its proxy in place. Also, if you are changing the primary key, then you must use the "assigned" id generator.

Personally, I wouldn`t lose too much time on that. You can also override clone, however I don`t like doing so.




_________________
- Frank


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 17, 2005 4:22 pm 
Beginner
Beginner

Joined: Tue Jun 22, 2004 3:16 pm
Posts: 35
Thanks, Frank.

I noticed that you don't have to setQuestioId(null), if call evict(q) and save(q), the databse will have a new record of Question with a new Id. The problem is the Answer elements in answers collection. I have to set each Answer's id to null, and then there will be a new set of Answer that have the same value but new primary key.

getHibernateTemplate().evict(q);
q.setCollectionElementIdToNull();
getHibernateTemplate().save(q);

I implemeted this setCollectionElementIdToNull(). It iterates any collection in Question and set all the element in those collections with null id.

I just saw another user's post, interesting...http://forum.hibernate.org/viewtopic.ph ... 35#2247235. I am going to ask him for detail.

J,


Top
 Profile  
 
 Post subject: Re: session.evict generates update statements!
PostPosted: Thu Aug 06, 2009 1:11 pm 
Regular
Regular

Joined: Fri May 22, 2009 4:50 am
Posts: 59
Hi Frank,
I have a problem for which solution might be evict(). But I am not very sure of it. Can kidnly help me with that.
I am using hibernate and spring and all my DAOs extend HibernateDAOSupport.
I have an entity 'Order' which contains an attribute 'Quote' and Quote contains collections of 'Buy' and 'Sell' values.

<class name="Order" table="ORDERS" lazy="false">
<id name="objectId" column="ORDER_UUID">
<generator class="uuid.hex"/>
</id>
<!-- for Quote -->
<many-to-one name="Quote" class="Quote"
column="quote_id"
cascade="all"
unique="true"/>

</class>

<class name="Quote" table="QUOTE">
<id name="id" column="QUOTE_ID">
<generator class="sequence">
<param name="sequence">quote_sequence</param>
</generator>
</id>
<map name="sell" table="sell" sort="natural">
<key column="quote_id"/>
<index column="price"/>
---------
</map>
<map name="buy" table="buy" sort="natural">
<key column="quote_id"/>
<index column="price"/>
---------
</map>
</class>

As you must have noticed, I am using lazy="false" and cascade="all" in Order mapping. So my Order object has all its collection initialized.

Now problem is when I stress test my code to store and update orders(I am updating some other attribute of order which has nothing to do with these collections) it throws the following error:
Illegal attempt to associate a collection with two open sessions

I Suppose reason is, the collections are not versioned like objects so when the same collection is opened with multiple sessions HB doesn't know who's going to win, as mentioned by Costin Leau in the following thread,
http://forum.springsource.org/showthread.php?t=26782

Can any one please suggest me solution to it? It was wondering if evicting the order entity before calling un update would solve my problem and whether it is correct to do so.
Any help is appreciated.

Thanks


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