I'm implementing some retry handling in my methods using Spring Retry.
I have a Data Access Layer (DAL) in my application and a Service Layer in my application.
My Service layer calls the DAL to make a remote connection to retrieve information. If the DAL fails it will retry. However, if the number of retries fails I would like to rethrow an exception.
In my current project I something very similar to this:
#Configuration
#EnableRetry
public class Application {
#Bean
public Service service() {
return new Service();
}
}
#Service
class Service {
#Autowired
DataAccessLayer dal;
public void doSomethingWithFoo() {
Foo foo = dal.getFoo()
// do something with Foo
}
}
#Service
class DataAccessLayer {
#Retryable(RemoteAccessException.class)
public Foo getFoo() {
// call remote HTTP service to get Foo
}
#Recover
public Foo recover(RemoteAccessException e) {
// log the error?
// how to rethrow such that DataAccessLayer.getFoo() shows it throws an exception as well?
}
}
My Application has a Service and the Service calls DataAccessLayer getFoo. If getFoo fails a number of times the DAL will handle the retries. If it fail's after that I'd like my Service layer to do something about it. However I'm not sure how to let that be known. I'm using intelliJ and when I try to throw e; in the #Recover recover method I don't get any warnings that DataAccessLayer.getFoo throws any exceptions. I'm not sure if it will. But I'd like the IDE to warn me that when the retries fail a new exception will be thrown to let the Service layer know to expect it. Otherwise if it calls dal.getFoo it doesn't know to handle any errors. How is this typically handled? Should I not use the AOP declarative style and go for imperative?
You can change getFoo() (and recover()) to add throws <some checked exception> and wrap the RemoteAccessException in it (in recover()).
That will force the service layer to catch that exception.
I try save list of entities to Oracle Db.
#Transactional
public void save() {
//logick
for (QuittanceType quittanceType : quittance) {
quittancesService.parseQuittance(quittanceType);
}
//logick
}
On each step I call this method:
#Transactional
#Override
public void parseQuittance(QuittanceType quittance) {
try {
//logick create payToChargeDb
paymentToChargeService.saveAndFlush(payToChargeDb);
} catch (Exception e) {
log.warn("Ignore.", e);
}
}
and method
#Override
public PaymentsToCharge saveAndFlushIn(PaymentsToCharge paymentsToCharge) {
return paymentToChargeRepository.saveAndFlush(paymentsToCharge);
}
When I try save entity with constraint My main transaction rollback and I get stacktrace:
Caused by: java.sql.BatchUpdateException: ORA-02290: CHECK integrity constraint violated(MYDB.PAYMENTS_TO_CHARGE_CHK1)
But I want skip not success entities and save success. I marck my method
#Transactional(propagation = Propagation.REQUIRES_NEW)
and it look like this:
#Transactional
#Override
public void parseQuittance(QuittanceType quittance) {
try {
//logick create payToChargeDb
paymentToChargeService.saveAndFlushInNewTransaction(payToChargeDb);
} catch (Exception e) {
log.warn("Ignore.", e);
}
}
and
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Override
public PaymentsToCharge saveAndFlushInNewTransaction(PaymentsToCharge paymentsToCharge) {
return paymentToChargeRepository.saveAndFlush(paymentsToCharge);
}
But when I try save entity with constraint I not get exception and not enter to catcj block. just stop working debugging and the application continues to work. I do not get any errors. and as if rollback is happening
The proxy created by #Transactional does not intercept calls within the object.
In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation (in effect, a method within the target object calling
another method of the target object) does not lead to an actual
transaction at runtime even if the invoked method is marked with
#Transactional. Also, the proxy must be fully initialized to provide
the expected behavior, so you should not rely on this feature in your
initialization code (that is, #PostConstruct).
https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative
The same documentation recommends use of AspectJ if you want this behaviour.
I have created an asynchronous service for a long running stored procedure call. Things work good but the transaction is not getting timed out after the specified value given in the timeout attribute of the transactional annotation..The structure of the code is given below (not the real one...just skeleton...ignore semantics/syntax)
//asynchronous service
#override
#async("myCustomTaskExecutor")
#Transactional(rollbackfor=Exception.class,timeout=600)
public void serviceMethod(){
//repository method is invoked.
repository.callStoredProcedure();
}
//Repository method in the Repository class
#Transactional(rollbackfor=Exception.class,timeout=600)
public void callStoredProcedure(){
//Stored procedure is called from the private method using hibernate doWork implementation.
privateCallmethod();
}
private void privateCallmethod() throws ApplicationException{
Session session = null;
try{
session = entityManager.unwrap(Session.class);
session.doWork(new Work(){
#Override
public void execute(Connection connection) throws SQLException {
OracleCallableStatement statement =null;
try{
//using hibernate 4.x and ref cursors are used...so went on with this approach..
//suggest if there is some better approach.
String sqlString =“{begin storProcName(?,?)}”;
statement = connection.prepareCall(sqlString);
statement.setInt(1,5);
statement.setString(2,“userName5”);
statement.executeUpdate();
}
catch(Exception e){
throw RunTimeException(e.getMessage);
}
finally{
if(statement != null)
statement.close();
}
}
}
});
}
catch(Exception e){
throw ApplicationException(e.getMessage);
}
//Not using Final block to close the session.Is it an issue ?
}
delay is happening in the stored procedure side(Thread.sleep(700) are not used) yet transaction is not timed out...
Questions :
I guess #Transactional is enough on the service method alone...give little bit insight on correct approach of using #Transactional annotation
for this code setup.
Will the #Transactional works for the JDBC calls inside the doWork Interface implementation...is that whats the issue is ?
Some article suggest to use oracle.jdbc.readTimeout or setQueryTimeout in the CallableStatement... Is it the right way to achieve this.
Kindly point out the mistakes and explain the causes
If #Transactional Annotated method is not the entry point to the class, it will not be transactional unless you enable load time weaving (Spring default is Compile time weaving) https://stackoverflow.com/a/17698587/6785908
You should invoke callStoredProcedure() from outside this class, then it will be transactional. If you invoke serviceMethod() which in turn invokes callStoredProcedure(), then it will not be transactional
I used setQueryTimeout() approach to resolve the issue as #Transactional timeout does not work with the hibernate dowork() method...I guess its due to the hibernate work executes in different thread and it low level JDBC methods to invoke the store procedures...
NOTE: This particular application uses very spring 3.x version and hibernate 4.x with JPA 2.0 spec...little outdated versions
I'm using Spring's 'HTTP Invoker' remoting solution to expose DAOs to many different applications, but have all database access in a single server.
This works well, but if the server throws, say, a HibernateSystemException, Spring serializes that and sends it over the wire back to the client. That doesn't work because the client doesn't (and shouldn't) have HibernateSystemException in its classpath.
Might there be a way to have Spring Remoting wrap my exception in something that I specify that would be common between client and server to avoid issues like this?
I know that I could do that in my server code by wrapping everything the DAO does in a try/catch, but that's admittedly sloppy.
Thanks,
Roy
I ran into this issue as well; I am exposing a service via HTTP Invoker that accesses a database using Spring 3.1, JPA 2, and Hibernate as the JPA provider.
To work around the problem, I wrote a custom Interceptor and an exception called WrappedException. The interceptor catches exceptions thrown by the service and converts the exceptions and causes to WrappedException using reflection and setters. Assuming the client has the WrappedException on its class path, the stack trace and original exception class names are visible to the client.
This relaxes the need for the client to have Spring DAO on its class path and as far as I can tell, no original stack trace information is lost in the translation.
Interceptor
public class ServiceExceptionTranslatorInterceptor implements MethodInterceptor, Serializable {
private static final long serialVersionUID = 1L;
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
return invocation.proceed();
} catch (Throwable e) {
throw translateException(e);
}
}
static RuntimeException translateException(Throwable e) {
WrappedException serviceException = new WrappedException();
try {
serviceException.setStackTrace(e.getStackTrace());
serviceException.setMessage(e.getClass().getName() +
": " + e.getMessage());
getField(Throwable.class, "detailMessage").set(serviceException,
e.getMessage());
Throwable cause = e.getCause();
if (cause != null) {
getField(Throwable.class, "cause").set(serviceException,
translateException(cause));
}
} catch (IllegalArgumentException e1) {
// Should never happen, ServiceException is an instance of Throwable
} catch (IllegalAccessException e2) {
// Should never happen, we've set the fields to accessible
} catch (NoSuchFieldException e3) {
// Should never happen, we know 'detailMessage' and 'cause' are
// valid fields
}
return serviceException;
}
static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
Field f = clazz.getDeclaredField(fieldName);
if (!f.isAccessible()) {
f.setAccessible(true);
}
return f;
}
}
Exception
public class WrappedException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String message = null;
public void setMessage(String message) {
this.message = message;
}
#Override
public String toString() {
return message;
}
}
Bean Wiring
<bean id="exceptionTranslatorInterceptor" class="com.YOURCOMPANY.interceptor.ServiceExceptionTranslatorInterceptor"/>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="YOUR_SERVICE" />
<property name="order" value="1" />
<property name="interceptorNames">
<list>
<value>exceptionTranslatorInterceptor</value>
</list>
</property>
</bean>
I can understand you don't want your clients to have HibernateSystemException in their classpath, but I'd argue they should, if you're using HTTPInvoker appropriately. It's not designed to be a service facade / interface layer: all it's meant to do is let you run Java methods on a remote JVM, using HTTP instead of RMI.
So if you really don't want the clients to have a dependency on Hibernate, your try/catch block is the way to go. (I'd argue against that though, since it'll make debugging a pain: your stack trace will now be divided between the client and the server).
I haven't used it myself but you could try the org.springframework.remoting.support.RemoteExporter.setInterceptors(Object[]) method to add an aspect to catch that particular exception in just one place rather than adding try/catches all over the place.
I would argue a try/catch in a Facade layer in front of your DAOs is exactly what you want, in order to gain full control over the exceptions you return. I agree it initially feels ugly, but it's a vital layer between client and DAO, in my opinion.
You might even return a OperationStatus object of some sort, rather than use void return types, to convey both outcome (worked, didn't) and error message, for store-data API calls.
I used a solution similar to N1H4L's but with AspectJ.
First I made all the exceptions I want the client to be aware of to extend the class BusinessException (which in my case is a very simple subclass of RuntimeException in the jar with the service interface and DTOs).
Since I don't want the client to know much about the internals of the service I just say "Internal server error".
package com.myproduct.myservicepackage;
import com.myproduct.BusinessException;
import org.aspectj.lang.*;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class InternalServerErrorExceptionAspect {
#Pointcut("execution(public * com.myproduct.myservicepackage..*Service.*(..))")
public void publicServiceMethod() {}
#Around("publicServiceMethod()")
public Object hideNonBusinessExceptions(ProceedingJoinPoint jp) throws Throwable {
try {
return jp.proceed();
} catch (BusinessException e) {
throw e;
} catch (RuntimeException e) {
e.printStackTrace();
throw new RuntimeException("Internal server error.")
}
}
}
Here's the BusinessException class:
package com.myproduct.BusinessException;
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 8644864737766737258L;
public BusinessException(String msg) {
super(msg);
}
}
I was using AspectJ to wrap exception but it does not work for exception that occur in Spring proxy, e.g. annotation #Transactional when the connection to the database fails.
However, the method setInterceptor on RmiServiceExporter works perfectly.
I'm using Spring 2.5 transaction management and I have the following set-up:
Bean1
#Transactional(noRollbackFor = { Exception.class })
public void execute() {
try {
bean2.execute();
} catch (Exception e) {
// persist failure in database (so the transaction shouldn't fail)
// the exception is not re-thrown
}
}
Bean2
#Transactional
public void execute() {
// do something which throws a RuntimeException
}
The failure is never persisted into DB from Bean1 because the whole transaction is rolled back.
I don't want to add noRollbackFor in Bean2 because it's used in a lot of places which don't have logic to handle runtime exceptions properly.
Is there a way to avoid my transaction to be rolled back only when Bean2.execute() is called from Bean1?
Otherwise, I guess my best option is to persist my failure within a new transaction? Anything else clean I can do?
This is one of the caveats of annotations... your class is not reusable!
If you'd configure your transactions in the XML, if would have been possible.
Assuming you use XML configuration: if it's not consuming expensive resources, you can create another instance of bean2 for the use of the code you specified. That is, you can configure one been as you specified above, and one with no roll back for exception.