spring@Transactional注解的方法报死锁异常后部分回滚的原因何在?

时间:2022-07-08 20:36:47
废话不多说,直接上代码:

service接口方法:
public ClassA executeFun(ClassC c ,ClassD d);


service接口方法实现类:
@Override
@Transactional
public ClassA executeFun(ClassC c ,ClassD d){
    ClassB b = new ClassB();
    保存b;
    更新c;
    更新d;
}

当service接口方法实现类代码执行完进行事务提交时抛出(更新c的死锁)异常:org.springframework.orm.jpa.JpaSystemException: org.hibernate.exception.LockAcquisitionException: Deadlock found when trying to get lock; try restarting transaction; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.LockAcquisitionException: Deadlock found when trying to get lock; try restarting transaction
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.LockAcquisitionException: Deadlock found when trying to get lock; try restarting transaction
        at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387)
        at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310)
        at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:80)
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:512)
        ... 52 more
Caused by: org.hibernate.exception.LockAcquisitionException: Deadlock found when trying to get lock; try restarting transaction
        at org.hibernate.dialect.MySQLDialect$1.convert(MySQLDialect.java:392)
        at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
        at org.hibernate.engine.jdbc.internal.proxy.AbstractStatementProxyHandler.continueInvocation(AbstractStatementProxyHandler.java:129)
        at org.hibernate.engine.jdbc.internal.proxy.AbstractProxyHandler.invoke(AbstractProxyHandler.java:81)
        at com.sun.proxy.$Proxy632.executeUpdate(Unknown Source)
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3189)
        at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3087)
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3416)
        at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:140)
        at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:362)
        at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:354)
        at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:276)
        at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:326)
        at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
        at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1213)
        at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:402)
        at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
        at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
        at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:75)
        ... 53 more
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:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
        at com.mysql.jdbc.Util.getInstance(Util.java:386)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1066)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4187)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4119)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2570)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2731)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2815)
        at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2375)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2359)
        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.hibernate.engine.jdbc.internal.proxy.AbstractStatementProxyHandler.continueInvocation(AbstractStatementProxyHandler.java:122)
        ... 69 more
出现的结果就是: 更新对象ClassB的事务没有回滚;更新对象ClasssC的事务回滚了;更新对象ClassD的事务回滚了。
注:1、数据库是mysql innodb;
        2、抛出的异常(死锁异常)是运行时异常;

         最后请教各位大侠:spring@Transactional注解的方法报死锁异常后部分回滚的原因何在?有什么解决方案?



6 个解决方案

#1


在整个方法前加上 @Transactional(rollbackFor=Exception.class) 

#2


试过了,不行!

#3


本帖最后由 defonds 于 2015-11-03 14:34:48 编辑
@Override
@Transactional
public ClassA executeFun(ClassC c ,ClassD d){
    ClassB b = new ClassB();
    保存b;
    更新c;
    更新d;
}

这段代码,如果"更新c;"这句抛了异常,那么"更新d;"也就不会执行了吧,
引用 楼主 shower2010 的回复:
更新对象ClassB的事务没有回滚;更新对象ClasssC的事务回滚了;更新对象ClassD的事务回滚了。

既然没有执行"更新d;",那么也就谈不上“更新对象ClassD的事务回滚了”。
此外,更新c的死锁,那么也就是说没有对c进行更新,也谈不上“更新对象ClasssC的事务回滚了”。
无论如何,同一事务中后续代码抛了异常,前边数据库操作不应该不回滚,因此我怀疑你的 spring 事务控制压根儿就没起作用。

#4


引用 楼主 shower2010 的回复:
注:1、数据库是mysql innodb;
这句话有所欠妥。
MySQL 的存储引擎不是在建库时定义的,是在建表 DDL 时声明的,同库里可以有多种类型的存储引擎的表,每个表的存储引擎未必一致。
如果你的 spring 事务配置没问题,我怀疑是你的类 B 对应的表存储引擎可能是 MyISAM 所致。
查看你的实体类 B 对应的表(比如是 TABLE_B)的存储引擎是否是 InnoDB:
SHOW CREATE TABLE TABLE_B;
如果结果是 ENGINE=MyISAM,那么就是它的原因了。

#5


引用 4 楼 defonds 的回复:
Quote: 引用 楼主 shower2010 的回复:


注:1、数据库是mysql innodb;
这句话有所欠妥。
MySQL 的存储引擎不是在建库时定义的,是在建表 DDL 时声明的,同库里可以有多种类型的存储引擎的表,每个表的存储引擎未必一致。
如果你的 spring 事务配置没问题,我怀疑是你的类 B 对应的表存储引擎可能是 MyISAM 所致。
查看你的实体类 B 对应的表(比如是 TABLE_B)的存储引擎是否是 InnoDB:
SHOW CREATE TABLE TABLE_B;
如果结果是 ENGINE=MyISAM,那么就是它的原因了。


非常感谢,此问题的原因正如Defonds所说的一样,实体B对应的表的存储引擎是 MyISAM 所致。
我的mysql默认的存储引擎是innoDB,但是这个表在创建时给指定为MyISAM了。

再次谢谢各位!

#6


你首先确定你service所在的类有没有别的 方法也在操作那个表,同时那个方法却在另一个事务种,如果有就会死锁,因为你其中一个事务没有commit,在执行中,另一个事务的写操作是会死锁的

#1


在整个方法前加上 @Transactional(rollbackFor=Exception.class) 

#2


试过了,不行!

#3


本帖最后由 defonds 于 2015-11-03 14:34:48 编辑
@Override
@Transactional
public ClassA executeFun(ClassC c ,ClassD d){
    ClassB b = new ClassB();
    保存b;
    更新c;
    更新d;
}

这段代码,如果"更新c;"这句抛了异常,那么"更新d;"也就不会执行了吧,
引用 楼主 shower2010 的回复:
更新对象ClassB的事务没有回滚;更新对象ClasssC的事务回滚了;更新对象ClassD的事务回滚了。

既然没有执行"更新d;",那么也就谈不上“更新对象ClassD的事务回滚了”。
此外,更新c的死锁,那么也就是说没有对c进行更新,也谈不上“更新对象ClasssC的事务回滚了”。
无论如何,同一事务中后续代码抛了异常,前边数据库操作不应该不回滚,因此我怀疑你的 spring 事务控制压根儿就没起作用。

#4


引用 楼主 shower2010 的回复:
注:1、数据库是mysql innodb;
这句话有所欠妥。
MySQL 的存储引擎不是在建库时定义的,是在建表 DDL 时声明的,同库里可以有多种类型的存储引擎的表,每个表的存储引擎未必一致。
如果你的 spring 事务配置没问题,我怀疑是你的类 B 对应的表存储引擎可能是 MyISAM 所致。
查看你的实体类 B 对应的表(比如是 TABLE_B)的存储引擎是否是 InnoDB:
SHOW CREATE TABLE TABLE_B;
如果结果是 ENGINE=MyISAM,那么就是它的原因了。

#5


引用 4 楼 defonds 的回复:
Quote: 引用 楼主 shower2010 的回复:


注:1、数据库是mysql innodb;
这句话有所欠妥。
MySQL 的存储引擎不是在建库时定义的,是在建表 DDL 时声明的,同库里可以有多种类型的存储引擎的表,每个表的存储引擎未必一致。
如果你的 spring 事务配置没问题,我怀疑是你的类 B 对应的表存储引擎可能是 MyISAM 所致。
查看你的实体类 B 对应的表(比如是 TABLE_B)的存储引擎是否是 InnoDB:
SHOW CREATE TABLE TABLE_B;
如果结果是 ENGINE=MyISAM,那么就是它的原因了。


非常感谢,此问题的原因正如Defonds所说的一样,实体B对应的表的存储引擎是 MyISAM 所致。
我的mysql默认的存储引擎是innoDB,但是这个表在创建时给指定为MyISAM了。

再次谢谢各位!

#6


你首先确定你service所在的类有没有别的 方法也在操作那个表,同时那个方法却在另一个事务种,如果有就会死锁,因为你其中一个事务没有commit,在执行中,另一个事务的写操作是会死锁的