Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring事务回滚异常在抛出后继续执行代码_Java_Spring_Hibernate_Spring Transactions - Fatal编程技术网

Java Spring事务回滚异常在抛出后继续执行代码

Java Spring事务回滚异常在抛出后继续执行代码,java,spring,hibernate,spring-transactions,Java,Spring,Hibernate,Spring Transactions,我有一个安排会议和发送确认电子邮件的服务,我注意到如果我多次单击提交按钮,将发送多封电子邮件 服务内容如下: @Service @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = { Exception.class }) public class MeetingService { public void scheduleAndInvite(int meetingId) { try {

我有一个安排会议和发送确认电子邮件的服务,我注意到如果我多次单击提交按钮,将发送多封电子邮件

服务内容如下:

@Service
@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = { Exception.class })
public class MeetingService {
    public void scheduleAndInvite(int meetingId) {
        try {
            Meeting meeting = meetingDao.loadById(meetingId);

            // Validations.
            if (meeting.getMeetingStatus() != MeetingStatus.Draft) {
                throw new FmcUserException("not draft");
            }

            // Persist entity
            meeting.setMeetingStatus(MeetingStatus.Scheduled);

            meetingDao.persistMyEntity(meeting);

            // This eventually calls JavaMailSender. Uses the Meeting hibernate entity
            sendInvitations(meeting);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            throw new FmcSystemException(ex); // This class extends RuntimeException.
        }
    }
在浏览器中多次单击Submit时,我希望第一次测试(Status!=draft)足以评估此会议是否已安排。在这种情况下,异常被抛出,被catch块捕获,从而跳过sendInvestments()调用

它会在日志中正确生成大量异常:

12:45:01,117 ERROR [my.framework.mvc.BaseController] (default task-55) could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.LockAcquisitionException: could not execute statement: org.springframework.dao.CannotAcquireLockException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.LockAcquisitionException: could not execute statement
    at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:232)
    at org.springframework.orm.hibernate5.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:755)

Caused by: org.hibernate.exception.LockAcquisitionException: could not execute statement
    at org.hibernate.dialect.MySQLDialect$3.convert(MySQLDialect.java:522)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
我不可能理解这到底是如何发送多封电子邮件的。我在这里读到spring继续执行直到方法结束,但为什么?!抛出异常后,为什么执行应该在throw()语句之后继续

我知道这个问题可以通过包装sendEmail呼叫来解决:

    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
            sendInvitations(meeting);
        }
    });
但话说回来。为什么?


非常感谢

Spring在抛出异常后不执行代码。这是不可能的,java就是这样工作的

在我看来,这是因为通过多次按下submit,您将多次向服务器发送请求,这些请求将并行执行。在某一点上,不同的线程将互相阻碍


如果要正确解决此问题,您需要同步此代码,或者使用数据库锁来阻止其他事务通过。(使用SELECT…FOR UPDATE或其他方法)

引发异常后,Spring没有执行代码。这是不可能的,java就是这样工作的

在我看来,这是因为通过多次按下submit,您将多次向服务器发送请求,这些请求将并行执行。在某一点上,不同的线程将互相阻碍


如果要正确解决此问题,您需要同步此代码,或者使用数据库锁来阻止其他事务通过。(使用SELECT…(用于更新或其他方式)

Spring在异常之后肯定不会执行。发送多封电子邮件的原因是存在竞争条件。如果多次按submit,相同的请求将并行执行,因此
meetingDao.loadById
可以在代码中完成更新之前执行多次。我明白了。是否因为提交仅在事务结束时执行?如果是这样的话,有没有办法在更新后及时强制提交?或者使用TransactionSynchronizationManager是一种更好的方法?您可以强制提交,但仍然会有竞争条件,因为多个参与者可以在提交事务之前访问读取部分。一个“简单”的修复方法是在提交请求后禁用提交按钮。但这并不能阻止任何人直接调用端点。对于这样的系统问题,我通常会建议您的请求的idem效力;然而,对于你正在做的事情来说,这可能是完全的矫枉过正。我现在将使用TransactionSynchronizationManager在提交后执行非事务性方法。如果您想将您的评论升级为完整答案(以帮助他人),我将接受。@Leon请将您的评论升级为答案,以便我可以接受!谢谢,Spring绝对不会在异常后执行。发送多封电子邮件的原因是存在竞争条件。如果多次按submit,相同的请求将并行执行,因此
meetingDao.loadById
可以在代码中完成更新之前执行多次。我明白了。是否因为提交仅在事务结束时执行?如果是这样的话,有没有办法在更新后及时强制提交?或者使用TransactionSynchronizationManager是一种更好的方法?您可以强制提交,但仍然会有竞争条件,因为多个参与者可以在提交事务之前访问读取部分。一个“简单”的修复方法是在提交请求后禁用提交按钮。但这并不能阻止任何人直接调用端点。对于这样的系统问题,我通常会建议您的请求的idem效力;然而,对于你正在做的事情来说,这可能是完全的矫枉过正。我现在将使用TransactionSynchronizationManager在提交后执行非事务性方法。如果您想将您的评论升级为完整答案(以帮助他人),我将接受。@Leon请将您的评论升级为答案,以便我可以接受!谢谢