Spring DAO Repository exception handling - java

In Spring Docs there's a statement that #Repository annotation enables an "exception translation" but no additional details available.
What is this feature about and how it works?

Anything the DAO throws will be unchecked. Don't catch anything in the DAO. If you catch exceptions in the DAO or within the service method, Spring won't know to rollback the transaction. Configure exception-handling at the controller layer (using an exception handler, not with try-catch), that is where the data access exceptions will usually be caught, because there is nothing to do to handle them except to log them.

That means if you are using Spring Abstraction for JDBC, JPA/Hibernate or JDO then you don't have to implement JDBC or RDBMS vendor specific error handling. So Spring wraps all those exceptions and then wraps them into DataAccessException class. When you want to switch to different persistence technology, you don't have to worry about refractoring your code.

Related

Patterns for exception handling in Spring Boot using beans or AOP

I have a Spring Boot server application where exceptions are caught, logged and handled when possible and when not possible they are handled by #ControllerAdvice as the last resort.
I want to add more logic for exception handling, for example, sending a metric to a monitoring solution on each exception. For example there's a Spring bean MetricsBean which is a service which sends metrics. Using MetricsBean is easy for errors which are handled by #ControllerAdvice because it's also a Spring bean.
However using MetricsBean is not as straightforward in such code:
try {
// business logic
} catch (Exception ex) {
logger.error(ex)
}
because that would mean manually autowiring MetricsBean into each service class. Also logger object is not a Spring bean.
So I have a dilemma how do I send a metric from inside or around logger.error() method when the logger class itself is not a Spring bean. I'm wondering what the best practice is in such cases. Here're some of the options:
Should logger also be a Spring bean? (seems tedious and error-prone)
Should there be a custom Exception extending class which would be a Spring bean and will send a metric inside its constructor? (seems tedious and error-prone)
Should Spring AOP be used with the pointcut being logger.error() method?
I'm surprised that there's not an established solution for this pattern for such an established framework as Spring (most of the solutions focus on handling errors in #ControllerAdvice).
I'd like the solution to be as generic as possible (point 3 seems to win there) but perhaps there's a pattern which I'm missing.
Not sure if it is the best practice but I would use a UtilityClass as a facade exposing a static method. In that method, wire the metrics registry using Metrics.globalRegistry to avoid injection. This will avoid coupling the metric details from where it is used.
You can then either invoke the static method everywhere you are handling an exception (so besides logger.error(ex)) or combine this with your third option with the pointcut.
Simple example:
#UtilityClass
public class ExceptionUtility {
public static void handleException(Throwable throwable) {
Counter.builder("some.name.exception")
.tag("name", throwable.getClass().getName())
.register(Metrics.globalRegistry)
.increment();
}
}
Usage:
try {
...
} catch (RestClientException restClientException) {
ExceptionUtility.handleException(restClientException);
}

Does Spring handle Hibernate NonUniqueObjectException?

I'm using Spring MVC to write a RESTful web service, with a database back-end. My save routine is wrapped in a try/catch ...
private void saveData() {
try {
service.saveReport(xmlData);
}
catch(DataAccessException e) { // Spring's DataAccessException
throw new MyException();
}
}
which works for DB errors like a unique constraint violation for instance.
But I'm getting Hibernate's NonUniqueObjectException currently, due to a bug in my code.
I'm curious however, why doesn't Spring's DataAccessException "catch" the NonUniqueObjectException? Is there another Spring Exception which I should use (in addition to DataAccessException) to "catch" this Hibernate Exception?
Or I can certainly catch the Hibernate exception myself. But I didn't know if Spring would/could.
UPDATE -------
My service.save(xmlData) actually does more than just "save" but first does some processing, copying the XML data into the correct (list of) #Entity beans, then looping through the Entity beans, saving each one. The Hibernate Exception occurs during setting up the Entity beans themselves, not during the actual dao.save(entityBean).
Thanks!
Chris
Your DAO neeeds to be proxied to allow for Spring automatic exception translation. Either add the #Repository annotation to your DAO or explictly configure a PersistenceExceptionTranslationPostProcessor bean post processor to your configuration

How to catch the DAO Service Layer Exception cleanly in Spring Data JPA

Would like to check on the DAO Service Layer Implementation for Exception Handling in Spring Data JPA.
The functional module which is using the DAO service layer interface isn't a GUI, hence we need the DAO Service layer expception to be caught transparently (Some error message to be written to the log file). The functional module isn't going to take any action for DAO layer exception, hence I want to catch the exceptions by the DAO service layer itselves..
If all you want to do is log the exception, then you could use an Advice which would log exceptions in DAO lays, something like:
#Aspect
public class ExceptionLogginAspect {
#AfterThrowing(value = "execution(* x.y.dao.*(*))", throwing = "ex")
public void afterThrowing(Exception ex){
//log exception here
}
}

Bulk import and #Transactional

I'm developing an import module to load in a bulk way from an excel file. I'm using spring to manage the transactional context and spring-data-jpa like DAO, to read a excel file I'm using POI.
#Service
public class ImportService{
#Autowired
UserRepository userRepository;
#Autowired
UserDetailRepository userDetailRepository;
#Autowired
AddressRepository addressRepository;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Map<String,StringBuffer> import(byte[] bytesUploaded) {
wb = new XSSFWorkbook(new ByteArrayInputStream(bytesUploaded));
XSSFSheet mySheet = (XSSFSheet) wb.getSheetAt((short)0);
for(Row row: mySheet){
if(row.getRowNum() >= 2 ){
readRowFromExcel(row);
}
}
}
#Transactional(propagation = Propagation.NESTED,rollbackFor=Exception.class)
private void readRowFromExcel(Row row) {
try{
User user = readUserFromFile(row);
UserDetail = readUserDetailFromFile(row,user);
Address address = readAddressFromFile(row,user);
//A few check to verify the corretly load of the entities
userDetailRepository.save(userDetail);
addressRepository.save(address);
userRepository.save(user);
}
catch (Exception e){
//Do something
}
}
}
I would want that if an exception occurred during read or the save of the row nothing become persistent, however an internal exception mustn't cause rollback the outer transaction.
How can I manage this kind of situation?
Am I facing the problem correctly?
Can you give me some suggestion about this (I'm new of spring)?
Thanks
A good start is the Transaction management section of the Spring framework reference documentation
There are a few things in your sample code that needs improvements:
Declarative transaction management
See section 11.5.1
The most important concepts to grasp with regard to the Spring Framework’s
declarative transaction support are that this support is enabled via AOP
proxies, and that the transactional advice is driven by metadata (currently
XML- or annotation-based). The combination of AOP with transactional metadata
yields an AOP proxy that uses a TransactionInterceptor in conjunction with
an appropriate PlatformTransactionManager implementation to drive transactions
around method invocations.
What that means typically is that calling your private method will not honour the transaction demarcation as you expect. Calling other methods of your service internally won't work either by default as it does not go through the proxy. In your case, Using #Transactional provides additional useful references.
The #Transactional on readRowFromExcel seems useless as you have the guarantee that a fresh transaction will be created before calling import.
Exception handling
In order for the AOP proxy to manage an exception thrown during a transactional method, you have to actually make sure the exception is thrown outside the method boundary so that the proxy can see it. In the case of your example, the do something part should make sure to throw the appropriate exception. If you want fine-grained management of the exception, you should probably define another exception type than Exception.
Wrapping up
If you want to read your excel file and save the content to your store using Spring data, you should probably have a single public annotated method whose purpose is to read an item and store it using Spring data. This would need some refactoring of your API. If the excel sheet is fairly large, you should also consider Spring Batch to process it.
What would be the purpose of the outer transaction here? You should also consider failure scenario such as when one particular row cannot be read (or written to the store). Are you leaving the file half-processed or are you flagging this error somewhere?

How to call a custom rollback method in Spring Transaction Management?

Environment: Spring 3, Custom Transaction Management, JDBC Transactions
I just read the Spring docs on using the transaction template to handle transaction management. It seemed overly complex so I want to ask:
Most of my transactions are JDBC related, meaning I just declare an #Transactional on my service. But now I am making a REST service call to another site which needs to rollback if any of the following JDBC operations fail, I'll provide the rollback code in this case.
As I progress in my method, in my transaction - I want to save a reference to the REST service call (needed to roll back that action), and upon exception I just want a method myCustomRollback() called which can access the previously stored object.
Why not just provide a map in the transactionTemplate for storing stuff and define a custom rollback method on the #Transactional annotation?
This is the way I think about it, I'm not following the way Spring thinks about this. Can someone help me bridge the gap between what I want and how I accomplish it most efficiently in Spring? I only need to do this for a few special case operations.
To anyone still reading this:
I solved a similar problem with spring events - as suggested by Den Roman in option 3.
Here's the basic idea (scenario is fictional):
Whenever I perform external operations that need to be rolled back together with the transaction, I publish an event inside my #Transactional method using support from spring (org.springframework.context.ApplicationEventPublisher):
#Transactional
public String placeOrder(Order order) {
String orderId = orderServiceGateway.createOrder(order);
applicationEventPublisher.publishEvent(new OrderCreatedEvent(orderId));
workflowService.startWorkflow(orderId);
return orderId;
}
The event itself can be any object - I created a POJO with details about the remote entity to be deleted.
Then I registered a special event listener that is bound to a transaction phase - in my case to the rollback:
#TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollBackOrder(OrderCreatedEvent orderCreatedEvent) {
String orderId = orderCreatedEvent.getOrderId();
orderServiceGateway.deleteOrder(orderId);
}
Of course, it's recommended to catch & log the exception from rollback operation, not to lose the original exception from the placeOrder() method.
By default these events are synchronous, but they can be made async by additional configuration.
Here's a very good article on this mechanism, including detailed configuration and pitfalls: Transaction Synchronization and Spring Application Events (DZone)
While I don't like the solution 100% because it clutters the business logic with event publishing stuff and binds to spring, it definitely does what I expect it to do and makes it possible to pass context from the transactional method to the rollback method - which is not available through a traditional try/catch block outside of the transactional method (unless you put your context in the exception itself, which is not very nice).
I've re-read your question a few times and am not sure I understand your question completely. I assume your executing someCode and if that fails you would like to execute myCustomRollback which has some information about someCode. So I'll try to provide a Generic answer.
If you want spring to rollback some code. It will only rollback that which is rollBackAble, like jdbc transactions. Assume you have a method which performs 2 calls.
#Transactional
public void doStuff(SomeEntity entity, File file) {
persist(entity);
customFileService.createOnFileSystem(file);
throw new RunTimeException();
}
So the code above will always rollback. It will undo the persisting of your entity, but not the creation of your file, since that is not managed by Spring transactions, unless you provide custom implementation for it to be.
Second, Spring provides 2 ways of working with transactions:
Spring AOP: a proxy is created at runtime which will decorate your code with transactional stuff. If your class would be named MyClass, then Spring will create a class names MyClassProxy, which will wrap your code in transactional code.
AspectJ: at compile time your .class file will be adjusted and transactional code will be embedded inside your method.
The aspectJ approach seems harder to configure, but isn't so much and is way easier to use. Since anything which is annotated with #Transactional will be embedded (weaved) with code. For Spring AOP this is not the case. Transactional inner method calls for instance in Spring will be ignored! So aspectJ provides a more intuitive approach.
Back to what I think your question is (the code is all in 1 class):
public void doSomeCode() {
Object restCall = initialize();
try {
execute(restCall);
} catch (CustomException e) {
myCustomRollback(restCall; e);
}
}
#Transactional(rollbackFor = CustomException.class)
private void execute(Object restCall) throws CustomException {
// jdbc calls..
restCall = callRest(restCall);
throw new CustomException();
}
void myCustomRollback(Object restCall, CustomException e) {
...
}
The code above will only work with AspectJ! Since your making inner method calls which also seems to be private! AOP at runtime cannot handle this.
So what happens is everything (which is rollbackAble) in execute will be rollbacked. And in doStuff you have information about the objects which were used in execute, you now can use in myCustomRollback to rollback your REST stuff manually.
Not sure if I answered this question properly, but I hope it helps someone with a similar problem.
1 solution is to implement your own transactional manager by extending a one
2 solution is to use TransactionSynchronizationManager class
3 solution is to use #TransactionalEventListener in case you have Spring 4
Spring transaction management the default behavior for automatic rollback is for unchecked exceptions
so for a custom exception,
#Transactional(rollbackFor = CustomException.class, noRollbackFor = RuntimeException.class)
public void doSomething(...
)
the transaction be rolled back if it there is an exception that matches the specified. If an exception not matches, it is propagated to caller of the service or TransactionRolledBackException wrapper
if you use use the org.springframework.transaction.PlatformTransactionManager it is more manageable handling exceptions than template
check the documentation http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html
you can use the AfterThrowing advice (when an exception is thrown) & call your method (myCustmRollback()) there, you can use TransactionSynchronizationManager class to get thecurrent transaction & roll it back...
alternatively.. you can use the AroundAdvice to begin & commit/rollback your transaction (this way you can use the spring provided transaction manager by using the TransactionSynchronizationManager class)

Categories