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.





Spring Security: Authenticate user on LDAP without manager/admin user

26 03 2009

In my previou post on spring security with LDAP, I had used a manager/admin user to authenticate a user.

You can use the following code to authenticate the user directly, without manager/admin user:

<ldap-server id="ldapServer" url="ldap://ldapserver:port"/>

  <beans:bean id="bindAuthenticator" class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
    <beans:constructor-arg ref="ldapServer" />
    <beans:property name="userDnPatterns">
      <beans:list>
        <beans:value>CN={0},ou=Users,dc=example,dc=com</beans:value>
      </beans:list>
    </beans:property>
  </beans:bean>




Spring Security : Use Ldap for authentication, and database for authorities

12 03 2009

If you need to use Ldap for authentication and database /repository for authorization with spring security, here is a sample:

LDAP authenticator configuration

<ldap-server id="appLdapServer" url="ldap://ldapserver:port/dc=example,dc=com" manager-dn="uid=admin,ou=system" manager-password="your-pwd" />

<beans:bean id="companyLdapAuthProvider" class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
  <custom-authentication-provider />
  <beans:constructor-arg>
    <beans:bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
      <beans:constructor-arg ref="appLdapServer"/>
      <beans:property name="userDnPatterns">
        <beans:list><beans:value>uid={0},ou=Users</beans:value></beans:list>
      </beans:property>
    </beans:bean>
  </beans:constructor-arg>
  <beans:constructor-arg>
    <beans:bean class="com.mycompany.web.security.MyAuthoritiesPopulator">
      <beans:constructor-arg ref="myUserServiceBean"/>
    </beans:bean>
  </beans:constructor-arg>
</beans:bean>

Authorities populator:

public class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator {

	private MyUserService userService;

    public MyAuthoritiesPopulator(MyUserService userService){
       this.userService = userService;
    }

	@Override
	public GrantedAuthority[] getGrantedAuthorities(DirContextOperations userData, String username) {
		Set userPerms = new HashSet();

        //get users permissions from service
		Set permissions = userService.getPermissions(username);

		for (MyPermission perm : permissions) {
				userPerms.add(new GrantedAuthorityImpl(perm.getName()));
		}
		return userPerms.toArray(new GrantedAuthority[userPerms.size()] );
	}
}




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 StoredFile(inputStream);
myRepository.save(fileContent );

Don’t forget to write a test:)





Unit Testing Repositories with DbUnit and Spring

28 05 2008
In this post, I will create a base class for repository tests. I used DBUnit to populate database configured in a testContext.xml. And I used Spring test annotations to wire test classes with dataSource and repositories.
Requirements
* Spring 2.5
* DbUnit 2.2.2
* Junit 4.4Here you are

import javax.sql.DataSource;

import org.dbunit.DataSourceDatabaseTester;
import org.dbunit.IDatabaseTester;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:*testContext.xml" })
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class })

public abstract class AbstractRepositoryTest {

 private IDatabaseTester databaseTester;

 private DataSource dataSource;

 @Autowired
 public void setDataSource(DataSource dataSource) {
     this.dataSource = dataSource;
 }

 @Before
 public void setUpDatabase() throws Exception {
    try {
      databaseTester = new DataSourceDatabaseTester(dataSource);
      databaseTester.setTearDownOperation(DatabaseOperation.DELETE_ALL);
      IDataSet dataSet = getDataSet();
      databaseTester.setDataSet(dataSet);

      databaseTester.onSetup();
  } catch (Exception e) {     e.printStackTrace();     throw e;  }
 }

 protected abstract IDataSet getDataSet() throws DataSetException ;

 @After
 public void tearDownDatabase() throws Exception {
   if (databaseTester != null) {
       databaseTester.onTearDown();
   }
 }

}

And here is a saimple example test for MyRepository.

import java.util.List;

import javax.sql.DataSource;

import org.dbunit.DataSourceDatabaseTester;
import org.dbunit.IDatabaseTester;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.csv.CsvURLDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.junit.After;import org.junit.Assert;
import org.junit.Before;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;

public class MyRepositoryTest extends AbstractRepositoryTest {

 private MyRepository myRepository;

 @Autowired
 public void setMyRepository(@Qualifier("myRepository") MyRepository myRepository) {
      this.myRepository = myRepository;
 }

 protected IDataSet getDataSet() throws DataSetException {
    // returns CVS files in the currenct directory
    return new CsvURLDataSet(getClass().getResource(""));
}

 @Test
 public void listMyXEntities() {
    List xList = myRepository.listXEntities();
    Assert.assertEquals(3, xList.size());
  }
}

I really like the simplicity that comes with annotations!

PS : In the AbstractRepositoryTest’s setUpDatabase() method, there is a try-catch. I added it because in  org.springframework.test.context.junit4.SpringMethodRoadie.java (v1.13), at line 335, there is a catch like :

catch (FailedBefore ex) {
}
So in case of any exception in set up, you loose it.




How to get Spring Context in HttpSessionListener

20 03 2007

You can access Spring application context in the HttpSessionListener :

public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
   ServletContext servletContext =   httpSessionEvent.getSession().getServletContext();

   WebApplicationContext appContext=   (WebApplicationContext) servletContext
     .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

   //Now we can get xServis from context
   IXServis servis = (IXServis )appContext.getBean("xServis");
   //do whatever you like with servis...}