Back Forum Reply New

Transaction Manager appears to be eating the runtime exception?

mit(AbstractPlatformTransactionManager.java:701)
at org..transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:321)
at org..transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:116)
at org..aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org..aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at $Proxy28.deleteGroup(Unknown Source)
at com.mycompany.service.GroupManagerTest.deleteGroup(GroupManagerTest.java:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org..test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:160)
at org..test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:233)
at org..test.context.junit4.SpringMethodRoadie$RunBeforesThenTestThenAfters.run(SpringMethodRoadie.java:333)
at org..test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:217)
at org..test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:197)
at org..test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:143)
at org..test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:160)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
at org..test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:97)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:253)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:237)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:146)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org..orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:655)
... 31 more
Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`mino_authentication/group_members`, CONSTRAINT `group_members_group_fk` FOREIGN KEY (`group_id`) REFERENCES `group` (`group_id`) ON UPDATE CASCADE)
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1666)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1082)
at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
... 39 moreIf I remove @Transactional from my service layer all exceptions propagate up normally.

So this concludes that the transaction manager is eating the exception.
How will you ever know if the transaction was successful or not using annotations?

So this concludes that the transaction manager is eating the exception.

It has nothing to do with the TransactionManager eating the exceptions. The exception occurs AFTER your method finishes, so your catch is useless, actually all the catches you have are useless. Which you could also deduct if you would check the stacktrace and inspect your error message on why the unit test fails.

pseudo runtime executed code with @Transactional in place

Code:
try {  start transaction   execute service method commit transaction lt;-- fails here NOT inside service method
catch (Exception e) { rollback transaction convert exception to DataAccessException throw converted exception
}
So don't catch the exceptions, and check for a DataAccessException. If you don't want that, use an after-throwing advice to convert the DataAccessException to something you want (in your case a DeleteException).

Marten,

Thanks for your reply.

I can catch the DataIntegrityViolationException in the caller (my unit test).
However I wanted the Service layer to be the start of the transaction and was hoping i could convert the DataIntegrityViolationException into my own custom exception, which would be thrown to the caller of the service (my unit test).

If i move the @transactional from the Service layer to the Dao layer I am able to catch the DataIntegrityViolationException in the service and then wrap it up there.

I guess I was looking for keeping the @transactional in the Service layer.

What i don't understand is why the exception occurs after the service layer makes the call?  The Dao layer should be the first responder to the runtime exception.  Would Propagation.REQUIRES_NEW solve the problem?

What i don't understand is why the exception occurs after the service layer makes the call? The Dao layer should be the first responder to the runtime exception. Would Propagation.REQUIRES_NEW solve the problem?

For this single simple case it would solve it, but for larger more complicated service methods it would make things worse. Especially if you have multiple data access methods. Imagine 3 database calls each have a requires_new, first 2 succeed last one fails, no way to rollback first 2 because they are already committed.

If i move the @transactional from the Service layer to the Dao layer I am able to catch the DataIntegrityViolationException in the service and then wrap it up there.

See explanation above.

However I wanted the Service layer to be the start of the transaction and was hoping i could convert the DataIntegrityViolationException into my own custom exception, which would be thrown to the caller of the service (my unit test).

You can still do that by writing a after-throwing advice which converts your own exception.  that way it is even a reusable component.

What i don't understand is why the exception occurs after the service layer makes the call? The Dao layer should be the first responder to the runtime exception. Would Propagation.REQUIRES_NEW solve the problem?

Have you actually looked at the psuedo code I have written.

The exception occurs when there is a commit NOT when you do delete. You are using hibernate which is basically delaying (correctly!!!) everything until it is forced to synchronize the state of the session with the database. In this case the commit.

I suggest a book on hibernate and a book on Aspect Oriented Programming (transactions are applied using Aspects).

I guess I was looking for keeping the @transactional in the Service layer.

Which is the correct layer and which should also be the transactional layer/boundary of your application.

Marten,

I actually missed that part in the pseudo code.  Thanks for your help, I understand the flow now and have everythign working.

I originally decided not to use Propagation.REQUIRES_NEW for the same reason you discussed.

-phil
¥
Back Forum Reply New