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: Property-level optimistic-lock bindings
PostPosted: Wed Apr 06, 2005 3:02 pm 
Newbie

Joined: Wed Apr 06, 2005 11:57 am
Posts: 2
I am having issues with the new property-level optimistic-lock setting in conjunction with the class-level optimistic-lock="all" setting.

First, let me explain why I'm considering using the optimistic-lock="all" setting (as opposed to the optimistic-lock="version" setting) and why the property-level optimistic-lock setting looks appealing.

I have a legacy database which already has a "version" column in several tables. I'm writing a new application using Hibernate that will have to interact with the same database at the same time as the legacy application. The legacy application does optimistic locking in much the same way as Hibernate (with the <version> element). However, the database itself handles updating the value in this column whenever a record is inserted or updated. In fact, for SQL Server we use the 'rowversion' data type (equivalent to byte[8]), also known as 'timestamp', which is made precisely for this purpose.

If I use the <version> element to map this column to a property using Hibernate, then Hibernate will try to update this column, in which case SQL Server will give an error: "Cannot update a timestamp column". A similar error is given when trying to insert a value in this column. I know it sounds very strange, but it would be ideal if Hibernate allowed me to use the update="false" setting on my <version> element. I want it to be in the WHERE clause, but I don't want the column to be updated. After the update, the persistent instance will not have the correct rowversion value, but I think that's OK because I'm using session-per-application-transaction and the Session and any persistent objects will always be discarded after each update.

It seems my only alternative (aside from not using managed versioning) is to use optimistic-lock="all". As long as I use dynamic-update="true", my rowversion column will not be written to the database (as long as it hasn't changed). But, it will still be included in the WHERE clause check for optimistic locking. Indeed that seemed to work when I tried it. However, my only problem with this approach is that all columns are included in the WHERE clause. In some cases there are an awful lot of columns, so for efficiency I'd really rather not have them all included in each UPDATE operation.

So, when I saw the new property-level optimistic-lock attribute, it seemed perfect. I could turn off optimistic locking for all properties except my rowversion column. Unfortunately, it didn't quite work. I'm getting an "Invalid parameter binding(s)" error whenever I try to set optimistic-lock="false" for any properties if I have optimistic-lock="all" for the class. Shouldn't that work? It's generating the correct parameterized SQL query, but it's trying to put in too many parameters. Can that be fixed?

Hibernate version: 3.0 (31 March 2005)

Mapping documents: Employee.hbm.xml
Code:
<hibernate-mapping>
    <class name="com.company.Employee" table="EMPLOYEES"
        dynamic-insert="false" dynamic-update="true" optimistic-lock="all">
        <id name="id" column="EmployeeID" type="long">
            <generator class="native" />
        </id>
        <property name="firstName" column="FirstName" type="string" not-null="true" optimistic-lock="false" />
        <property name="lastName" column="LastName" type="string" not-null="true" optimistic-lock="false" />
        <property name="title" column="Title" type="string" not-null="false" optimistic-lock="false" />
        <property name="version" column="version" type="binary" insert="false" update="true" optimistic-lock="true" />
    </class>
</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():
Code:
        Employee employee = new Employee();
        session.load(employee, new Long((long)1));
        employee.setLastName("Last" + System.currentTimeMillis()); // Dummy update for testing
        session.saveOrUpdate(employee);


Full stack trace of any exception that occurs:
Code:
org.hibernate.exception.SQLGrammarException: could not update: [com.company.Employee#1]
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:59)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
    at org.hibernate.persister.entity.BasicEntityPersister.update(BasicEntityPersister.java:2013)
    at org.hibernate.persister.entity.BasicEntityPersister.updateOrInsert(BasicEntityPersister.java:1923)
    at org.hibernate.persister.entity.BasicEntityPersister.update(BasicEntityPersister.java:2163)
    at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:75)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:239)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:223)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:137)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:675)
    at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:293)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)
    at com.company.EmployeeTest.testSomething(EmployeeTest.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at junit.framework.TestCase.runTest(TestCase.java:154)
    at junit.framework.TestCase.runBare(TestCase.java:127)
    at junit.framework.TestResult$1.protect(TestResult.java:106)
    at junit.framework.TestResult.runProtected(TestResult.java:124)
    at junit.framework.TestResult.run(TestResult.java:109)
    at junit.framework.TestCase.run(TestCase.java:118)
    at junit.framework.TestSuite.runTest(TestSuite.java:208)
    at junit.framework.TestSuite.run(TestSuite.java:203)
    at junit.textui.TestRunner.doRun(TestRunner.java:116)
    at com.intellij.rt.execution.junit2.IdeaJUnitAgent.doRun(IdeaJUnitAgent.java:57)
    at junit.textui.TestRunner.start(TestRunner.java:172)
    at com.intellij.rt.execution.junit.TextTestRunner2.startRunnerWithArgs(TextTestRunner2.java:23)
    at com.intellij.rt.execution.junit2.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:97)
    at com.intellij.rt.execution.junit2.JUnitStarter.main(JUnitStarter.java:31)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:78)
Caused by: java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC]Invalid parameter binding(s).
    at com.microsoft.jdbc.base.BaseExceptions.createException(Unknown Source)
    at com.microsoft.jdbc.base.BaseExceptions.getException(Unknown Source)
    at com.microsoft.jdbc.base.BasePreparedStatement.validateParameterIndex(Unknown Source)
    at com.microsoft.jdbc.base.BasePreparedStatement.setObjectInternal(Unknown Source)
    at com.microsoft.jdbc.base.BasePreparedStatement.setString(Unknown Source)
    at org.hibernate.type.StringType.set(StringType.java:24)
    at org.hibernate.type.NullableType.nullSafeSet(NullableType.java:62)
    at org.hibernate.type.NullableType.nullSafeSet(NullableType.java:44)
    at org.hibernate.persister.entity.BasicEntityPersister.update(BasicEntityPersister.java:1988)
    ... 35 more


Name and version of the database you are using:
Microsoft SQL Server 2000 (8.00.760 (SP3))
Microsoft SQL Server 2000 Driver for JDBC (Service Pack 3 (2.2.0040 from 7/6/2004))

The generated SQL (show_sql=true):
Code:
Hibernate: select employee0_.EmployeeID as EmployeeID0_, employee0_.FirstName as FirstName0_0_, employee0_.LastName as LastName0_0_, employee0_.Title as Title0_0_, employee0_.version as version0_0_ from EMPLOYEES employee0_ where employee0_.EmployeeID=?
Hibernate: update EMPLOYEES set LastName=? where EmployeeID=? and version=?


Debug level Hibernate log excerpt:
Code:
13:53:40,871  WARN JDBCExceptionReporter:57 - SQL Error: 0, SQLState: 07009
13:53:40,871 ERROR JDBCExceptionReporter:58 - [Microsoft][SQLServer 2000 Driver for JDBC]Invalid parameter binding(s).
13:53:40,871 ERROR AbstractFlushingEventListener:277 - Could not synchronize database state with session


Top
 Profile  
 
 Post subject: version element and MS SQL Server rowversion
PostPosted: Tue May 31, 2005 12:50 pm 
Newbie

Joined: Wed Apr 06, 2005 11:57 am
Posts: 2
I am dissappointed that noone has responded to this post. However, my development team has managed to work out a solution to make the <version> element work with a 'rowversion' column in Microsoft SQL Server.

The solution is actually fairly simple...

1. Implement the org.hibernate.usertype.UserVersionType interface, newly available in Hibernate 3.0, to define a custom type corresponding to the MS SQL Server 'rowversion'.

2. Implement the org.hibernate.persister.entity.EntityPersister interface to define a persister that does not try to write the version column to the database (remember, MS SQL Server will throw an error if you try). For simplicity, extend the org.hibernate.persister.entity.BasicEntityPersister class.

3. Use the <version> element and set the 'persister' property in all class mapping files that need a version column for optimistic locking.

This solution has one caveat. Once a row has been written to the database, the value in the 'rowversion' column will be updated in the database, but it will not be updated in the hibernate persistent object. Therefore, it will not be possible to successfully update the object after the first time. The object must be retrieved again from the database before it can be updated.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 16, 2005 9:52 am 
Newbie

Joined: Wed Nov 16, 2005 9:37 am
Posts: 1
Could you please post your solution? I have the same problem with a legacy database I´m using.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 07, 2008 10:46 am 
Regular
Regular

Joined: Thu Mar 06, 2008 5:06 am
Posts: 68
Hi,
I know this topic is a little bit old, but I have the same problem. Unfortunately I have no idea how to implement a persister so that the version column will not be updated.
Can anybody post some hints or a solution?

Thank you very much!


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.