Exception propagates after already caught - spring

I'm having the strangest thing happening and I can't figure out why. The best way to describe this is to provide a simplistic example:
#Service
#Transactional
public class Foo{
public ModelAndView delete(#ModelAttribute("abc") Long id) {
ModelAndView mav = new ModelAndView();
try {
getDaoService().delete(id); //Calls Bar.delete()
} catch (final Exception e) {
// Add a custom error message to the mav for the user to see
mav.getModelMap().addAttribute(blah, blah);
}
return mav;
}
}
#Service
#Transactional
public class Bar {
public void delete(final E entity) throws HibernateException {
if (null != entity) {
try {
sessionFactory.getCurrentSession().delete(entity);
} finally {
sessionFactory.getCurrentSession().flush();
}
}
}
}
In this particular case, I am trying to delete an object which has a constraint violation (ORA-02292). I expect the delete to fail because of this. When the delete fails, I wish to show the user an appropriate custom message.
Instead of being able to show the user a custom message, the call fails and displays the following to the screen:
org.springframework.transaction.UnexpectedRollbackException: Transaction
rolled back because it has been marked as rollback-only
When I use a debugger, I can see that the error is appropriately caught and that the ModelAndView object has the custom message inside of it. So, I have no clue why an exception is still being thrown after it has been caught and dealt with. Does anyone have insight into why this is happening?

On the #Transactional annotation, you can state whether or not to roll back your transaction due to a given exception using the noRollbackForClassName attribute. You can do it similar to this.
#Service
#Transactional(noRollbackForClassName = "java.lang.Exception")
public class YourClass {
...
}
However, note that just saying noRollbackForClassName = "java.lang.Exception" would mean it will not rollback for any Exception (or its subclasses), hence its not a good practice.
What you should do is, figure out what exception is actually thrown first (may be by printing out the e.getClass().getName()), then set that class name as the noRollbackForClassName value.
Reason wise, this is happening because if some exception is thrown while attempting to delete(), the current transaction is automatically marked as roll back only, and if it is attempted to be committed, the exception you see will be thrown. The way to get passed this is to explicitly state that this certain exception should not cause a roll back.

The issue is because once an exception is thrown, Spring internally marks the tx as rollback-only. This is completely separate from Java exception handling. You have several options:
Make sure your expected exception does not throw exceptions which extend RuntimeException; Spring only rolls back tx's when its a type RuntimeException (see this page, section 10.5.3). HibernateException extends RuntimeException, so that's why you're getting the rollback marker.
Run each tx in its own transaction by moving the transactional method to its own class and annotating it using #Transactional(propagation=Propagation.REQUIRES_NEW). Then each call will run in its own tx and will not affect the overall tx.
Use the noRollbackForClassName style venushka mentioned. But use with caution, for the reason mentioned.

The Exception is being thrown in Bar#delete and is caught in Foo#delete. There is a #Transactional annotation on Bar#delete which is crossed before the exception is caught. This inner transaction is participating in the outer transaction and so the entire transaction is marked for rollback.
To avoid this you could remove the #Transactional annotation for Bar#delete. This method is already called within the scope of the other transaction.

Add property "globalRollbackOnParticipationFailure" to the hibernateTransactionManager bean definition as follows.
<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="hibernateSessionFactory" />
**<property name="globalRollbackOnParticipationFailure" value="false" />**
</bean>

Related

Spring Transactional on method with multiple db operation

I'm little confused. I though #Transactional on method means all operations or none.
Say I have this method:
#Transactional
public void fewDbOpeations(){
calculation1();
myDao.saveResult();
calculation2();
myDao.saveResult();
}
Say calculation2() throw exception or my second call to myDao.saveResult goes wrong , what I see is the even though the whole method annotated with #Transactional the saving result after calculation1() call is successful.
That is my first interaction with database saved the records I want but the second one failed but I thought because the method is #Transactinal even the first call to save to database should be rolled back.
Do I miss something?
#Transactional (rollbackFor = Exception.class)
public void fewDbOpeations(){
calculation1();
myDao.saveResult();
calculation2();
myDao.saveResult();
}
Try using this as well and throw Exceptions.
It depends on how you handle exceptions and if there are still #Transactional annotated on those internal method calls.
To have "all or nothing" behaviour in fewDbOpeations(), make sure the followings for all the internal method calls :
Do not annotated with #Transactional(propagation=REQUIRES_NEW)
Do not catch the exception inside and not throw out. Throw RuntimeException or Error but not checked Exception (Assume you are using default setting).

Transaction rollback and save info

In the service layer, I have some method who have a transactional annotation.
#Transactional
public void process() throws ProcessPaymentException{
try{
.... do some operation
catch (ProcessPaymentException ppe) {
save db problem issue.
}
}
It seem like if there are a issue, there are roll back... and nothing is saved in the db...
ProcessPaymentException extend Exception
Is there a way to rollback the process in the try but do the save in the catch?
Edit
Nested transaction could be a solution if this link is ok
https://www.credera.com/blog/technology-insights/java/common-oversights-utilizing-nested-transactions-spring/
Existing answer of using ControllerAdvise should help in normal setup that incoming requests are coming through Spring MVC (i.e. through a Controller).
For cases that is not, or you do not want to tie your exception handling logic to Spring MVC, here are some alternatives I can think of
(Here I assume you want to rely on declarative transaction control instead of programmatically controlling transactions yourself)
Separate service/component to save error in different transaction.
In short, you can have a separate service, which create its own transaction by propagation REQUIRES_NEW. e.g.
#Service
public class FooService
#Inject
private ErrorAuditService errorAuditService;
#Transactional
public void process() throws ProcessPaymentException{
try{
.... do some operation
catch (ProcessPaymentException ppe) {
errorAuditService.saveErrorAudit(ppe.getErrorText());
throw ppe; // I guess you want to re-throw the exception
}
}
}
#Service
public class ErrorAuditService
#Transactional(propagation=REQUIRES_NEW)
public void saveErrorAudit() {
// save to DB
}
}
One step further, if the error handling it the same for different services, you may create an advise, which will be called when service method throws exception. In that advise, you can save the error in db (using ErrorAuditService), and rethrow the exception.
Because processes of try-catch are wrapped by the same transaction.
The transaction manager do rollback whenever an exception is thrown. So, not thing would be saved.
Is there a way to rollback the process in the try but do the save in the catch?
Yes. Create Exception Handler to save db problem issue after rollback.
this is the idea
#ControllerAdvice
public class HandlerName {
#ExceptionHandler(ProcessPaymentException.class)
public void saveDbIssue(ProcessPaymentException ex) {
// save db problem issue.
}
But it only works if u want to save static data.

Spring Data Jpa - Repository doesn't thrown exception when #transactional exists

I have the following classes:
public interface GarageRepository extends PagingAndSortingRepository<Garage, Integer> {}
public class GarageBO {
private GarageRepository garageRepository;
public void updateGarage(Garage garage) {
try {
garageRepository.save(garage);
} catch (Exception e) {
throw BoozinaExceptions.getCodeException(garage, e);
}
}
}
public class GarageFacade implements GarageService {
private GarageBO garageBO;
#Transactional
public void updateGarage(Garage garage) {
garageBO.updateGarage(garage);
}
}
Supposing that i'm trying to update a garage and an unique violation is throwed.
When i call updateGarage from GarageFacade with the #Transactional annotation, garageRepository doesn't throws any exception.
When i call the same method without the #Transactional annotation, garageRepository throws the unique violation exception and now i can convert the exception using BoozinaExceptions.getCodeException(garage, e);
This behavior happens because when i have the #Transactional annotation, the exception will be throwed when Spring jpa data execute the commit. This happens after GarageBO.updateGarage execution ok ?
But i need to convert the unique violation. How can i do that ?
How to handle spring exception after commit then convert to my exception ?
What you describe is the expected behavior. The transaction is committed after the method ends, the commit leads to the unique constraint violation.
Adding #Transactional to the service methods makes the transaction end after the call to the service method the transaction is committed. Removing it makes the transaction commit after the call to the repository method.
Also why do you have a BO a Facade and a Repository?! Basically the BO and Facade are the same IMHO.
TO fix your your problem have your GarageRepository extend JpaRepository instead of PagingAndSortingRepository and call the saveAndFlush method instead of save. This will execute the sql (not committing the transaction) and trigger a constraint violation exception.
Another solution is to instead of doing a try/catch in your BO create and Aspect which does the conversion. Saves you coding the try/catch each time you need it.

catch DB exception in JSF+EJB application

I'm using Glassfish 3.1 with JSF2 and EJB stateless to query and write an Oracle DB. The table the user wants to populate in this web application has a primary key. When the user tries to add a new record the ejb method invoking em.persist is called. Now, if the user tries to add a record that has an already used primary key value, I got an exception in the EJB.
I would like to pop up a message to the user indicating that an error in the database occurred but I can't figure out how the JSF managed bean could catch the EJB exception.
Is there any way?
EJB has the concept of system exceptions and application exceptions.
Runtime exceptions, like EntityExistsException are system exceptions. These will among others cause any transaction to be rolled-ed back and cause the EJB instance bean to be discarded (destroyed). Most importantly for your problem, they will be wrapped in an EJBException.
There is no magic surrounding catching these exceptions. Adjusting the code from Petr above,
the following will just work:
Backing bean:
#EJB
private DAOBean daoBean;
public void savePerson(Entity e) {
try {
daoBean.save(e);
} catch (EJBException e) {
FacesMessage message = new FacesMessage("entity is already exists.");
FacesContext.getCurrentInstance.addMessage(null, message);
}
}
EJB:
private EntityManager em;
public void save(Entity e) {
em.persist(e);
}
Note that you can retrieve the cause of the exception to see if was an EntityExistsException or not (omitted above for brevity).
Since you probably have no need to destroy your EJB instance for this case, a better pattern is to define your own exception that inherits from a RuntimeException and is annotated with the #ApplicationException with the rollback attribute set to true.
E.g.
#ApplicationException(rollback = true)
public class MyException extends RuntimeException {
public MyException(Throwable cause) {
super(cause);
}
}
Wrap your EntityExistsException in your EJB into this exception and throw and catch it.
I strongly advise you NOT to use error codes or boolean success/failure as a result. This is a well-known anti pattern and makes your code incredible error prone.
You can create a custom exception class. Let's say UserException with a enum values of possible exception option.
In you EJB you can define your methods as throwable. If you need throw a exception.
In your JSF-SiteBean you only need to use a simple try/catch.
Is exception from type UserException ... get enum reason ... etc.

How to use transaction advisors with annotations?

supose I have the following example:
#Transactional(propagation=Propagation.SUPPORTS, readOnly=true)
public class MyServiceImpl implements MyService {
...
#Transactional(propagation=Propagation.REQUIRED, readOnly=false)
public TransactionResponse addMyEntity(MyEntity e) throws SQLException{
...
}
...
}
And in my applicationContext:
<tx:annotation-driven transaction-manager="txManager" />
Ok, now I want to add an interceptor after the rollback of the transaction if an SQLException is thrown. How can I do this?
Thanks in advance
EDIT
I'll try to clarify what I'm trying to do:
I have a WS, that persists some information on a DB and returns a response (an isOk boolean and an errorMessage if something went wrong)
Whenever an exception is risen, I need to:
Rollback the transaction
Build the response with the error message and return it to the client.
It's like a try/catch around spring's transaction proxy
Use the order attribute of tx:annotation-driven, and of the interceptor you will be using. Thus you specify which one runs before the other. See the AOP AspectJ advice ordering section

Resources