-->
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.  [ 4 posts ] 
Author Message
 Post subject: @Version causing INSERT instead of UPDATE using merge()
PostPosted: Mon Apr 02, 2007 4:15 am 
Newbie

Joined: Mon Apr 02, 2007 3:35 am
Posts: 4
Hi all,

Hibernate version: 3.2.1GA

Spring version:2.0.3

Name and version of the database you are using:MySQL version 5.x

Using JPA, I have added a @Version annotation to a 'private Integer version' field of my entity class.

When creating a new instance of the entity class, it will contain a null version field value, along with an id value (e.g. 1) and some other field value (e.g. firstName=="Peter").

The entity managers' merge() method is called and a new record is inserted, having the value 0 in the version column.

Later, when updating (e.g. changing the firstName to another value), a detached entity instance, having a null version field and an id of 1, is entered into the entitymanagers' merge() method, and upon commit a java.sql.BatchUpdateException (duplicate key) is thrown, because an insert is attempted instead of an update.

The problem I am trying to get a grip on is that, from my point of view, there should be an attempt to update a previously inserted record because there is already a record having id 1.

I thought that the entitymanager, under the hood, would find an attached instance of the record having an id value of 1, and copy all the values (except for the version value), from the detached instance, and finally commit the updated attached instance, rendering an incremented version value.

If I simply skip the version field (or at least the @Version annotation), the above procedure will of course work for both insert and updates using the merge method.

So, why is the @Version annotation causing me this headache?

/Peter Odéus


Top
 Profile  
 
 Post subject: JPA Standard references
PostPosted: Wed Apr 04, 2007 6:32 am 
Newbie

Joined: Mon Apr 02, 2007 3:35 am
Posts: 4
Hi,

I got the following following from ejb-3_0-fr-spec-persistence.pdf via http://jcp.org/aboutJava/communityprocess/final/jsr220/index.html

Quote:
3.4.2 Version Attributes
The Version field or property is used by the persistence provider to perform optimistic locking. It is
accessed and/or set by the persistence provider in the course of performing lifecycle operations on the
entity instance. An entity is automatically enabled for optimistic locking if it has a property or field
mapped with a Version mapping.

An entity may access the state of its version field or property or export a method for use by the application
to access the version, but must not modify the version value[17]. Only the persistence provider is
permitted to set or update the value of the version attribute in the object.

The version attribute is updated by the persistence provider runtime when the object is written to the
database. All non-relationship fields and properties and all relationships owned by the entity are
included in version checks.

The persistence provider's implementation of the merge operation must examine the version attribute
when an entity is being merged and throw an OptimisticLockException if it is discovered that
the object being merged is a stale copy of the entity—i.e. that the entity has been updated since the
entity became detached. Depending on the implementation strategy used, it is possible that this exception
may not be thrown until flush is called or commit time, whichever happens first.

The persistence provider runtime is only required to use the version attribute when performing optimistic
lock checking. Persistence provider implementations may provide additional mechanisms beside
version attributes to enable optimistic lock checking. However, support for such mechanisms is not
required of an implementation of this specification.[18]

If only some entities contain version attributes, the persistence provider runtime is required to check
those entities for which version attributes have been specified. The consistency of the object graph is not
guaranteed, but the absence of version attributes on some of the entities will not stop operations from
completing.


Particularly the 4th paragraph caught my attention, as it discusses the existence of a stale copy of the entity.

But can an entity be considered to be stale if it does NOT claim to belong to any particular version, i.e. being null.

Shouldn't the entitymanager's merge-method evaluate whether to insert or update, instead of blindly trying an insert when version field is null?

/Peter Odéus


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 05, 2007 6:44 am 
Newbie

Joined: Mon Apr 02, 2007 3:35 am
Posts: 4
I got the following from http://www.hibernate.org/hib_docs/entitymanager/reference/en/html_single/:
Quote:
Here is the exact semantic of merge():

if there is a managed instance with the same identifier currently associated with the persistence context, copy the state of the given object onto the managed instance

if there is no managed instance currently associated with the persistence context, try to load it from the database, or create a new managed instance
...


I take it (even if it is not written) that upon successful load from the database (see 3rd line of the quote), the state of the given object is copied onto the managed instance.

I propose that, in the presence of a null version field of the given object, combined with a successful load from the database, the state of the given object must be copied onto the managed instance, ultimately ending in an UPDATE and not an INSERT as it is today. And by "state" I mean every non-transient field except for the version field.

Comments, pointers?

/Peter Odéus


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 09, 2007 8:25 pm 
Newbie

Joined: Sun Apr 09, 2006 4:57 pm
Posts: 10
Location: Milan (Italy)
At page 413 of Persistence Java with Hibernate you find this statement:

An insertion also occurs if the instance you passed into merge() was a transient instance, not a detached object.

So, your first merge is managed as an INSERT, as you are "creating a new instance of the entity class".

You say that the second merge is done updating a detached object. But, in this case, the version should not be null. A detached object "was" persistent, so the version field should have a value (even 0, but not null). If version is null, you probably are actually creating a new transient object with the same id of a persistent object. But it is transient, so the merge will manage it using an INSERT (duplicate key!).

See page 528 to know when Hibernate assumes that an instance is an unsaved transient instance.

So, the only way to work, in my opinion, is first to get the instance to modify (using the find or the get method) and then to modify its properties inside the session (a part id and version, that are not to be modified). After flushing the session your old instance will be UPDATED.

It can be a boring procedure if you have to copy many properties, but you can always use the BeanUtils (Jakarta commons) to write an helper method to copy every property from the transient bean to the persistent one (a part version and id, of course).

I think that, in Hibernate, the standard merge should be mantained as it is, but a mergeNoVersion method (or something like that) should be available to simplify the very usual activity of "updating" an object starting from an XML file in a ripetitive way, knowing the id of the object to update but, of course, not knowing its version.


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