本文目录导读:

在Java中处理数据库事务,核心是保证一组数据库操作要么全部成功(提交),要么全部失败(回滚),以维护数据的一致性和完整性,主要分为两种主流方式:JDBC原生事务和Spring声明式事务。
下面我们分别介绍这两种方式的处理案例。
JDBC原生事务(低层、手动控制)
这种方式需要手动获取数据库连接,开启事务,执行SQL,然后根据结果选择提交或回滚,适用于学习理解或小型项目。
核心步骤:
connection.setAutoCommit(false);// 关闭自动提交,开启事务- 执行多条SQL语句。
connection.commit();// 所有操作成功,提交事务- 如果发生异常,
connection.rollback();// 回滚所有操作 - 在
finally或 try-with-resources 中关闭资源。
案例代码:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JdbcTransactionExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/your_database";
String username = "root";
String password = "password";
// SQL语句:银行转账 A账户扣100,B账户加100
String sqlDebit = "UPDATE accounts SET balance = balance - 100 WHERE account_id = ?";
String sqlCredit = "UPDATE accounts SET balance = balance + 100 WHERE account_id = ?";
// 使用try-with-resources自动关闭Connection, PreparedStatement
try (Connection conn = DriverManager.getConnection(url, username, password);
PreparedStatement pstmtDebit = conn.prepareStatement(sqlDebit);
PreparedStatement pstmtCredit = conn.prepareStatement(sqlCredit)) {
// 1. 开启事务:关闭自动提交
conn.setAutoCommit(false);
// 2. 执行转账操作
// 扣钱
pstmtDebit.setInt(1, 1); // 账户1扣钱
int affectedRowsDebit = pstmtDebit.executeUpdate();
// 加钱
pstmtCredit.setInt(1, 2); // 账户2加钱
int affectedRowsCredit = pstmtCredit.executeUpdate();
// 模拟一个异常,触发回滚(比如业务校验失败)
// if (someCondition) { throw new RuntimeException("业务异常"); }
// 3. 所有操作成功,提交事务
conn.commit();
System.out.println("转账成功!");
} catch (SQLException e) {
// 4. 发生异常,尝试回滚
// 注意:这里连接对象需要提前获取,或者使用嵌套try-catch
System.err.println("转账失败,尝试回滚...");
// 由于try-with-resources在catch之前关闭连接,我们需要在catch中获取连接对象回滚,
// 通常做法是将Connection声明在try外面,或者使用更复杂的嵌套。
// 为简化演示,这里省略了回滚具体代码,实际应使用 connection.rollback();
e.printStackTrace();
// 更规范的写法是:在catch块中获取connection对象并rollback
}
}
}
注意事项:
- 务必在
finally或try-with-resources中关闭 Connection、Statement、ResultSet 等资源,防止内存泄漏。 - 回滚操作需要 Connection 对象尚未被关闭,JDBC 事务通常需要将 Connection 声明在 try 块外面,以便在 catch 中回滚。
Spring声明式事务(企业级、推荐)
在Spring框架中,最常用的是使用 @Transactional 注解(声明式事务),它基于AOP实现,让开发者只需关注业务逻辑,事务管理由Spring容器自动处理。
核心步骤:
- 配置数据源和事务管理器(通常在Spring Boot中自动配置,如DataSourceTransactionManager或JpaTransactionManager)。
- 在需要事务的类或方法上添加
@Transactional注解。 - Spring会在方法执行前开启事务,执行完正常提交,抛出RuntimeException(默认)时回滚。
案例代码(Spring Boot + MyBatis/Spring Data JPA):
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TransferService {
@Autowired
private AccountDao accountDao; // 假设使用MyBatis的Mapper或JPA的Repository
/**
* 转账操作,整个方法在一个数据库事务中执行
* @param fromAccountId 转出方账户ID
* @param toAccountId 转入方账户ID
* @param amount 转账金额
*/
@Transactional(rollbackFor = Exception.class) // 可以指定哪些异常需要回滚
public void transferMoney(int fromAccountId, int toAccountId, double amount) {
// 1. 扣钱
if (accountDao.debitBalance(fromAccountId, amount) != 1) {
// 如果扣钱影响行数不为1,抛出异常触发回滚
throw new IllegalStateException("账户扣款失败,可能账户不存在或余额不足");
}
// 2. 加钱
if (accountDao.creditBalance(toAccountId, amount) != 1) {
// 如果加钱影响行数不为1,抛出异常触发回滚(注意:此时扣钱操作也会被回滚)
throw new IllegalStateException("账户入账失败,可能目标账户不存在");
}
// 3. 其他业务逻辑(如记录日志),如果不抛出异常,全部成功提交
// ...
}
}
@Transactional 关键属性:
propagation:事务传播行为(如 REQUIRED、REQUIRES_NEW、NESTED 等)。REQUIRED(默认):支持当前事务,如果没有则创建新事务。REQUIRES_NEW:挂起当前事务,创建新事务执行当前方法。
isolation:事务隔离级别(如 READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE)。timeout:事务超时时间(秒),超时自动回滚。rollbackFor:指定哪些异常触发回滚(默认只回滚RuntimeException和Error,检查异常不受影响)。noRollbackFor:指定哪些异常不触发回滚。
Spring事务失效的常见原因:
- 方法不能是 private:Spring AOP代理无法拦截私有方法。
- 方法被类内其他方法调用(自调用):
this.doSomething(),没有通过代理对象调用,事务失效,解决方案:注入自身Service代理,或使用AopContext.currentProxy()。 - 异常被方法内 try-catch 捕获且未重新抛出:Spring无法感知异常,不会回滚。
- 事务管理器配置错误:数据源、事务管理器未正确注入。
- 数据库不支持事务(如某些MyISAM引擎的表)。
总结对比
| 特性 | JDBC原生事务 | Spring声明式事务 |
|---|---|---|
| 控制方式 | 手动(setAutocommit, commit, rollback) | 自动(注解驱动,AOP) |
| 代码侵入性 | 高 | 低(只需添加注解) |
| 灵活性 | 高,可精细控制 | 高,通过属性配置 |
| 推荐场景 | 学习、简单项目、非Spring项目 | 企业级应用、Spring/Spring Boot项目 |
| 资源管理 | 需手动关闭(建议try-with-resources) | 自动管理 |
| 异常回滚 | 需要手动捕获异常并调用rollback | 默认回滚RuntimeException,可配置 |
最佳实践:
- 大多数Java Web项目直接使用 Spring声明式事务,结合
@Transactional注解,简洁高效。 - 如果你不使用Spring框架(如纯Servlet/JDBC),则必须使用 JDBC原生事务 手动管理。
- 对于复杂的事务场景(如嵌套事务、长事务),需要理解事务的传播行为和隔离级别,谨慎设计。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播