Java 中实现事务的几种方法

Java 中实现事务的几种方法

目录

Java 中实现事务的几种方法

1. JDBC 原生事务

2. Spring 编程式事务

3. Spring 声明式事务(注解方式)

4. EJB 事务

5. 分布式事务

总结

事务是数据库操作中的重要概念,它确保了一组操作要么全部成功,要么全部失败,从而保证数据的一致性和完整性。在 Java 中,我们有多种方式来实现事务管理。本文将详细介绍几种常用的事务实现方法,并提供相应的代码示例。

1. JDBC 原生事务

JDBC 提供了最基础的事务控制方式,通过 Connection 对象来管理事务。默认情况下,JDBC 是自动提交事务的,我们可以通过手动设置来控制事务。

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.SQLException;

public class JDBCTransactionExample {

// 数据库连接信息

private static final String URL = "jdbc:mysql://localhost:3306/testdb";

private static final String USER = "root";

private static final String PASSWORD = "password";

public void transferMoney(int fromAccountId, int toAccountId, double amount) {

Connection connection = null;

PreparedStatement debitStmt = null;

PreparedStatement creditStmt = null;

try {

// 获取数据库连接

connection = DriverManager.getConnection(URL, USER, PASSWORD);

// 关闭自动提交,开始事务

connection.setAutoCommit(false);

// 从源账户扣款

String debitSql = "UPDATE accounts SET balance = balance - ? WHERE id = ?";

debitStmt = connection.prepareStatement(debitSql);

debitStmt.setDouble(1, amount);

debitStmt.setInt(2, fromAccountId);

debitStmt.executeUpdate();

// 向目标账户存款

String creditSql = "UPDATE accounts SET balance = balance + ? WHERE id = ?";

creditStmt = connection.prepareStatement(creditSql);

creditStmt.setDouble(1, amount);

creditStmt.setInt(2, toAccountId);

creditStmt.executeUpdate();

// 所有操作成功,提交事务

connection.commit();

System.out.println("转账成功!");

} catch (SQLException e) {

System.err.println("转账失败,回滚事务: " + e.getMessage());

try {

// 发生异常,回滚事务

if (connection != null) {

connection.rollback();

}

} catch (SQLException ex) {

System.err.println("回滚事务失败: " + ex.getMessage());

}

} finally {

// 关闭资源

try {

if (debitStmt != null) debitStmt.close();

if (creditStmt != null) creditStmt.close();

if (connection != null) {

// 恢复自动提交模式

connection.setAutoCommit(true);

connection.close();

}

} catch (SQLException e) {

System.err.println("关闭资源失败: " + e.getMessage());

}

}

}

public static void main(String[] args) {

JDBCTransactionExample example = new JDBCTransactionExample();

// 示例:从账户1向账户2转账100元

example.transferMoney(1, 2, 100.0);

}

}

JDBC 事务的核心 API:

setAutoCommit(false):关闭自动提交,开始事务commit():提交事务rollback():回滚事务setSavepoint():设置保存点,可部分回滚

2. Spring 编程式事务

Spring 框架提供了更灵活的事务管理方式。编程式事务通过 TransactionTemplate 或直接使用 PlatformTransactionManager 来控制事务。

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.transaction.TransactionStatus;

import org.springframework.transaction.support.TransactionCallbackWithoutResult;

import org.springframework.transaction.support.TransactionTemplate;

public class SpringProgrammaticTransactionExample {

private final TransactionTemplate transactionTemplate;

private final JdbcTemplate jdbcTemplate;

// 通过构造函数注入依赖

public SpringProgrammaticTransactionExample(TransactionTemplate transactionTemplate, JdbcTemplate jdbcTemplate) {

this.transactionTemplate = transactionTemplate;

this.jdbcTemplate = jdbcTemplate;

}

public void transferMoney(int fromAccountId, int toAccountId, double amount) {

// 使用TransactionTemplate执行事务

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

@Override

protected void doInTransactionWithoutResult(TransactionStatus status) {

try {

// 从源账户扣款

jdbcTemplate.update(

"UPDATE accounts SET balance = balance - ? WHERE id = ?",

amount, fromAccountId

);

// 向目标账户存款

jdbcTemplate.update(

"UPDATE accounts SET balance = balance + ? WHERE id = ?",

amount, toAccountId

);

System.out.println("转账成功!");

} catch (Exception e) {

System.err.println("转账失败,准备回滚: " + e.getMessage());

// 标记事务为回滚状态

status.setRollbackOnly();

}

}

});

}

public static void main(String[] args) {

// 实际应用中,这些会由Spring容器管理

// ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

// SpringProgrammaticTransactionExample example = context.getBean(SpringProgrammaticTransactionExample.class);

// 示例调用

// example.transferMoney(1, 2, 100.0);

}

}

Spring 编程式事务的配置(XML 方式):

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd">

3. Spring 声明式事务(注解方式)

声明式事务是 Spring 中最常用的事务管理方式,它通过注解或 XML 配置来管理事务,无需在代码中显式编写事务控制逻辑。

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

@Service

public class AccountService {

private final JdbcTemplate jdbcTemplate;

// 构造函数注入JdbcTemplate

public AccountService(JdbcTemplate jdbcTemplate) {

this.jdbcTemplate = jdbcTemplate;

}

/**

* 转账操作,使用@Transactional注解声明事务

* propagation = Propagation.REQUIRED:如果当前没有事务,就新建一个事务;如果已经存在一个事务中,加入到这个事务中

* isolation = Isolation.READ_COMMITTED:读取已提交的数据

* rollbackFor = Exception.class:遇到任何异常都回滚

*/

@Transactional(

propagation = org.springframework.transaction.annotation.Propagation.REQUIRED,

isolation = org.springframework.transaction.annotation.Isolation.READ_COMMITTED,

rollbackFor = Exception.class

)

public void transferMoney(int fromAccountId, int toAccountId, double amount) {

// 从源账户扣款

jdbcTemplate.update(

"UPDATE accounts SET balance = balance - ? WHERE id = ?",

amount, fromAccountId

);

// 模拟可能出现的异常

// if (true) throw new RuntimeException("模拟异常,触发回滚");

// 向目标账户存款

jdbcTemplate.update(

"UPDATE accounts SET balance = balance + ? WHERE id = ?",

amount, toAccountId

);

System.out.println("转账成功!");

}

}

@Transactional 注解的主要属性:

propagation:事务传播行为,如 REQUIRED、REQUIRES_NEW 等isolation:事务隔离级别,如 READ_COMMITTED、REPEATABLE_READ 等rollbackFor:指定哪些异常触发回滚noRollbackFor:指定哪些异常不触发回滚timeout:事务超时时间readOnly:是否为只读事务

4. EJB 事务

EJB(Enterprise JavaBeans)也提供了事务管理功能,主要通过注解来声明事务属性。

import javax.ejb.Stateless;

import javax.persistence.EntityManager;

import javax.persistence.PersistenceContext;

import javax.transaction.Transactional;

@Stateless

public class AccountEJB {

@PersistenceContext(unitName = "accountPU")

private EntityManager em;

/**

* 转账操作,使用EJB的@Transactional注解

* Transactional.TxType.REQUIRED:如果当前没有事务,就新建一个事务;如果已经存在一个事务中,加入到这个事务中

*/

@Transactional(Transactional.TxType.REQUIRED)

public void transferMoney(int fromAccountId, int toAccountId, double amount) {

Account fromAccount = em.find(Account.class, fromAccountId);

Account toAccount = em.find(Account.class, toAccountId);

if (fromAccount == null || toAccount == null) {

throw new IllegalArgumentException("账户不存在");

}

if (fromAccount.getBalance() < amount) {

throw new IllegalStateException("余额不足");

}

// 执行转账操作

fromAccount.setBalance(fromAccount.getBalance() - amount);

toAccount.setBalance(toAccount.getBalance() + amount);

// 合并实体状态

em.merge(fromAccount);

em.merge(toAccount);

System.out.println("转账成功!");

}

}

// 账户实体类

import javax.persistence.Entity;

import javax.persistence.Id;

@Entity

class Account {

@Id

private int id;

private String name;

private double balance;

// getter和setter方法

public int getId() { return id; }

public void setId(int id) { this.id = id; }

public String getName() { return name; }

public void setName(String name) { this.name = name; }

public double getBalance() { return balance; }

public void setBalance(double balance) { this.balance = balance; }

}

EJB 事务的传播级别主要有:

REQUIRED:默认值,如果当前有事务则加入,否则创建新事务REQUIRES_NEW:总是创建新事务SUPPORTS:如果当前有事务则加入,否则非事务执行MANDATORY:必须在已有事务中执行,否则抛出异常NOT_SUPPORTED:非事务执行,如果当前有事务则暂停NEVER:非事务执行,如果当前有事务则抛出异常

5. 分布式事务

在分布式系统中,事务可能涉及多个数据源或服务,这时需要使用分布式事务解决方案。Java 中常用的分布式事务实现包括 JTA(Java Transaction API)和一些开源框架如 Seata、Hmily 等。

import javax.annotation.Resource;

import javax.ejb.Stateless;

import javax.transaction.UserTransaction;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

@Stateless

public class JTADistributedTransaction {

// 注入JTA事务管理

@Resource

private UserTransaction utx;

// 数据库1连接信息

private static final String DB1_URL = "jdbc:mysql://localhost:3306/db1";

private static final String DB1_USER = "root";

private static final String DB1_PASSWORD = "password";

// 数据库2连接信息

private static final String DB2_URL = "jdbc:mysql://localhost:3306/db2";

private static final String DB2_USER = "root";

private static final String DB2_PASSWORD = "password";

public void distributeTransaction() throws Exception {

Connection conn1 = null;

Connection conn2 = null;

PreparedStatement stmt1 = null;

PreparedStatement stmt2 = null;

try {

// 开始JTA事务

utx.begin();

// 连接第一个数据库并执行操作

conn1 = DriverManager.getConnection(DB1_URL, DB1_USER, DB1_PASSWORD);

// 注意:不要设置autoCommit,由JTA管理

String sql1 = "INSERT INTO logs (message) VALUES (?)";

stmt1 = conn1.prepareStatement(sql1);

stmt1.setString(1, "操作1执行成功");

stmt1.executeUpdate();

// 连接第二个数据库并执行操作

conn2 = DriverManager.getConnection(DB2_URL, DB2_USER, DB2_PASSWORD);

String sql2 = "INSERT INTO records (content) VALUES (?)";

stmt2 = conn2.prepareStatement(sql2);

stmt2.setString(1, "操作2执行成功");

stmt2.executeUpdate();

// 提交事务

utx.commit();

System.out.println("分布式事务执行成功!");

} catch (Exception e) {

System.err.println("分布式事务执行失败,准备回滚: " + e.getMessage());

// 回滚事务

utx.rollback();

throw e;

} finally {

// 关闭资源

if (stmt1 != null) stmt1.close();

if (stmt2 != null) stmt2.close();

if (conn1 != null) conn1.close();

if (conn2 != null) conn2.close();

}

}

}

总结

Java 提供了多种事务实现方式,每种方式都有其适用场景:

JDBC 原生事务:适合简单应用,直接操作数据库连接,控制粒度细。Spring 编程式事务:适合需要在代码中精确控制事务边界的场景。Spring 声明式事务:最常用的方式,通过注解或配置实现,代码侵入性低,适合大多数企业应用。EJB 事务:适合使用 EJB 技术的分布式企业应用。分布式事务:适合跨多个数据源或服务的事务场景,实现复杂但必要时不可替代。

选择合适的事务管理方式需要根据应用的架构、复杂度和性能要求来决定。在实际开发中,Spring 声明式事务因其简洁性和灵活性而被广泛采用。

相关推荐

给WiiUPad增加4倍续航的方法
bet878365

给WiiUPad增加4倍续航的方法

📅 07-09 👁️ 603
初学必看
365bet官网娱乐

初学必看

📅 10-06 👁️ 3834
姞慧的意思
365bet官网娱乐

姞慧的意思

📅 11-29 👁️ 1160