Override Optimistic Lock and persist stale entity in Hibernate

In real time application, There could be a scenario to tell the hibernate not to allow concurrent update of an entity. Optimistic lock can be used to accomplish it, It never locks the underlying record in the DB rather it needs either Version or Timestamp column to determine whether the entity is up-to-date or stale.

 

@Column( name = "VERS", nullable = false )
@Version

private Long vers;

Version column is being updated automatically whenever entity is been udpated. Hibernate uses this version column value to check for stale. Second concurrent process which is updating this entity will receive OptimisticLockException/StaleObjectException.

If you want stale object to be updated  by any cause, use the following Spring AOP approach.Configure the following beans in your spring-beans.xml

 <bean id="retryingTransactionInterceptor">    
  <property name="order" value="1" />
  </bean>  
  <aop:config>     
   <aop:aspect id="retryingTransactionAspect" ref="retryingTransactionInterceptor">         
     <aop:pointcut  id="servicesWithRetryingTransactionAnnotation" expression="execution( * examples.service..*.*(..) ) 
     and @annotation(examples.annotation.RetryingTransaction)"/>         
     <aop:around method="retryOperation" pointcut-ref="servicesWithRetryingTransactionAnnotation"/>     
   </aop:aspect> 
  </aop:config>

 

 
package examples.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target( {
 ElementType.METHOD
} )
@Retention( RetentionPolicy.RUNTIME )
public @interface RetryingTransaction {
 int repeatCount() default 20;
}

 

@Transactional
 @RetryingTransaction
 public Test merge( Test test ) {
  return testDao.merge( test );
 }

 

package examples;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import javax.persistence.OptimisticLockException;
import javax.persistence.Version;
import org.aspectj.lang.ProceedingJoinPoint;
import org.hibernate.property.BasicPropertyAccessor;
import org.springframework.core.Ordered;
public class RetryingTransactionInterceptor implements Ordered {
 private static final int DEFAULT_MAX_RETRIES = 20;
 private int maxRetries = DEFAULT_MAX_RETRIES;
 private int order = 1;
 public void setMaxRetries( int maxRetries ) {
  this.maxRetries = maxRetries;
 }
 public int getOrder() {
  return this.order;
 }
 public void setOrder( int order ) {
  this.order = order;
 }
 public Object retryOperation( ProceedingJoinPoint pjp ) throws Throwable {
  int numAttempts = 0;
  Exception failureException = null;
  Object obj = null;
  Object arg1 = null;
  do {
   numAttempts++;
   try {
    Object[] obje = pjp.getArgs();
    arg1 = (Test) obje[ 0 ];
    obj = pjp.proceed();
    return obj;
   } catch ( RuntimeException re ) {
    failureException = re;
    System.out.println( "OptimisticLockException:"
      + ( (OptimisticLockException) re.getCause() ).getEntity() );
    incrementVersion( arg1 );
   }
  } while ( numAttempts <= this.maxRetries );
  throw failureException;
 }
 private Object incrementVersion( Object entityObject ) throws IllegalAccessException,
  InvocationTargetException, NoSuchMethodException {
  Object idValue = null;
  for ( Field field : entityObject.getClass().getDeclaredFields() ) {
   BasicPropertyAccessor basicGetter = new BasicPropertyAccessor();
   if ( field.getAnnotation( Version.class ) != null ) {
    System.out.println( "Id value:"
      + basicGetter.getGetter( entityObject.getClass(), field.getName() ).get(
       entityObject ) );
    idValue =
      basicGetter.getGetter( entityObject.getClass(), field.getName() ).get(
       entityObject );
    basicGetter.getSetter( entityObject.getClass(), field.getName() ).set(
     entityObject, (Long) idValue + 1, null );
    break;
   }
  }
  return idValue;
 }
}

Above code catches the OptimisticLock ,increase the version value and persist the stale
entity . You can also achieve it using simple update query too. It is up to
you to take correct decision.