Optimistic Locking and Versioning in Hibernate

16 05 2009

What is optimistic locking?

Optimistic locking is a method used to prevent simultaneously access problem on the same entity for change, which does not lock on the database level, in order to maintain the correct data.

For example, if two user wants to update same entity in different transactions, when first one saves and then second one saves without getting updates from the first user’s change, who will be the winner?

Most of the web applications, like ours, has such cases: Two users retrieve and modify a data on the page, then first user saves the data a transcation and then second user modifies and saves the data in an another transacion. Here are 3 alternatives:
1. Last commit wins : Both updates are performed, the second user overwrites without seeing the first user changes and without any error message
2. First commit wins : (Optimistic Locking) The update is performed, but second user gets error message, requiring restart of the processes on the first users changes.

3. Merge conflicting update : The second user are prompted to merge changes.

Automatic Versioning

Hibernate provides automatic versioning for options 2 and 3, using a version field managed by hibernate.

Versioning with integer version

public class MyClass {
...
private int version;
...
}


<class name="MyClass">
<id ...>
<version name="version" column="VERSION" access="field">
...
</class>

Versioning with timestamp

public class MyClass {
...
private int lastModifyDataTime;
...
}


<class name="MyClass">
<id ...>
<timestamp name="lastModifyDataTime" column="LAST_MODIFIY_DATE_TIME" access="field">
...
</class>

Automatic versioning is handled by hibernate, you don’t need to update versions/timestamps.
Considering the example mentioned above, when second user saves, hibernate finds this user is working on the stale data and throws StaleObjectException.

You can catch this exception, and rethrow your own exception. By the way, if you’re using Hibernate with Spring, this exeception is wrapped with HibernateOptimisticLockingFailureException.

One more point, you should be aware of that hibernate ignores the versioning when getting object and updating fields and then version are in the same session.





Hibernate inverse

9 07 2008

I have seen that many people have a confusion with Hibernate’s inverse keyword. Maybe, the term itself is a bit confusing.

The keyword “inverse” indicates which end of a relationship should be ignored. inverse=true means that this side is the inverse side, that is, this side is ignored. inverse=false means that this side is not inverse, that is, this side is the owner of the relationship.

Actually, to decide inverse value, you should think about what you want to do in parent-child relationship. If your parent class contains child list and child class has a reference to parent, and you want the parent to have the responsibility of the relation, i.e, save, delete, etc. operations, then you need to set  inverse=false at the child list property in the parent’s mapping file.





Blob Objects With Spring and Hibernate

6 06 2008

You have a stream objects, i.e files , and you want to save them to the database using Hibernate and Spring.

First of all, I suggest to create a different object only for saving content. Let’s sayyou have files to save:

public class MyFileContent extends Entity {

  private InputStream content;

  MyFileContent () {
  }

  public MyFileContent(InputStream content) {
   this.content = content;
  }

  public InputStream getContent() {
    return content;
  }

  private void setContent(InputStream content) {
    this.content = content;
   }
}

Here Entity is just a base class including an id, and getter and setter methods for id.

Here your Hibernate user type for blobs.

import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.hibernate.HibernateException;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.orm.hibernate3.support.AbstractLobType;

public class BlobUserType extends AbstractLobType {

 public int[] sqlTypes() {
   return new int[] { Types.BLOB };
 }

 public Class returnedClass() {
   return InputStream.class;
 }

 protected Object nullSafeGetInternal(ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
  throws SQLException, HibernateException {
   return lobHandler.getBlobAsBinaryStream(rs, names[0]);
 }

 protected void nullSafeSetInternal(PreparedStatement ps, int index, Object value, LobCreator lobCreator)
  throws SQLException, HibernateException {
   if (value != null) {
    lobCreator.setBlobAsBinaryStream(ps, index, (InputStream) value, -1);
   } else {
    lobCreator.setBlobAsBytes(ps, index, null);
   }
 }
}

And here, your hibernate mappping for MyFileContent class.

<class name="MyFileContent" table="MY_FILE_CONTENT">
 <id name="id" column="ID" type="java.lang.Long" unsaved-value="null">
 <generator class="sequence">
 <param name="sequence">S_MY_FILE_CONTENT</param>
 </generator>
 </id>
 <property name="content" column="CONTENT" type="BlobUserType"/>
</class>

Here, hibernate configuration possibly in your HibernateContext.xml

 <bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />

 <!-- Hibernate SessionFactory -->
 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" ..>
   <property name="lobHandler" ref="defaultLobHandler"/>
    ...
 </bean>

For Oracle and Websphere; you need to use this lob handler definition instead of defaultLobHandler. Because of differences in Oracle’s Blob objects.

 <bean id="oracleLobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler" >
   <property name="nativeJdbcExtractor" >
       <bean class="org.springframework.jdbc.support.nativejdbc.WebSphereNativeJdbcExtractor"/>
   </property>
 </bean>

And finally use it :

InputStream inputStream = ...;
MyFileContent fileContent = new MyFileContent(inputStream);
myRepository.save(fileContent );

Don’t forget to write a test:)








Follow

Get every new post delivered to your Inbox.