I've a service method called add() which is annotated with #Transactional.
I call it but when a ConstraintViolationException occurs inside corresponding DAO method it'll rollback the transaction even when I specify not to.
I expect that ConstraintViolationException to be caught and instead of it NotFoundException checked exception would be thrown.
#Override
#Transactional(noRollbackFor = ConstraintViolationException.class)
public User add(User user) throws NotFoundException {
try {
result = userDao.add(user);
} catch (RuntimeException e) {
throw new NotFoundException("Couldn't find group");
}
}
Is there a way to catch ConstraintViolationException without transaction rollback?
I'm using spring 3.1.1 and hibernate 3.6.
Ah, I see what happens. The ConstraintViolationException happens at commit time, after the method has been executed, when the transaction interceptor around your add() method tries to commit the transaction. Since it can't commit, obviously, the transaction is rollbacked. It can't to anything else.
Related
When using Spring Boot, I am unsure if error handling is already being taken care of by the Spring Framework, or if I have to implement it. For example, consider a controller method, which handles a DELETE request:
#DeleteMapping("/{studentId}")
public ResponseEntity<Long> deleteProfilePicture(#PathVariable Long studentId) {
return ResponseEntity.ok(profilePictureService.deleteprofilePictureByStudentId(studentId));
}
Is this fine, or should I instead wrap it inside a try-catch block:
#DeleteMapping("/{studentId}")
public ResponseEntity<Long> deleteProfilePicture(#PathVariable Long studentId) throws Exception {
try {
profilePictureService.deleteProfilePictureByStudentId(studentId));
} catch (DataAccessException e) {
throw new Exception("cannot delete profile picture of student: " + studentId);
}
}
Also: If I let my method deleteProfilePicture throw this Exception, who is handling the Exception? This must somehow be taken care of by Spring Boot, since it is possible to implement it without yielding any errors. Anyhow, what is the correct way of error handling in this scenario?
Spring Boot will turn the exception into an error response to the caller of the REST API. This does not mean that you shouldn't implement your own error handling logic, which you definitely should. As an example, you could use #ControllerAdvice to have a global exception handling for your application. Something along the following lines:
#ControllerAdvice
#Slf4j
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {Exception.class})
public ResponseEntity<Object> handleGenericExceptions(Exception exception, WebRequest webRequest) {
log.error("Handling: ", exception);
HttpStatus errorCode = HttpStatus.INTERNAL_SERVER_ERROR;
return this.handleExceptionInternal(exception, new ErrorInfo(errorCode.value(), "Unexpected Internal Server Error"), new HttpHeaders(), errorCode, webRequest);
}
}
You can read more about error handling in Spring Boot at https://www.baeldung.com/exception-handling-for-rest-with-spring.
Please refer to the below snippet of code
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { Throwable.class })
#Override
public MyClass myMethod(myParams) {
try{
myRepo.saveAll(myEntityList);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(myUrl,requestParam,String.class);
if(responseEnity.getBody()!=null && StringUtils.equalsIgnoreCase(responseEnity.getBody(),"Failure")){
log.error("Rest Call Failed");
throw new RestClientException("Error in remote service");
}
}catch(Exception e){
e.printStackTrace();
}
}
Now in the console log, I can see RestClientException being thrown but myRepo.saveAll(myEntityList); doesn't rollback instead it is inserting data into DB.
Can anyone figure out the issue?
My requirement is that in RestServiceCall if I get a failure response then my current transaction should get rollback entirely.
Your transaction is not rollbacked because there is no exception thrown, you're catching it.
As #Sumit Ghost mentioned, you can log it and rethrow the exception
I am using spring's #ControllerAdvice and #ExceptionHandler for exception handling.
Any method throws custom exception from Controller and corresponding #ExceptionHandler handle it. If Runtime exception occurs(eg any HibernateException) then it will throw Runtime Exception and I dont have any #ExceptionHandler for RuntimeExceptions.
My question is how to handle any runtime exception? do I need to add #ExceptionHandler for every Exception that is thrown by controller?
I dont want create an Generic ExceptionHandler for Exception.class because I have to send different error code according to exception occured.
one way to do it add try catch block in Controller and then throw the custom exception from catch block?
or is there any another better way?
All #ExceptionHandlers are inside #ControllerAdvice class.
the alternative is don't catch Exception in Controller. catch all Exception in service layer and throw a custom Exception eg. if you persisting a record failed, throw DatabaseException with message. see below method:
Student persist(Student object){
try{
studentDao.insert(object);
}catch(HibernateException e){
throw new DatabaseException("database operation failed!");
}
return student;
}
from you exception handler method you can get the message. this way you can set different message on different Exception.
#Transactional
public User getUser(String username) {
return userDAO.getUser(username);
}
I am using spring #Transactional annotation in my project.Now if transaction is rolled back i want to catch a notification so that i can show it to the user.How is it possible?
If the transaction fails, that indicates an exception was thrown. You will have to catch the Exception from the place (mostly the controller) where you are calling this method public User getUser(String username) and in the catch you can set some error code/ error message into the request and use it to display the message on UI.
In our application we are applying spring declarative transactions using annotations at service layer.
Here i am not getting any idea on how to handle exceptions properly.
What exactly my requirement is when dao layer throws any hibernate exception we are rolling back the transaction, but in one case i am getting InvalidDataAccessResourceUsageException because there is a unique index violation happening. So what i would like to do here is i want to catch InvalidDataAccessResourceUsageException exception at service class and have to rethrow the application specific exception to controller.
But whats happening here is as we have transaction demarcation at service layer class the session is flushing at service layer(ie when tx commits) after executing the method, as a result i cant catch it into the same method and it is directly propagating to the controller.
Please suggest me work around on this.
also seeking one more clarification, suppose i have a method like below
#Transactional(value="transactionManager",readOnly = false, propagation = Propagation.REQUIRED,rollbackFor = HibernateException.class)
public SomeDTO editObject(SomeDTO someDto, String user) throws EditException {
try{
/*
call to dao.edit();
another call to anotherDao.addEditsTOAnotherTable();
some business logic*/
} catch(HibernateException e){
} catch(InvalidDataAccessResourceUsageException ie){}
}
Can i catch exceptions as above. Note: I am not handling or throwing any exceptions from dao. Also there is no session cache mechanisms like FlushMode.ALWAYS etc at dao layer as it will flush during tx.commit().
By default #Transactional will rollback for any RuntimeException, and since HibernateException is a RuntimeException , roll back will be done automaticaly and you don't have to add rollbackFor = HibernateException.class
You can handle Exception this way:
try{
}catch(InvalidDataAccessResourceUsageException e){
throw new YourApplicationExceptionNotUniqueIndex();
}
and :
YourApplicationExceptionNotUniqueIndex shoud extends RuntimeException that way you wil have a rollback at your sevice layer and you can catch the exception at your Controller .
Better to handle check all the database constraints before editing into database