-->
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.  [ 7 posts ] 
Author Message
 Post subject: Map/Set updating every element on save
PostPosted: Mon Jan 29, 2007 1:53 pm 
Beginner
Beginner

Joined: Thu Dec 09, 2004 3:19 pm
Posts: 34
I started a topic a while back and I've narrowed the problem.

If you resave an in-memory object, Hibernate will UPDATE every associated Set/Map element even though they haven't changed.

If I load the object from Hibernate (replacing my in-memory version), then this extraneous UPDATE problem doesn't occur.

This seems like a bug, different saving behavior depending on whether it's the original object or one retrieved from the DB. But maybe it's by design and there's something I can do to clue in Hibernate to avoid this?

The work around is to always reload the object from the DB right after you create it for the first time, but that's something of a pain in my design as where I save objects has no idea if the object is new or old.

Thoughts?

Hibernate version:
2.1.6
Mapping documents:
<class name="com.ncube.nable.platform.contentstorage.Volume">
<id name="id" column="ID">
<generator class="native" />
</id>
<map name="files" table="FILES" lazy="false" cascade="all-delete-orphan" >
<key column="VOLUME_ID"></key>
<index column="FILE_NAME" type="text" />
<one-to-many class="com.ncube.nable.platform.contentstorage.cmdinfo.CSSFileInfo" />
</map>
</class>
<class name="com.ncube.nable.platform.contentstorage.cmdinfo.CSSFileInfo">
<id name="id" column="ID">
<generator class="native" />
</id>
<property name="path" />
<property name="size" />
<property name="permissions" />
<property name="lastModifiedTime" />
</class>

Code between sessionFactory.openSession() and session.close():
Volume v = new Volume( "volName", "/volPath" );
CSSFileInfo fileA = new CSSFileInfo( "a", "b", "c", "d" );
CSSFileInfo fileB = new CSSFileInfo( "e", "f", "g", "h" );
v.getFiles().put( fileA.getPath(), fileA );
v.getFiles().put( fileB.getPath(), fileB );
_hibSvc.beginTransaction();
_hibSvc.getSession().saveOrUpdate( v );
_hibSvc.commitTransaction();
_hibSvc.endSession();

System.out.println( "Resaving in-memory Volume" );

_hibSvc.beginTransaction();
_hibSvc.getSession().saveOrUpdate( v );
_hibSvc.commitTransaction();
_hibSvc.endSession();

System.out.println( "Resaving retrieved Volume" );

_hibSvc.beginTransaction();
Volume vLoad = (Volume)_hibSvc.getSession().load( Volume.class, v.getId() );
_hibSvc.getSession().saveOrUpdate( vLoad );
_hibSvc.commitTransaction();
_hibSvc.endSession();

Name and version of the database you are using:
HSQL 1.8.0 and also Oracle 10.

The generated SQL (show_sql=true):
09:47:24,905 [main] DEBUG SchemaExport:149 - create table MC.VS_VOLUMES (
CSS_ID bigint not null,
VOLUME_ID bigint not null,
primary key (CSS_ID, VOLUME_ID)
)
09:47:24,905 [main] DEBUG SchemaExport:149 - create table MC.ContentStorageServer (
ID bigint generated by default as identity (start with 1)
)
09:47:24,905 [main] DEBUG SchemaExport:149 - create table MC.Volume (
ID bigint generated by default as identity (start with 1)
)
09:47:24,905 [main] DEBUG SchemaExport:149 - create table MC.CSSFileInfo (
ID bigint generated by default as identity (start with 1),
path varchar(255),
size varchar(255),
permissions varchar(255),
lastModifiedTime varchar(255),
VOLUME_ID bigint,
FILE_NAME longvarchar
)
09:47:24,905 [main] DEBUG SchemaExport:149 - alter table MC.VS_VOLUMES add constraint FKFFE92CF7273D8460 foreign key (VOLUME_ID) references MC.Volume
09:47:24,905 [main] DEBUG SchemaExport:149 - alter table MC.VS_VOLUMES add constraint FKFFE92CF7770D78F7 foreign key (CSS_ID) references MC.ContentStorageServer
09:47:24,905 [main] DEBUG SchemaExport:149 - alter table MC.CSSFileInfo add constraint FK15D8874D273D8460 foreign key (VOLUME_ID) references MC.Volume
09:47:24,921 [main] INFO SchemaExport:160 - schema export complete

09:47:25,030 [main] DEBUG SQL:226 - insert into MC.Volume (ID) values (null)
09:47:25,061 [main] DEBUG SQL:226 - call identity()
09:47:25,077 [main] DEBUG SQL:226 - insert into MC.CSSFileInfo (path, size, permissions, lastModifiedTime, ID) values (?, ?, ?, ?, null)
09:47:25,077 [main] DEBUG SQL:226 - call identity()
09:47:25,077 [main] DEBUG SQL:226 - insert into MC.CSSFileInfo (path, size, permissions, lastModifiedTime, ID) values (?, ?, ?, ?, null)
09:47:25,093 [main] DEBUG SQL:226 - call identity()
09:47:25,093 [main] DEBUG SQL:226 - update MC.CSSFileInfo set VOLUME_ID=?, FILE_NAME=? where ID=?
09:47:25,108 [main] DEBUG SQL:226 - update MC.CSSFileInfo set VOLUME_ID=?, FILE_NAME=? where ID=?

Resaving in-memory Volume
09:47:25,108 [main] DEBUG SQL:226 - update MC.CSSFileInfo set path=?, size=?, permissions=?, lastModifiedTime=? where ID=?
09:47:25,108 [main] DEBUG SQL:226 - update MC.CSSFileInfo set path=?, size=?, permissions=?, lastModifiedTime=? where ID=?

Resaving retrieved Volume
09:47:25,124 [main] DEBUG SQL:226 - select volume0_.ID as ID0_ from MC.Volume volume0_ where volume0_.ID=?
09:47:25,155 [main] DEBUG SQL:226 - select files0_.VOLUME_ID as VOLUME_ID__, files0_.ID as ID__, files0_.FILE_NAME as FILE_NAME__, files0_.ID as ID0_, files0_.path as path0_, files0_.size as size0_, files0_.permissions as permissi4_0_, files0_.lastModifiedTime as lastModi5_0_ from MC.CSSFileInfo files0_ where files0_.VOLUME_ID=?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 29, 2007 2:34 pm 
Regular
Regular

Joined: Wed Dec 07, 2005 4:19 pm
Posts: 53
I may way off on this one.
This sounds like one of the 'identity' / hash problems. Here goes my wild theory:

On the save, Hibernate updates the Id values in objects in your map. BUT your hashCode() now probably returns a different value. As a result, nothing can be found in the exisiting map (Hibernate cache), and hence flush() reports those objects as 'dirty'.

When you load objects from database to begin with, they are inserted into the map including the Id, the 'right' value, and the hash map is thus 'valid'.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 29, 2007 2:51 pm 
Beginner
Beginner

Joined: Thu Dec 09, 2004 3:19 pm
Posts: 34
mbrunecky wrote:
I may way off on this one.
This sounds like one of the 'identity' / hash problems. Here goes my wild theory:

On the save, Hibernate updates the Id values in objects in your map. BUT your hashCode() now probably returns a different value.


Damn that would have been awesome if that were it :). Sadly, my hashcode implementations only refer to things that wouldn't change on save. The "id" isn't referenced at all in the hashcode implementations. They're really, really simple.

Volume.hashcode()
{
return new HashCodeBuilder() // HashCodeBuilder from Jakarta Commons
.append(_properties)
.toHashCode();
}

CSSFileInfo.hashcode()
{
return new HashCodeBuilder()
.append( _path )
.toHashCode();
}

We've also seen the "upates all map/set items despite no changes" on objects fetched via Hibernate that were written to database by a different (non-Java, non-Hibernate) process, so Hibernate doesn't even create the initial object in that case. I wasn't able to reproduce a simple test case though with that due to the dependency on the other app.

I figured this one had a small enoguh test case to be helpful in either exposing a bug or educating me on something I didn't understand with Hibernate.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 29, 2007 3:00 pm 
Regular
Regular

Joined: Wed Dec 07, 2005 4:19 pm
Posts: 53
Well, then
- I'd turn on Hibernate DEBUG log
- I'd add a Session Interceptor, and in the onFlushDirty(), onSave() look what Hibernate thinks has changed in your objects


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 29, 2007 3:08 pm 
Regular
Regular

Joined: Wed Dec 07, 2005 4:19 pm
Posts: 53
Hmm. Perhaps this is it:

Code:
_hibSvc.commitTransaction();
_hibSvc.endSession();

System.out.println( "Resaving in-memory Volume" );

_hibSvc.beginTransaction();
_hibSvc.getSession().saveOrUpdate( v );
_hibSvc.commitTransaction();


I am not sure what your _hibSvc.endSession(); does, but if it closes your Hibernate session, then your objects became detached.

Thean, assuming that hibSvc.beginTransaction(); re-opened some (new) Session, that Session knows nothing about your detached objects, so it will consider them all dirty - and will perform the update.

[Please, rate this posting if it helped]


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 29, 2007 3:24 pm 
Beginner
Beginner

Joined: Thu Dec 09, 2004 3:19 pm
Posts: 34
deleted - it double posted.


Last edited by ckessel on Mon Jan 29, 2007 3:27 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 29, 2007 3:25 pm 
Beginner
Beginner

Joined: Thu Dec 09, 2004 3:19 pm
Posts: 34
mbrunecky wrote:
Hmm. Perhaps this is it:

Code:
_hibSvc.commitTransaction();
_hibSvc.endSession();

System.out.println( "Resaving in-memory Volume" );

_hibSvc.beginTransaction();
_hibSvc.getSession().saveOrUpdate( v );
_hibSvc.commitTransaction();


I am not sure what your _hibSvc.endSession(); does, but if it closes your Hibernate session, then your objects became detached.

Thean, assuming that hibSvc.beginTransaction(); re-opened some (new) Session, that Session knows nothing about your detached objects, so it will consider them all dirty - and will perform the update.



My _hibSvc.endSession() is just calling the endSession() defined in the HibernateInAction HibernateService (just a session.close()).

That's not the problem though. Ending a session shouldn't cause it to completely re-UPDATE every item in the cascade hierarchy on the next save otherwise there's not much point to the cache.

Just to be sure though, I just tried (see below based on your comments) saving the loaded version twice in a row, ending the session between each save, and don't see the UPDATE problem. If it was just ending the session that caused it, that should show the same issue. I think it has something to do with the original object and the cache though. The cache seems to know what got loaded and if it's dirty, but maybe doesn't add the original object that got saved to the cache?

_hibSvc.beginTransaction();
Volume vLoad = (Volume)_hibSvc.getSession().load( Volume.class, v.getId() );
_hibSvc.getSession().saveOrUpdate( vLoad );
_hibSvc.commitTransaction();
_hibSvc.endSession();

_hibSvc.beginTransaction();
_hibSvc.getSession().saveOrUpdate( vLoad );
_hibSvc.commitTransaction();
_hibSvc.endSession();


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