当前位置: 首页 > 图灵资讯 > 技术篇> 详解Java之Spring框架中事务管理的艺术

详解Java之Spring框架中事务管理的艺术

来源:图灵教育
时间:2024-01-14 13:54:11

第1章:引言

大家好,我是小黑。今天我们来谈谈Spring框架中的事务管理。事务管理是开发小型应用程序和大型企业应用程序不可避免的话题。那么,为什么事务管理如此重要呢?假设在银行系统中转账时,资金从A账户中扣除,但未进入B账户,这是事务管理处理不当的结果。显然,我们需要一种机制来确保数据的完整性和一致性。

为此,Spring框架提供了一套优雅的事务管理机制,不仅强大,而且相对容易理解和实现。

第二章:事务管理基础

在深入Spring的事务管理之前,让我们先了解几个基本概念。简单地说,事务是一系列的操作,要么成功,要么失败。这涉及到ACID原理:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

  • 原子性:事务中的所有操作要么完成,要么不完成。
  • 一致性:数据库必须从一个一致性状态转移到另一个一致性状态。
  • 隔离:事务的执行不应受到其他事务的干扰。
  • 持久性:一旦事务提交,结果是永久性的。

在Java中,交易管理最初是通过JDBC实现的。但这种方法很快就变得无能为力,因为它需要程序员手动管理许多细节,而且很容易出错。Spring提供了一种声明式的交易管理方法,这大大简化了交易的处理方法。那么,Spring是如何做到的呢?让小黑给你看。

import org.springframework.transaction.annotation.Transactional;public class TransferService {    @Transactional    public void transferMoney(String fromAccountId, String toAccountId, Double amount) {        // 这是一些转账的业务逻辑        // 例如,从一个账户到另一个账户存款    }}

在这个代码中,@Transactional 注释是Spring事务管理的核心。只要该注释添加到该方法中,Spring就会自动处理该方法的事务。如果该方法成功实施,将提交事务;如有异常,事务将回滚。这就是声明事务管理的魅力:简单、直观、易于理解和使用。

相比之下,传统的事务管理模式需要我们手动控制事务的每个阶段,如开始、提交或回滚。这不仅代码量大,而且容易出错。小黑给大家看一个传统的JDBC事务管理示例:

import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class TraditionalTransaction {    public void transferMoney(String fromAccountId, String toAccountId, Double amount) {        Connection conn = null;        try {            conn = getConnection(); // 获取数据库连接            conn.setAutoCommit(false); // 开始事务            // 执行一系列数据库操作            // ...            conn.commit(); // 提交事务        } catch (SQLException e) {            if (conn != null) {                try {                    conn.rollback(); // 回滚事务发生错误                } catch (SQLException ex) {                    ex.printStackTrace();                }            }            e.printStackTrace();        } finally {            if (conn != null) {                try {                    conn.close(); // 关闭连接                } catch (SQLException e) {                    e.printStackTrace();                }            }        }    }    private Connection getConnection() {        // 这是获取数据库连接的代码        return null;    }}

从上面的例子可以看出,传统的方式使事务管理繁琐,容易出错。Spring的声明事务管理极大地简化了这一过程。

第三章:Spring框架中抽象事务的关键组件

抽象Spring事务的核心是PlatformTransactionManager接口。该接口为不同的事务管理策略提供了标准化的方法。不同的数据库和持久框架(如JDBC)、Hibernate)都有相应的实现。

来看看PlatformTransactionManager一个基本示例:

import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.TransactionDefinition;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.support.DefaultTransactionDefinition;public class TransactionManagerExample {    private PlatformTransactionManager transactionManager;    public void performTransaction() {        TransactionDefinition definition = new DefaultTransactionDefinition();                // 开始事务        TransactionStatus status = transactionManager.getTransaction(definition);        try {            // 执行业务逻辑            // ...            transactionManager.commit(status); // 事务提交        } catch (Exception e) {            transactionManager.rollback(status); // 事务回滚        }    }}

在这个例子中,小黑被使用PlatformTransactionManager明确一个事务的开始和结束。虽然这种方法比传统的JDBC事务管理更抽象,但它仍然需要手动控制事务的边界。

定义和传播事务

Spring中的事务是通过的TransactionDefinition界面定义。该界面包含各种与事务相关的属性,如隔离级别、超时、只读状态等。这些属性允许我们根据具体需要定制事务行为。

例如,如果小黑想创造一个新的、独立于当前事务的事务,他可以这样做:

import org.springframework.transaction.TransactionDefinition;import org.springframework.transaction.support.DefaultTransactionDefinition;DefaultTransactionDefinition def = new DefaultTransactionDefinition();def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

在这个代码中,PROPAGATION_REQUIRES_NEW 说明如果目前有事务,就挂起当前事务,创造新的事务。这种灵活性是Spring事务管理的一个非常强大的方面。

事务状态及回滚

Spring除了定义事务外,还提供了TransactionStatus接口跟踪事务的当前状态。这个接口允许我们检查事务是否已经完成,是否有回滚等。

在事务过程中,如果发生异常,Spring允许我们明确指定哪些异常应该触发事务回滚。这是通用的过@Transactional 注解的rollbackFor 属性实现:

import org.springframework.transaction.annotation.Transactional;@Transactional(rollbackFor = Exception.class)public void someTransactionalMethod() {    // ...}

在这个例子中,没有Exception异常类型会触发事务回滚。这种灵活的异常处理机制使事务管理更加强大和可靠。

第四章:声明事务管理

现在小黑带大家深入了解Spring的声明事务管理。在Spring中,声明事务管理是通过的@Transactional实现注释。这种方法的优点是极大地简化了代码,使事务管理更加直观和易于维护。

使用@Transactional注释

@Transactional注释可以应用于类别或方法。当应用于类别时,该类别中的所有公共方法都将被视为事务方法。当应用于该方法时,只有标记该注释的方法才被视为事务方法。

让我们看一个简单的例子:

import org.springframework.transaction.annotation.Transactional;import org.springframework.stereotype.Service;@Servicepublic class AccountService {    @Transactional    public void transferFunds(String fromAccountId, String toAccountId, Double amount) {        // 这是转账的业务逻辑        // 包括从一个账户和另一个账户加钱        // 如果在此过程中发生任何异常,事务将自动回滚    }}

这个例子中,transferFunds 方法上的@Transactional注释告诉Spring,这种方法需要在事务环境中运行。如果执行过程中出现异常,Spring将自动回滚事务。

事务传播行为

事务的传播行为定义了事务方法相互调用时的行为。Spring提供多种传播行为选项,如PROPAGATION_REQUIREDPROPAGATION_REQUIRES_NEW等等。可根据业务需要选择这些选项。

比如小黑想创造一个新的事务,不管目前有没有事务,都可以这样设置:

@Transactional(propagation = Propagation.REQUIRES_NEW)public void someMethod() {    // ...}
只读事务

如果事务只涉及数据读取,没有数据修改,小黑可以将事务标记为只读。这可以优化事务的执行,特别是在使用某些类型的事务管理器(如Hibernate事务管理器)时。

例如:

@Transactional(readOnly = true)public List<Account> findAllAccounts() {    // ...}
事务隔离等级

事务的隔离水平决定了事务可能受到其他并发事务的影响。Spring提供与J

例如,DBC具有相同的隔离等级,ISOLATION_READ_COMMITTEDISOLATION_SERIALIZABLE等等。小黑可根据具体数据一致性和并发要求选择合适的隔离级别。

例如,如果小黑想在事务中防止脏读,可以将隔离级别设置为 READ_COMMITTED

@Transactional(isolation = Isolation.READ_COMMITTED)public void someSensitiveOperation() {    // 这里的操作可以避免脏读}
异常回滚策略

在声明事务管理中,Spring默认情况下只有在异常和错误发生时才会回滚。然而,小黑可以定制回滚规则,并指定特定的异常类型来触发回滚。

例如,如果小黑想在特定检查型异常发生时回滚事务,可以这样设置:

@Transactional(rollbackFor = {CustomCheckedException.class})public void someOperation() throws CustomCheckedException {    // ...}

通过这些设置,我们可以非常灵活地控制事务行为,以满足不同的业务需求。声明事务管理不仅简单地使用事务,而且提高了代码的清晰度和可维护性。

第五章:编程事务管理何时使用编程事务管理

编程事务管理是在代码中显式控制事务的开始、提交和回滚。这种方法在需要细粒度控制事务的情况下非常有用。例如,当事务操作需要根据运行过程中的某些条件进行动态决定,或者在大事务中需要处理多个独立的小事务时。

使用Transactiontemplatet

Spring为编程事务管理提供了一个方便的类别:TransactionTemplate。这一类简化了编程事务管理,使我们能够更方便地实现事务逻辑。

来看看TransactionTemplate基本用法:

import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.support.TransactionCallback;import org.springframework.transaction.support.TransactionTemplate;public class ProgrammaticallyTransaction {    private final TransactionTemplate transactionTemplate;    public ProgrammaticallyTransaction(PlatformTransactionManager transactionManager) {        this.transactionTemplate = new TransactionTemplate(transactionManager);    }    public Object someBusinessLogic() {        return transactionTemplate.execute(new TransactionCallback<Object>() {            @Override            public Object doInTransaction(TransactionStatus status) {                // 这里写的是事务的业务逻辑                // 可根据需要调用statuss.setRollbackOnly()来回滚动事务                return null;                // 返回值可以是业务逻辑的结果            }        });    }}

在这个例子中,我们使用它TransactionTemplate定义一个事务块。execute接受一种方法TransactionCallback,它包含了在事务中执行的业务逻辑。如果在执行过程中没有异常抛出,事务将自动提交;如有异常抛出或显式调用status.setRollbackOnly(),事务将回滚。

使用Platformtransactionmanger

对于想要更深层次控制事务的小黑,可以直接使用PlatformTransactionManager。这种方法

它为事务操作提供了最完整的控制,但也意味着需要更多的代码和复杂性。

让我们来看看一个用途PlatformTransactionManager的例子:

import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.TransactionDefinition;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.support.DefaultTransactionDefinition;public class ManualTransaction {    private final PlatformTransactionManager transactionManager;    public ManualTransaction(PlatformTransactionManager transactionManager) {        this.transactionManager = transactionManager;    }    public void executeBusinessLogic() {        TransactionDefinition definition = new DefaultTransactionDefinition();        TransactionStatus status = transactionManager.getTransaction(definition);        try {            // 在这里进行具体的业务操作            // ...            transactionManager.commit(status); // 业务成功,提交事务        } catch (Exception e) {            transactionManager.rollback(status); // 异常,回滚事务            throw e;        }    }}

在这个代码中,我们首先创建了一个TransactionDefinition对象定义事务的属性,然后通过transactionManager.getTransaction(definition)开始新的事务,获得新的事务TransactionStatus对象。该对象可用于在执行过程中检查事务状态,并在必要时滚动事务。如果业务逻辑顺利完成,请调用transactionManager.commit(status)提交事务;如果发现异常,请调用transactionManager.rollback(status)来回滚事务。

虽然编程业务管理提供了更多的灵活性和控制,但它也带来了更多的复杂性。当我们选择使用哪种业务管理策略时,我们需要考虑业务逻辑的复杂性和对业务控制的需求。

在大多数情况下,声明式事务管理已经足够有用了,但当需要具体的事务控制逻辑时,编程式事务管理就显得尤为重要。

编程事务管理使我们能够准确地控制事务的每一个细节,这对于处理复杂的业务逻辑或根据不同的情况灵活处理事务非常有用。例如,在复杂的数据处理过程中,可能只有一些步骤需要事务控制,或者根据某些条件动态决定是否滚动事务。在这些情况下,编程事务管理提供了必要的灵活性和精确的控制。

第六章:Spring事务管理高级特征事务的传播行为

在Spring中,事务的传播行为定义了一种与现有事务相关的事务方法。这对于理解和设计复杂的事务逻辑至关重要。

  • PROPAGATION_REQUIRED:若目前有事务,则加入事务;若无,则新建事务。
  • PROPAGATION_REQUIRES_NEW:总是创造新的事务,如果有现有的事务,就挂起来。
  • PROPAGATION_SUPPORTS:如果目前有事务,则加入事务,如果没有,则以非事务方式执行。
  • ....更多的行为,比如PROPAGATION_MANDATORYPROPAGATION_NEVER等。

让我们举个例子:

@Transactional(propagation = Propagation.REQUIRES_NEW)public void someMethod() {    // 这种方法总是在新事务中运行}
事务隔离等级

事务的隔离水平决定了事务对其他并发事务的可见性。不同的隔离水平可以防止脏读、不重复读、幻读等问题。

  • ISOLATION_READ_UNCOMMITTED:允许读取未提交的数据变更可能导致脏读、不重复读和幻读。
  • ISOLATION_READ_COMMITTED:允许在一个事务中读取另一个已经提交的事务所做出的改变。
  • ISOLATION_REPEATABLE_READ:确保在事务中一次读取数据时,可以多次重复读取相同的数据。
  • ISOLATION_SERIALIZABLE:完全遵循ACID的原则,确保没有脏读、重复读和幻读。

例如:

@Transactional(isolation = Isolation.SERIALIZABLE)public void performHighlySensitiveOperation() {    // 这里的操作将在最严格的隔离级别下进行}
只阅读事务和加班设置
  • 只读事务:如果事务只是读取

在不更新数据的情况下,小黑可以将事务标记为只读。这样做可以帮助数据库优化事务,提高性能。

@Transactional(readOnly = true)public List<User> getUsers() {    // 这个事务只读取数据,不修改}
  • 加班设置:在Spring中,可以为事务指定加班时间。如果事务超过这个时间范围,就会自动回滚。这对避免长期占用资源非常有用。
@Transactional(timeout = 10) //10秒超时publicic void processLargeData() {    // 这个事务处理了大量的数据,但是如果超过10秒还没有完成,就会自动回滚}

在处理大量数据或复杂查询时,我们可以通过设置只读事务和超时时间来进一步优化事务的性能和稳定性。

同步和异常处理事务

在Spring事务管理中,事务同步是指事务状态与正在执行的业务逻辑之间的协调。Spring通过TransactionSynchronizationManager管理事务同步,确保资源(如数据库连接)在事务开始时打开,在事务结束时正确关闭。

异常处理也是事务管理的重要组成部分。默认情况下,Spring只在异常运行时回滚事务。但我们可以通过@Transactional注解的rollbackFor属性来自定义哪些异常应该触发回滚。

例如:

@Transactional(rollbackFor = {CustomException.class})public void serviceMethod() {    // 这种方法在抛出Customexception时会触发事务回滚}

通过理解和运用这些高级特性,我们可以在Spring中实现复杂而强大的事务管理策略。这些功能使Spring事务管理非常强大,满足了各种复杂业务场景的需求。

第七章:事务同步和异常处理事务同步

在Spring中,事务同步主要是指在事务过程中保持数据源、缓存、信息等资源的一致性。这是通过的TransactionSynchronizationManager它是Spring事务管理的核心组成部分之一。

TransactionSynchronizationManager注册事务可以同步回调,以便在事务的不同阶段进行特定的操作。例如,缓存可以在事务提交或回滚时清除,或更新一些与事务状态相关的数据。

让我们看一个示例:

import org.springframework.transaction.support.TransactionSynchronizationAdapter;import org.springframework.transaction.support.TransactionSynchronizationManager;public void registerSynchronization() {    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {        @Override        public void afterCommit() {            // 事务提交后执行的操作            // 例如,清除某些缓存        }        @Override        public void afterCompletion(int status) {            // 事务完成后执行的操作(无论是提交还是回滚)            // 例如,发送通知或更新状态        }    });}
异常处理

在spring的交易管理中,正确处理异常是确保交易正确回滚的关键。默认情况下,spring在遇到异常和错误时会回滚。对于检查异常,默认情况下不会触发回滚。

我们可以通过@Transactional注解中指定rollbackFornoRollbackFor属性定制哪些异常应该触发事务回滚,哪些不应该。

例如,当特定的检查异常发生时,小黑想要回滚:

import org.springframework.transaction.annotation.Transactional;@Transactional(rollbackFor = {MyCheckedException.class})public void myTransactionalMethod() throws MyCheckedException {    // 在抛出MycheckedException异常时,这里的代码会触发事务回滚}

相反,如果小黑想在特定操作中异常发生时不回滚事务,可以这样设置:

@Transactional(noRollbackFor = {MyRuntimeException.class})public void anotherTransactionalMethod() {    // 在抛出MyRuntimeException时,这里的代码不会触发事务回滚}

正确的异常处理策略对维持数据库的一致性,避免不必要的数据损失至关重要。在设计事务管理逻辑时,我们需要仔细考虑哪些操作可能会抛出异常,以及这些异常如何影响事务。

事务同步不仅有助于管理事务的内部资源,而且确保事务的外部效应(如缓存更新、信息发送等)与事务状态一致。灵活而强大的异常处理机制允许我们准确地控制事务的回滚行为,以更好地处理复杂的业务场景。通过掌握这些知识,我们可以更有信心地管理和优化我们的Spring应用程序。

第八章:最佳实践和常见问题
  1. 明智地选择事务边界:确保事务不会太大或太小。太大的事务可能会锁定太多的资源,影响性能;太小的事务可能无法有效保护数据的完整性。

  2. 合理使用声明和编程交易管理:虽然声明交易管理更简洁,但编程交易管理可能更适合在需要更详细控制的情况下进行。

  3. 避免事务中的远程呼叫:远程呼叫(如HTTP请求和远程呼叫)会增加事务持续时间,增加数据库锁定时间,影响系统性能。

  4. 在事务中不要处理太多的业务逻辑:尽量保持事务简洁,专注于数据访问操作,以减少事务执行时间。

  5. 仔细选择事务的隔离级别:不同的隔离级别有不同的性能影响。选择合适的隔离级别可以避免不必要的性能费用。

常见问题及解决方案
  1. 问题:事务不回滚解决方案:检查是否正确使用@Transactional回滚是否在适当的异常情况下触发。

  2. 问题:事务管理器配置不正确

    解决方案:确保事务管理器在Spring配置中得到正确声明,并且所有与事务相关的操作都在其管理范围内。

  3. 问题:事务传播行为不当造成的问题解决方案:了解不同事务传播行为的含义,根据具体的业务场景选择合适的传播行为。

  4. 问题:脏读、不重复读和幻读解决方案:通过设置适当的事务隔离级别来避免这些问题。例如,ISOLATION_REPEATABLE_READ可防止不能重复阅读,ISOLATION_SERIALIZABLE能防止幻读。

  5. 问题:长期事务影响系统性能解决方案:重新审视业务逻辑,将长期事务分为多个短期事务,或移除非关键操作。

例子:选择正确的事务传播行为

在使用Spring事务时,正确选择事务传播行为至关重要。例如,假设有一个服务类需要在现有事务中执行,可以使用PROPAGATION_REQUIRED

import org.springframework.transaction.annotation.Transactional;@Transactional(propagation = Propagation.REQUIRED)public class MyService {    public void performService() {        // 业务逻辑    }}

如果这种方法需要在自己的独立事务中执行,无论外部事务是否存在,都可以使用PROPAGATION_REQUIRES_NEW

@Transactional(propagation = Propagation.REQUIRES_NEW)public class MyService {    public void performIndependentService() {        // 独立的业务逻辑    }}