i want to get Exception on method through Spring aop, and the method annotate a #Transactional annotation.
when i insert a record that is in the DB, it would be throw Exception, but aop can't catch the
Exception. Is double proxy problem ? outer proxy is Transaction, inner proxy is aop ?
any idea idea ?
#Override
#Transactional
public boolean doInsert(Object domainObject) throws Exception {
boolean success = false;
if (beforeInsert(domainObject)) {
if (insert(domainObject)) {
success = afterInsert(domainObject);
}
}
return success;
}
#AfterThrowing(value = "execution(* com.digiwin.newb2.patterns.Nb2ProgramService.doInsert(..))", argNames="retVal", throwing="retVal")
public void doInsertThrowException(Exception ex) {
int index = 0;
}
when insert a record throw exception but not trigger aop...
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement
at org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:143)
at org.springframework.orm.hibernate4.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:680)
at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:562)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
at com.digiwin.newb2.test.ProgramSingle02TestService$$EnhancerByCGLIB$$8eea6179.doInsert(<generated>)
at com.digiwin.newb2.patterns.SysSingle01Controller.saveActionCore(SysSingle01Controller.java:486)
at com.digiwin.newb2.patterns.SysSingle02Controller.saveActionCore(SysSingle02Controller.java:870)
at com.digiwin.newb2.patterns.SysSingle01Controller.saveAction(SysSingle01Controller.java:410)
Related
I couldn't find a good solution: In my Spring Boot app, as an #ExceptionHandler method, I need to define a handler not for a specific exception, but for any exception caused by a specific exception (i.e. a wrapped exception).
Example: Sometimes I get this:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:541) ~[spring-orm-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746) ~[spring-tx-5.1.4.RELEASE.jar:5.1.4.RELEASE]
... 121 common frames omitted
Caused by: custom.TechRoleException: My custom TechRoleException
at myapp.method1[..]
at myapp.methodOuter[..]
My custom TechRoleException is an exception I throw inside some Hibernate EventListener's pre-update method, and the direct exception is that Persistence couldn't occur.
However, the following method that tries to use my custom exception is never reached:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(TechRoleException.class)
public String techRoleException(HttpServletRequest request, Exception ex) {
System.out.println("Got here");
return "home";
}
}
Here's a similar thread where the answer is wrong and didn't answer this question:
#ExceptionHandler for Wrapped Exception / getCause() in Spring
Maybe something like that?
#ExceptionHandler(Exception.class)
public String techRoleException(HttpServletRequest request, Exception ex) {
if(ex instanceof TechRoleException) {
System.out.println("Got here");
return "home";
} else {
throw ex; //or something else
}
}
My final working answer is to handle a General Exception, and then use Apache ExceptionUtils.getRootCause() to detect the specific Caused-By I'm looking for within this general handler.
(Other specific Exceptions won't come to this method if they have their dedicated Handlers. But if there's no dedicated Handler, the exception will come here.) This is the only way to detect some target Caused-By.
#ExceptionHandler(Exception.class)
public String handleGeneralException(HttpServletRequest request, Exception ex) {
Throwable rootCause = ExceptionUtils.getRootCause(ex);
if (rootCause != null && "com.myapp.TechRoleException".equals(rootCause.getClass().getName())
{
//... handle this Caused-By Exception here
// ...
}
// All other exceptions that don't have dedicated handlers can also be handled below...
// ...
}
I have the following method:
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public void applyLog(int codFilial, List<LogLojaCentralCompactoEntity> items) {
}
which internally calls:
#Override
#Transactional(noRollbackFor = PersistenceException.class)
public void apply(LogCompactoEntity entity) {
}
The second method has a try/catch a PersistenceException. The problem is the transaction rolls back then it reaches PersistenceException.
I know Spring #Transactional defaults to roll back in any unchecked exception, but I am explicitly telling noRollbackFor the PersistenceException.
Why its not working? Any way to threat it?
Thanks.
Edit - the try/catch method inside apply does this:
try {
insert();
}
catch(PersistenceException e)
{
update();
}
Edit2 - log:
Edit3 - exception handling part:
if (acao.equalsIgnoreCase("I")) {
try {
insertQuery.executeUpdate();
}
catch(PersistenceException e) {
int affected = updateQuery.executeUpdate();
if(affected == 0)
throw new LogApplyException("O insert falhou e o update não afetou registros.");
}
}
Edit4 - Some more info:
1) Removing #Transactional from apply, leaving #Transaction just on applyLog results on this exception:
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute statement
2) Same as 1, but adding noRollbackFor = PersistenceException.class (on applyLog) results on this exception:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
Edit 5:
#lzagkaretos solution is valid (Thank you), but I want to know how to handle this for future cases.
UPDATE
I think that relying in primary key violation exception in order to find if record should be inserted or updated is not something you should do. Another implementation you can use instead is finding before the execution if record is already saved in the database to perform an update, or not to perform an insert.
For example, if you can use spring data repositories, the code might seem like this.
public void apply(LogCompactoEntity entity) {
LogCompactoEntity logCompactoEntity = (entity.getId() == null) ? new LogCompactoEntity() : logCompactoRepository.findOne(entity.getId());
if (logCompactoEntity == null) {
logCompactoEntity = new LogCompactoEntity();
}
copyProperties(entity, logCompactoEntity);
logCompactoRepository.save(logCompactoEntity);
}
--
Maybe you shouldn't have a try/catch block in apply method and declare it with throws PersistenceException in method signature.
In order for noRollbackFor to work, PersistenceException has to be thrown from apply method. You can try the following.
try {
insert();
}
catch(PersistenceException e) {
update();
throw e;
}
I'm having some trouble trying to catch an exception when there are concurrency violations using hibernate and Spring AOP. This is my scenario:
(My MyConcurrentStateControl has a #Version column)
Service layer
#Override
public Integer saveWork(WorkDto dto) throws MyException {
try {
return workBusinessLogicService.saveWork(dto);
} catch (PersistenceException ex) {
// -- Concurrent insert exception
if (ex.getCause() instanceof ConstraintViolationException) {
String errorMsg = "The same item is being created by another user. Please refresh.";
LOGGER.error(errorMsg, ex);
throw new MyException(errorMsg);
}
} catch (StaleObjectStateException ole){
// -- Concurrent update exception
String errorMsg = "The same item is being updated by another user. Please refresh";
LOGGER.error(errorMsg, ole);
throw new MyException(errorMsg);
}
return -1;
}
Business Logic layer
#Override
#Transactional(rollbackFor = {Exception.class, MyException.class, PersistenceException.class, StaleObjectStateException.class})
public Integer saveWork(WorkDto dto) throws MyException, PersistenceException, StaleObjectStateException {
MyConcurrentStateControl concurrentState = concurrentStateManager.getState(dto.getId());
if (concurrentState == null) {
concurrentState = new MyConcurrentStateControl();
}
// -- Do updates in some other tables --
// Save Concurrent State for concurrency check (Optimistic Locking)
Integer id = concurrentStateManager.save(concurrentState);
// -- Also tried entityManager.flush();
}
This is the error I'm getting in the log, and it is thrown in this line:
return workBusinessLogicService.saveWork(dto);
It is the error I'm expecting when multiple threads call the service, but I can't do anything with it.
[#|2015-01-30T20:53:49.793+0000|WARNING|glassfish3.1.2|javax.enterprise.system.core.transaction.com.sun.jts.jta|_ThreadID=32;_ThreadName=Thread-12;|JTS5054: Unexpected error occurred in after completion
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.mymodel.entity.ConcurrentState#7]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2471)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3123)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3021)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3350)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:140)
......................
org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1012)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at net.bull.javamelody.MonitoringSpringInterceptor.invoke(MonitoringSpringInterceptor.java:74)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
at com.mymodel.business.core.service.WorkServiceImpl$$EnhancerByCGLIB$$b1800d24.saveWork(<generated>)
I know the actual queries are triggered on transaction commit, and at that point the control has moved out of the method, therefore I'm not being able to catch the StaleObjectStateException. But how can I do that, or is there some alternative? All I want is:
Roll back all transactions.
Show a reasonable message to the user that there have been concurrent updates and he needs to refresh the UI.
Using spring 4.0.6.RELEASE, Hibernate 4.3.6.Final and hsqldb 2.3.2. My integration test looks like the following;
#Test(expected = DataIntegrityViolationException.class)
public final void testDuplicateItems() {
final ServerEntity serverEntity1 = new ServerEntity("DuplicateItem");
opService.save(serverEntity1);
opService.save(serverEntity1);
}
This works as expected. However, when I run my standalone java component i can save the first item, the second item which is a duplicate is not saved but Im unable to catch the exception. Here is the log file
WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper: SQL Error: -104, SQLState: 23505
2014-08-27 14:52:06,843 ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper: integrity constraint violation: unique constraint or index violation; UK_NFU7LXMMDFVIR1WD08662085N table: SERVERENTITY
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [UK_NFU7LXMMDFVIR1WD08662085N]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:161)
at org.springframework.orm.hibernate4.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:681)
at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:563)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy38.execute(Unknown Source)
at com.opserver.simpleapp.MainApp.start(MainApp.java:60)
at com.opserver.simpleapp.MainApp.main(MainApp.java:37)
... 6 more
Both the service and dao implementations have #Transactional at class level. I've a component class that is calling the service class, this component class is not transactional! The component class prints a response, does the session need to be flushed here?
Need to figure out why the save method in the dao is not throwing the exception, I can actually see it an id being created and then rolled back.
J
My component class is very basic;
boolean isValid = opServerService.loadXMLFile("Server.xml");
try{
if (isValid) {
System.out.println("Entity has been added");
} else {
System.out.println("Entity has not been added");
}
}catch (Exception ex){
System.out.println("that was a focked up");
}
The problem is that "Entity has been added" gets printed to console and then I see the above error in console.
DAO looks like this
#Override
#Transactional
public final void save(final ServerEntity serverEntity) throws DataIntegrityViolationException {
LOGGER.debug(">>start(serverEntity=" + serverEntity + ")");
Preconditions.checkNotNull(serverEntity);
this.getCurrentSession().save(serverEntity);
}
Service method with #Transactional at class level, looks like this
#Override
public final void save(ServerEntity serverEntity) {
opServerDao.save(serverEntity);
}
And Component looks like this
#Component
public class AddCommand implements Command {
#Autowired
OpService opService;
public AddServerCommand() {
super();
}
#Override
public void execute(String[] options) {
try{
boolean isValid = opService.save("Server.xml");
if (isValid) {
System.out.println("Entity has been added");
} else {
System.out.println("Entity has not been added");
}
}catch (Exception ex){
System.out.println("Exception found");
}
}
}
You should catch your exception in the component that's calling the service.
Found the solution, was missing from the applicationContext.xml. The wrapped try/catch around the opService now catches the exception. Need to implement my own custom exception handler but for now at least I know the component class is handling the exception which is being thrown from the service class.
Thanks for your help.
J
I have a question. I use Spring+Hibernate and I can't handle exceptions in methods marked as #Transactional. Previously, when I used Spring JDBC everything worked just fine.
DAO classes are marked with #Repository.
Here is snipper of my old code.
in service:
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, rollbackFor = {Exception.class})
public boolean bookTickets(Integer userId, List<Integer> ticketsId) throws TicketsServiceException {
Integer currId = null;
try {
for (Integer ticketId : ticketsId) {
currId = ticketId;
Ticket ticket = ticketDAO.getTicketById(ticketId);
bookingDAO.bookTicket(ticket, userId);
}
} catch (DuplicateKeyException e) {
throw new TicketsServiceException(String.format(MESSAGE_TICKET_ALREADY_BOOKED, currId));
} catch (EmptyResultDataAccessException e) {
throw new TicketsServiceException(String.format(MESSAGE_NO_SUCH_TICKET, currId));
}
return true;
in dao:
#Transactional(propagation = Propagation.MANDATORY)
public void bookTicket(Ticket ticket, final int userId) {
MapSqlParameterSource map = new MapSqlParameterSource();
map.addValue(Constants.TABLE_BOOKING.FIELD_TICKET_ID, ticket.getId());
map.addValue(Constants.TABLE_BOOKING.FIELD_USER_ID, userId);
int rowsAffected = template.update(QUERY_INSERT_BOOKING, map);
if (LOG.isTraceEnabled()) {
LOG.trace("Affected " + rowsAffected + " rows.");
}
}
Now I'm moving my DAO from Spring JDBC onto hibernate 3.
That's what I have now.
in service:
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, rollbackFor = {Exception.class})
public boolean bookTickets(Integer userId, List<Integer> ticketsId) throws TicketsServiceException {
Integer currId = null;
try {
for (Integer ticketId : ticketsId) {
currId = ticketId;
Ticket ticket = ticketDAO.getTicketById(ticketId);
bookingDAO.bookTicket(new Booking(ticketId, userId));
}
} catch (Exception e) {
throw new TicketsServiceException(String.format(MESSAGE_THERE_IS_NO_SUCH_TICKET_OR_ALREADY_BOOKED, currId));
}
return true;
}
in dao:
#Override
public void bookTicket(Booking booking) {
getHibernateTemplate().save(booking);
}
The problem is that I can't handle exceptions inside try-catch block in service method. They are throwing directly into my controller.
I think the problem is in difference between transaction managers org.springframework.jdbc.datasource.DataSourceTransactionManager in Spring JDBC and org.springframework.orm.hibernate3.HibernateTransactionManager in hibernate, but I dont have any ideas how to overcome this problem, so I need your help.
AFTER UPDATE. ADDED EXAMPLE OF EXCEPTION.
2013-10-21 14:51:17 ERROR BookingController:89 - Unhandled exception
org.springframework.dao.DataIntegrityViolationException: Could not execute JDBC batch update; SQL [insert into booking (ticket_id, user_id, booking_id) values (?, ?, ?)]; constraint ["CONSTRAINT_INDEX_2 ON PUBLIC.BOOKING(TICKET_ID) VALUES ( /* key:1 */ 2, 1, 1)"; SQL statement:
insert into booking (ticket_id, user_id, booking_id) values (?, ?, ?) [23505-173]]; nested exception is org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:643)
at org.springframework.orm.hibernate3.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:793)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:664)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:755)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:475)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:270)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
at cdp.tarasenko.springmvc.task3.service.TicketsService$$EnhancerByCGLIB$$fabdc899.bookTickets(<generated>)
at cdp.tarasenko.springmvc.task3.controller.BookingController.bookTickets(BookingController.java:54)
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.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:755)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:594)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:486)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:524)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:233)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1065)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:413)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:192)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:999)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:250)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
at org.eclipse.jetty.server.Server.handle(Server.java:351)
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:454)
at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:900)
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:954)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:857)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:77)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:609)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:45)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:599)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:534)
at java.lang.Thread.run(Thread.java:662)
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:141)
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.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
... 51 more
Caused by: org.h2.jdbc.JdbcBatchUpdateException: Нарушение уникального индекса или первичного ключа: "CONSTRAINT_INDEX_2 ON PUBLIC.BOOKING(TICKET_ID) VALUES ( /* key:1 */ 2, 1, 1)"
Unique index or primary key violation: "CONSTRAINT_INDEX_2 ON PUBLIC.BOOKING(TICKET_ID) VALUES ( /* key:1 */ 2, 1, 1)"; SQL statement:
insert into booking (ticket_id, user_id, booking_id) values (?, ?, ?) [23505-173]
at org.h2.jdbc.JdbcPreparedStatement.executeBatch(JdbcPreparedStatement.java:1167)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
... 59 more
Is there any ways to handle exceptions inside service layer?
Thank you.
Take a look of hibernate FlushMode
Default HiberanateTemplate FlushMode is FlusMode.AUTO, so session synchronization will occur on commit or before some queries to prevent stale state.
You could change HibernateTemplate flush mode to FlushMode.ALWAYS, inefficient and not recomended (but will work as you expect now), or call Session.flush() at some points.
see HiberanateTemplate.setFlushMode() and Session.flush()
You don't get DB exceptions like constraint violations before committing the TX and TX gets committed in transaction manager. The best solution would be handling TX manager exceptions using an afterThrowing aspect (AOP). Alternatively, you can call your #Transactional methods inside another non-transactional method in your service and have it catch the exceptions. Personally, I don't like the later as you will have to create a wrapper method for all your services.
Or you can invoke your transactional code in explicit transaction mode.
E.g.
//No #transactional here
public void service() {
PlatformTransactionManager txManager= ...;
TransactionStatus tx = txManager.getTransaction(new DefaultTransactionDefinition());
try{
.... do something
txManager.commit(tx);
}catch(DataAccessException ex){
txManager.rollback(tx);
... handle error
}
}