I am writing some Advice and I want the Transactions to commit before it gets to the advice. It seems to work for Save and Delete, but when I update, it runs thorough the advice, and then throws the exception.
try {
retVal = pjp.proceed();
} catch (Exception e) {
PSPSFaultException fault = new PSPSFaultException(e);
pmLog.error(ERR_MSG, fault);
throw fault;
}
As you can see here, I am trying to Wrap the exceptions in our own Runtime exception.
I tried ordering:
<tx:annotation-driven transaction-manager="txManager" order="1"/>
and
#Around( "execution(* blah.blah.PersistenceProvider.*(..))")
#Order(value=2)
public Object persistenceWrapper(ProceedingJoinPoint pjp) throws Throwable {
but this does not seem to make any difference in the stack or functionality.
#Transactional(propagation = Propagation.REQUIRED)
public class PersistenceProviderImpl extends HibernateDaoSupport implements PersistenceProvider {
#Override
public void update(Object o) {
this.getHibernateTemplate().update(o);
}
Is there a way to get the update to fire before the advice? The transactions are at the dao level...
Ok, so you are doing 2 things wrong:
You should have smaler value near your advices and bigger value near
your transaction manager (higher priority for advices, executed
first - lower priority for transaction manager, executed later)
Yous should put #Order(1) annotation in front of whole #Aspect class (strange, but it works for me)
Something like this:
...
<tx:annotation-driven transaction-manager="txManager" order="200"/>
...
#Order(1)
#Aspect
public class MyAspect {
...
In my case the above advice was not working. I'm using compile time weaving with the Maven, so this might be the reason.
My goal was to handle database exceptions after the transaction was finished and wrap them into another exception.
I had to include in one of the Aspect files (I guess which one does not matter):
#DeclarePrecedence(value = "*..*TransactionException*, org.springframework.transaction.aspectj.AbstractTransactionAspect, *..*Logging*, *")
Related
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.
i want to make somethings clear about #Transactional spring annotation.
Suppose i have written my code something as under,
case-1
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public void method1(){
.....method body....
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public String method2(){
try{
method1()
}
catch(Exce...){
}
}
case 2:
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public void method1(){
try{
}
catch(Excep..){
}
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public String method2(){
method1()
}
What would happen in case-1 if some exception occurs in excecution of method1, does it rollback transaction?
and what would happen for the same in case-2.
Thanx in advance
There will be no rollback in any of your use cases regardless of the exception type or propagation as long as you catch the exception in method2.
Why?
Spring creates a proxy class for the Transactional class or members, this is not visible for you. This proxy makes it possible to Spring to execute code before and after your function, basically open transaction, commit or rollback. This proxy is injected into your classes when you autowire them.
Of course the Transactional annotations only work when you're calling them through the proxy!
Your method1 call is always a local function call, so as long as you catch the exception in method2 it won't cause any rollback.
But please read the documentation.
You are free to specify. Using annotations for instance you can write like this:
#Transactional(rollbackFor=Exception.class, timeout=10)
Default behavior is not to rollback when an exception is thrown across the transaction demarcation line.
The Spring #Transactional default configuration (as you used it here) is rolling back only for java.lang.RuntimeException s and the propagation is REQUIRED.
So in case you are calling method2():
CASE 1: no rollback as you are catching in method2() java.lang.Exception (which is the parent of java.lang.RuntimeException)
CASE 2: same - as you are catching in method1() java.lang.Exception
The meaning of the propagation here is that when you've started a transaction if you encounter another annotation of #Transaction another transaction is not started and the application uses the "outer" transaction. So do not expect that the rollback logic will get executed before the method2() finishes. Anyway, one may assume from your snippet that the methods are in the same Spring Bean so calling a method internally in a bean (method2() calls method1() here) the AOP annotation are not "active" meaning that the f/w will not even "see" the #Transactionl on method1().
EDIT
Question changed so that the rollback is for all java.lang.Throwable s. So the answer is the same except for the fact that if an java.lang.Error is thrown then there is a rollback in both cases after the method2() is finished.
I did'nt get proper understanding from existing answers I have fully form-based web-application in which i have one spring controller for each form.
common base-class having code of varible(singleton object) declaraion of all service class instances(to-avoid declaring bo member variable in every controller,for business sevices interaction),which is parent for all controllers.
Common base-class having code of variable(singleton object) declaration of all dao service instances,which is parent for all BO's classes.
Common base-class having code of variable(singleton object) declaration of HibernateTemplate,SessionFactory using #Autowired annotaions.Which is parent for all dao classes.One parent for all of above three classes which is having Logger object instances.
One fbisExceptionHandler.java which captures exceptions from all #RequestMapping methods from all controllers as bellow
#ControllerAdvice
public class ExceptionControllerAdvice
{
#ExceptionHandler(Exception.class)
public ModelAndView exception(Exception e)
{
ModelAndView mav = new ModelAndView("exception");
mav.addObject("name", e.getClass().getSimpleName());
logger.error( e.getMessage())
mav.addObject("message", e.getMessage());
return mav;
}
}
please answer my questions
1) Is this single method fine for handling all exceptions .i,e those getting caught in controller,service classes,dao classes ?
2)why my all hibernate dao's methods not using either try..catch block or throws declaraion at methods signature....Is hibernate dao's did'nt throw any exception ? If so where they caught ?
for example one of my hibernate dao is
3)How all constarints from db vendors are handled ? Is there any hibernate inbuilt way ?
4) why my all my bo's methods not using either try..catch block or throws declaraion at methods signature.... where exactly exceptions being thrown and caught ?
My sample dao
package com.fbis.form18.daoimp;
all imports
#Repository
public class Form18daoImp implements Form18Dao{
#Autowired
SessionFactory sessionFactory;
HibernateTemplate hibernateTemplate;
#Autowired
public void setSessionFactory(SessionFactory sessionFactory)
{
this.hibernateTemplate=new HibernateTemplate(sessionFactory);
}
public void save(Form18Dtls form18dtls) {
sessionFactory.getCurrentSession().save(form18dtls);
logger.info("Form18Dtls saved successfully");
}
#Override
public Form18Dtls findbyFacId(String facID) {
#SuppressWarnings("rawtypes")
List list=hibernateTemplate.find("from Form18Dtls where FAC_ID=?", facID);
logger.info("findbyFacId ran successfully");
return (Form18Dtls)list.get(0);
}
all dao's methods are continued here..
}
My sample bo
package com.fbis.form18.boimp;
all imports
#Service("form18Bo")
public class Form18boImp implements Form18Bo
{
#Autowired
Form18Dao form18dtlsdao;
#Transactional
public void save(Form18Dtls from18dtls)
{
String UniqueTransactionNumber=getFacId(from18dtls.getFacDistrict(),"Form18_Dtls");
from18dtls.setFacId(UniqueTransactionNumber);
from18dtls.setSubmtdDate(new java.util.Date());
from18dtls.setAppStatus("I");
from18dtls.setFormId(23);
form18dtlsdao.save(from18dtls);
}
public String getFacId(String district,String tableName)
{
FacIdGenerator facid=new FacIdGenerator();
String genFacid=facid.factoryIdCreator("F18", district, tableName);
logger.info("getFacId ran successfully");
return genFacid;
}
#Override
public Form18Dtls findbyFacId(String facID) {
return form18dtlsdao.findbyFacId(facID);
}
other bo's continued here
If i am following wrong heirarkey of classes,suggestions are welcome,Thanks in advance
If I understand correctly, your question revolves around exceptions thrown from the data access layer, in this case you are using hibernate as your vendor.
If this is so then this is a pure java issue regarding checked exceptions and un-checked exceptions. Hibernate exceptions are un-checked (extend the RuntimeException) so there is no need to declare them on the method signatures and no need to catch them. On top of that, it is possible to tell Spring to transform specific vendor (e.g. hibernate) exceptions to Spring DataAccessException (see http://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/dao.html).
Regarding your numbered questions above:
Yes
As I said, hibernate throws RuntimeExceptions, you do not need to declare them and if you do not catch them, then your #ExceptionHandler will catch them and handle them.
I don't understand the question
The same answer as in number 2
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>
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