java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java实现事务方法

Java中实现事务的几种方法代码示例

作者:阿贾克斯的黎明

事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消,这篇文章主要介绍了Java中实现事务的几种方法,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

事务是数据库操作中的重要概念,它确保了一组操作要么全部成功,要么全部失败,从而保证数据的一致性和完整性。在 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:

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 方式):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    
    <!-- 数据源配置 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/testdb"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    </bean>
    
    <!-- 事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- 配置TransactionTemplate -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
        <!-- 可以设置默认的事务属性 -->
        <property name="isolationLevelName" value="ISOLATION_READ_COMMITTED"/>
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
    </bean>
    
    <!-- JdbcTemplate配置 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- 业务类 -->
    <bean id="transactionExample" class="SpringProgrammaticTransactionExample">
        <constructor-arg ref="transactionTemplate"/>
        <constructor-arg ref="jdbcTemplate"/>
    </bean>
    
    <!-- 启用事务注解支持 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

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 注解的主要属性:

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 事务的传播级别主要有:

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 提供了多种事务实现方式,每种方式都有其适用场景:

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

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

到此这篇关于Java中实现事务的几种方法的文章就介绍到这了,更多相关Java实现事务方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文