解决Transaction is already completed - do not call commit or rollback more than once per transaction问题

本文分析了在使用Spring框架进行事务管理时遇到的常见错误,详细解释了事务多次提交的问题,并提供了修改后的代码示例来避免此类错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

        在看《Spring揭秘》事务章节内容,调试代码过程中,出现以下异常:

19/01/29 14:41:31 [main]: DEBUG datasource.DataSourceTransactionManager: Creating new transaction with name [ChansonTransaction]: PROPAGATION_REQUIRES_NEW,ISOLATION_READ_COMMITTED
19/01/29 14:41:31 [main]: DEBUG datasource.DataSourceTransactionManager: Acquired Connection [jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&useSSL=false, UserName=root@localhost, MySQL Connector Java] for JDBC transaction
19/01/29 14:41:31 [main]: DEBUG datasource.DataSourceUtils: Changing isolation level of JDBC Connection [jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&useSSL=false, UserName=root@localhost, MySQL Connector Java] to 2
19/01/29 14:41:31 [main]: DEBUG datasource.DataSourceTransactionManager: Switching JDBC Connection [jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&useSSL=false, UserName=root@localhost, MySQL Connector Java] to manual commit
19/01/29 14:41:31 [main]: DEBUG core.JdbcTemplate: Executing prepared SQL update
19/01/29 14:41:31 [main]: DEBUG core.JdbcTemplate: Executing prepared SQL statement [UPDATE t_user SET password=? WHERE user_name =?]
19/01/29 14:41:31 [main]: DEBUG core.JdbcTemplate: SQL update affected 1 rows
19/01/29 14:41:31 [main]: DEBUG datasource.DataSourceTransactionManager: Initiating transaction rollback
19/01/29 14:41:31 [main]: DEBUG datasource.DataSourceTransactionManager: Rolling back JDBC transaction on Connection [jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&useSSL=false, UserName=root@localhost, MySQL Connector Java]
19/01/29 14:41:31 [main]: DEBUG datasource.DataSourceUtils: Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&useSSL=false, UserName=root@localhost, MySQL Connector Java] to 4
19/01/29 14:41:31 [main]: DEBUG datasource.DataSourceTransactionManager: Releasing JDBC Connection [jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&useSSL=false, UserName=root@localhost, MySQL Connector Java] after transaction
19/01/29 14:41:31 [main]: DEBUG datasource.DataSourceUtils: Returning JDBC Connection to DataSource
Exception in thread "main" org.springframework.transaction.IllegalTransactionStateException: Transaction is already completed - do not call commit or rollback more than once per transaction
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:697)
	at springjiemi.platformtransaction.UserService.update(UserService.java:42)
	at springjiemi.platformtransaction.UserService.main(UserService.java:50)

 

分析

日志显示存在事务多次提交的问题。

分析了一下源代码:

public void update(String userName) {
		
		DefaultTransactionDefinition transactionDefinition  = new DefaultTransactionDefinition();
		transactionDefinition.setName("ChansonTransaction");
		//默认:TransactionDefinition.ISOLATION_DEFAULT
		transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
		//默认:TransactionDefinition.PROPAGATION_REQUIRED
		transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
		TransactionStatus txStatus = platformTransactionManager.getTransaction(transactionDefinition);
		try {
			userDao1.doDataAccess(userName);
			int i = 9/0;
			userDao2.doDataAccess(userName);
		} catch (DataAccessException e) {
			platformTransactionManager.rollback(txStatus);			
		} catch (Exception e) {
			platformTransactionManager.rollback(txStatus);
		}
		platformTransactionManager.commit(txStatus);
	}

        由于异常被catch,而在catch时,进行了事务回滚,正常在此应该退出方法处理(程序直接返回或者抛出异常),但事实上程序直接执行了后续的代码:platformTransactionManager.commit(txStatus);所以报错。
         

解决

        解决方法:在catch过程中,抛出异常,结束方法执行。

public void update(String userName) {
		
		DefaultTransactionDefinition transactionDefinition  = new DefaultTransactionDefinition();
		transactionDefinition.setName("ChansonTransaction");
		//默认:TransactionDefinition.ISOLATION_DEFAULT
		transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
		//默认:TransactionDefinition.PROPAGATION_REQUIRED
		transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
		TransactionStatus txStatus = platformTransactionManager.getTransaction(transactionDefinition);
		try {
			userDao1.doDataAccess(userName);
			int i = 9/0;
			userDao2.doDataAccess(userName);
		} catch (DataAccessException e) {
			platformTransactionManager.rollback(txStatus);
			throw e;
		} catch (Exception e) {
			platformTransactionManager.rollback(txStatus);
			throw e;
		}
		platformTransactionManager.commit(txStatus);
	}

文章结束。

### EXECUTE_TRANSACTION 示例、用法及场景 #### 1. Java 中的事务执行方法 `executeTransaction` 在Java企业级应用开发中,`executeTransaction` 方法通常用于封装事务边界内的业务逻辑。此方法确保所有数据库操作要么全部成功提交,要么完全回滚,从而保持数据的一致性和完整性[^3]。 ```java public void executeTransaction() { UserTransaction utx = null; try { utx = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction"); utx.begin(); // 执行多个持久化操作... entityManager.persist(newEntity); utx.commit(); // 提交事务 } catch(javax.transaction.NotSupportedException | javax.transaction.SystemException | javax.transaction.RollbackException | javax.transaction.HeuristicMixedException | javax.transaction.HeuristicRollbackException e) { if(utx != null){ try{ utx.rollback(); // 发生异常时回滚事务 }catch(Exception ex){} } throw new RuntimeException(e.getMessage(),e); } } ``` 上述代码展示了如何通过JTA API来启动一个新的全局事务,并尝试完成一系列持久化操作。如果过程中遇到任何问题,则会触发回滚机制以撤销所有的更改。 #### 2. Oracle PL/SQL 中自治事务的应用实例 对于Oracle数据库而言,当需要记录日志或其他独立于主事务的操作时,可以利用自治事务特性。下面是一个基于给定包定义创建的日志记录过程的例子: ```sql CREATE OR REPLACE PACKAGE BODY scott.pkg_test AS PROCEDURE p_main_test IS PRAGMA AUTONOMOUS_TRANSACTION; v_log_message VARCHAR2(200); BEGIN INSERT INTO log_table VALUES ('Main transaction started'); COMMIT; -- 自治事务立即生效 -- 主要业务逻辑... v_log_message := 'Main transaction completed successfully'; p_log_info(v_log_message); END p_main_test; PROCEDURE p_log_info(v_log_message IN VARCHAR2) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO log_table VALUES (v_log_message); COMMIT; -- 日志写入完成后即刻确认 END p_log_info; END pkg_test; / ``` 这里展示了一个名为 `p_main_test` 的存储过程,它内部调用了另一个负责记录消息到表中的辅助函数 `p_log_info` 。两者都声明了自己的自治事务属性,这意味着它们可以在不影响彼此的情况下单独提交或回滚各自的变更[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值