-->
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.  [ 5 posts ] 
Author Message
 Post subject: Optimistic locking with a trigger - “Strange” behaviour
PostPosted: Wed Nov 25, 2015 4:54 am 
Newbie

Joined: Wed Nov 25, 2015 4:45 am
Posts: 3
I want to use the optimistic locking feature present in hibernate. To do this I configured the mapping for my table as follows:
Code:
<hibernate-mapping package="org.example.dao.entity">
    <class name="org.example.dao.entity.EmployeeEntity" table="employee" dynamic-update="false">
        <id name="id" column="employee_id">
            <generator class="identity"/>
        </id>
        <version name="version" column="version" type="java.lang.Integer" generated="always"/>
        <property name="firstName" column="first_name" type="java.lang.String"/>
        <set name="projects" table="employee_to_project" inverse="true">
                <key column="employee_id"/>
                <many-to-many column="project_id" class="ProjectEntity"/>
        </set>
    </class>
</hibernate-mapping>


I used generated="always" and created the following trigger in db:
Code:
CREATE OR REPLACE FUNCTION public.tab_employee_update_version()
  RETURNS trigger
  LANGUAGE plpgsql
AS
$body$
BEGIN
  NEW.version = coalesce(OLD.version,0) + 1;
  RETURN NEW;
END;
$body$
/


Then I run the following code:
Code:
public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");

        final EmployeeService employeeService = ctx.getBean("employeeService", EmployeeService.class);

        // SELECT
        Employee employee = employeeService.getById(1L);

        // UPDATE
        employeeService.update(employee);   
    }


and get the HibernateOptimisticLockingFailureException despite the fact that employee record was not changed by another transaction meantime:
Code:
Exception in thread "main" org.springframework.orm.hibernate4.HibernateOptimisticLockingFailureException: Object of class [org.example.dao.entity.EmployeeEntity] with identifier [1]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.example.dao.entity.EmployeeEntity#1]
    at org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:202)
    at org.springframework.orm.hibernate4.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:730)
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:592)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:485)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy15.update(Unknown Source)
    at org.example.AppMain.main(AppMain.java:25)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.example.dao.entity.EmployeeEntity#1]
    at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2541)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3285)
    at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3183)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3525)
    at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:159)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:584)
    ... 14 more


Transaction boundaries are on service level (the EmployeeService class is marked with the @Transactional annotation).

I debugged the code and found out, that hibernate automatically increases the version during the update:
Code:
Hibernate: select employeeen0_.employee_id as employee1_1_0_, employeeen0_.version as version2_1_0_, employeeen0_.age as age3_1_0_, employeeen0_.first_name as first_na4_1_0_, employeeen0_.last_name as last_nam5_1_0_ from employee employeeen0_ where employeeen0_.employee_id=?
10:36:46,258 TRACE main sql.BasicBinder:81 - binding parameter [1] as [BIGINT] - [1]
10:36:46,270 TRACE main sql.BasicExtractor:78 - extracted value ([version2_1_0_] : [INTEGER]) - [57]
10:36:46,270 TRACE main sql.BasicExtractor:78 - extracted value ([age3_1_0_] : [INTEGER]) - [0]
10:36:46,271 TRACE main sql.BasicExtractor:78 - extracted value ([first_na4_1_0_] : [VARCHAR]) - [Bogumil]
10:36:46,271 TRACE main sql.BasicExtractor:78 - extracted value ([last_nam5_1_0_] : [VARCHAR]) - [Bednarek]
10:36:46,280 TRACE main type.CollectionType:783 - Created collection wrapper: [org.example.dao.entity.EmployeeEntity.projects#1]

Hibernate: select projects0_.employee_id as employee1_1_0_, projects0_.project_id as project_2_2_0_, projectent1_.project_id as project_1_3_1_, projectent1_.name as name2_3_1_ from employee_to_project projects0_ inner join project projectent1_ on projects0_.project_id=projectent1_.project_id where projects0_.employee_id=?
10:36:46,394 TRACE main sql.BasicBinder:81 - binding parameter [1] as [BIGINT] - [1]
10:36:46,397 TRACE main sql.BasicExtractor:78 - extracted value ([project_1_3_1_] : [BIGINT]) - [1]
10:36:46,397 TRACE main sql.BasicExtractor:78 - extracted value ([name2_3_1_] : [VARCHAR]) - [Project1]
10:36:46,398 TRACE main sql.BasicExtractor:78 - extracted value ([employee1_1_0_] : [BIGINT]) - [1]
10:36:46,398 TRACE main sql.BasicExtractor:78 - extracted value ([project_2_2_0_] : [BIGINT]) - [1]
10:36:46,399 TRACE main sql.BasicExtractor:78 - extracted value ([project_1_3_1_] : [BIGINT]) - [2]
10:36:46,399 TRACE main sql.BasicExtractor:78 - extracted value ([name2_3_1_] : [VARCHAR]) - [Project2]
10:36:46,400 TRACE main sql.BasicExtractor:78 - extracted value ([employee1_1_0_] : [BIGINT]) - [1]
10:36:46,400 TRACE main sql.BasicExtractor:78 - extracted value ([project_2_2_0_] : [BIGINT]) - [2]

Hibernate: update employee set age=?, first_name=?, last_name=? where employee_id=? and version=?
10:36:46,426 TRACE main sql.BasicBinder:81 - binding parameter [1] as [INTEGER] - [0]
10:36:46,426 TRACE main sql.BasicBinder:81 - binding parameter [2] as [VARCHAR] - [Bogumil]
10:36:46,427 TRACE main sql.BasicBinder:81 - binding parameter [3] as [VARCHAR] - [Bednarek]
10:36:46,427 TRACE main sql.BasicBinder:81 - binding parameter [4] as [BIGINT] - [1]
10:36:46,427 TRACE main sql.BasicBinder:81 - binding parameter [5] as [INTEGER] - [58]


Additionally, I found out that if I select and update a record in the same transaction, hibernate does not increase the version and everything works fine.

Can somebody explain me, why hibernate increases the version and therefore I get the exception?

More details about my application:

springframework version: 4.2.0.RELEASE
hibernate version: 4.3.11.Final
postgreSQL version: 9.3


Top
 Profile  
 
 Post subject: Re: Optimistic locking with a trigger - “Strange” behaviour
PostPosted: Wed Nov 25, 2015 10:27 am 
Regular
Regular

Joined: Mon Oct 19, 2015 7:49 am
Posts: 61
Location: ChengDu China
The xml element <version/>(or "@Version") means that version field should be increased by Hibernate automatically, not by you. so please delete your database trigger.

If your application is web app, you send the the old version selected from database to the browser and storage it in hidden field, when the user finished the input and choose the submit(or ajax submit), the version must be send to server side and it must be assigned to the version field of the entity object which are going to be updated.

Hibernate did this to make sure an exception will be raised if a row of database has changed after you select it and before you update it again.


Top
 Profile  
 
 Post subject: Re: Optimistic locking with a trigger - “Strange” behaviour
PostPosted: Wed Nov 25, 2015 10:42 am 
Newbie

Joined: Wed Nov 25, 2015 4:45 am
Posts: 3
Without trigger it works fine.

I just wonder why with the trigger it does not work as expected. I set generated="always" as documentation says, so it should work:
Quote:
If the version number is generated by the database, such as a trigger, use the annotation @org.hibernate.annotations.Generated(GenerationTime.ALWAYS).


I made an additional checks and found that after removing the 'set' element from the mapping file, it works fine:
Code:
<set name="projects" table="employee_to_project" inverse="true">
    <key column="employee_id"/>
    <many-to-many column="project_id" class="ProjectEntity"/>
</set>


Top
 Profile  
 
 Post subject: Re: Optimistic locking with a trigger - “Strange” behaviour
PostPosted: Wed Nov 25, 2015 12:05 pm 
Regular
Regular

Joined: Mon Oct 19, 2015 7:49 am
Posts: 61
Location: ChengDu China
Oh... sorry, had't got your point

I'm not very sure, because I've not read the source code about this functionality of hibernate, so I can't help you
...but it looks like a bug, please submit you issue or bug by "https://hibernate.atlassian.net/projects/HHH/issues",

You'd better create a maven project to reproduce the issue/bug and upload it to be the attachment of your report,
this project should be as small as possible so that the Hibernate Team can handle it as fast as possible.

Here is an example sumbited by me: https://hibernate.atlassian.net/browse/HHH-10324


Top
 Profile  
 
 Post subject: Re: Optimistic locking with a trigger - “Strange” behaviour
PostPosted: Thu Nov 26, 2015 4:31 am 
Newbie

Joined: Wed Nov 25, 2015 4:45 am
Posts: 3
I created a bug for this issue: https://hibernate.atlassian.net/browse/HHH-10340

@babyfish Thank you for help.


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