Before this, please refer to Hibernate Interview Questions and Answers: with annotations and Spring framework , as the examples below are extension of this blog. Q. How would you go about implementing a dead lock retry service using Spring and hibernate? A.
|
Define a custom annotation class DeadlockRetry.java.
package com.myapp.deadlock;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface DeadlockRetry {
int maxTries() default 21;
int tryIntervalMillis() default 100;
}
Define the DeadlockRetryMethodInterceptor for performing retries. The above annotation will be bound to the following implementation via Spring.
package com.myapp.deadlock;Wire up the annotation and the interceptor via Spring config transactionContext.xml. Only the snippet is shown.
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.exception.LockAcquisitionException;
import org.springframework.dao.DeadlockLoserDataAccessException;
public class DeadlockRetryMethodInterceptor implements MethodInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(DeadlockRetryMethodInterceptor.class);
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object obj = invocation.getThis();
Method method = invocation.getMethod();
Method targetMethod = obj.getClass().getMethod(method.getName(), method.getParameterTypes());
DeadlockRetry dlRetryAnnotation = targetMethod.getAnnotation(DeadlockRetry.class);
int maxTries = dlRetryAnnotation.maxTries();
int tryIntervalMillis = dlRetryAnnotation.tryIntervalMillis();
for (int i = 0; i < maxTries; i++) {
try {
LOGGER.info("Attempting to invoke " + invocation.getMethod().getName());
Object result = invocation.proceed(); // retry
LOGGER.info("Completed invocation of " + invocation.getMethod().getName());
return result;
} catch (Throwable e) {
Throwable cause = e;
//... put the logic to identify DeadlockLoserDataAccessException or LockAcquisitionException
//...in the cause. If the execption is not due to deadlock, throw an exception
if (tryIntervalMillis > 0) {
try {
Thread.sleep(tryIntervalMillis);
} catch (InterruptedException ie) {
LOGGER.warn("Deadlock retry thread interrupted", ie);
}
}
}
//gets here only when all attempts have failed
throw new RuntimeException
("DeadlockRetryMethodInterceptor failed to successfully execute target "
+ " due to deadlock in all retry attempts",
new DeadlockLoserDataAccessException("Created by DeadlockRetryMethodInterceptor", null));
}
}
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->Finally, annotate the method that needs to be retried in the event of dead lock issues.
<!-- Deadlock Retry AOP -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="deadlockRetryPointcut" class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut">
<constructor-arg><null/></constructor-arg>
<constructor-arg value="com.myapp.deadlock.DeadlockRetry" />
</bean>
<bean id="deadlockRetryMethodInterceptor" lass="com.myapp.deadlock.DeadlockRetryMethodInterceptor" />
<bean id="deadlockRetryPointcutAdvisor"
class="org.springframework.aop.support.DefaultPointcutAdvisor">
<constructor-arg ref="deadlockRetryPointcut" />
<constructor-arg ref="deadlockRetryMethodInterceptor" />
</bean>
package com.myapp.service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
//....other imports
public class EmployeeServiceImpl implements EmployeeService {
private final EmployeeTableRepository employeeRepository;
private PlatformTransactionManager transactionManager;
public EmployeeServiceImpl(EmployeeTableRepository employeeRepository, PlatformTransactionManager transactionManager) {
this.employeeRepository = employeeRepository;
this.transactionManager = transactionManager;
}
@DeadlockRetry
public Employee saveEmployee(Employeee employee) throws RepositoryException {
TransactionStatus transactionStatus =
transactionManager.getTransaction(new DefaultTransactionDefinition(
TransactionDefinition.PROPAGATION_REQUIRED));
try {
employee = this.employeeRepository.saveEmployee(employee);
} catch (Exception e) {
transactionManager.rollback(transactionStatus);
throw new RepositoryException(e);
} finally {
if (!transactionStatus.isCompleted()) {
transactionManager.commit(transactionStatus);
}
}
return employee;
}
//....other methods omitted for brevity
}
The above example gives a real life example using a custom annotation and AOP (i.e. Aspect Oriented Programming).
Note: Also refer to Spring Interview Questions and Answers for explanation on interceptors.
Q. What are the pros and cons between Spring AOP and AspectJ AOP?
A. The Spring AOP is simpler as it is achieved via a runtime dynamic proxy class. Unlike AspectJ, it does not have to go through load time weaving or a compiler. The Spring AOP uses the proxy and decorator design patterns.
Since Spring AOP uses proxy based AOP, it can only offer method execution pointcut, whereas the AspectJ AOP supports all pointcuts.
In some cases, the compile time weaving (e.g. AspectJ AOP) offers better performance.