首页 > 编程语言 >Java 方法中循环调用具有事务的方法

Java 方法中循环调用具有事务的方法

时间:2024-07-06 12:30:47浏览次数:17  
标签:事务 调用 Java 示例 Spring Transactional 方法

在Java中,循环调用一个具有事务的方法时,需要特别注意事务的边界和管理。通常,事务的边界是由框架(如Spring)来控制的,确保方法执行时数据的完整性和一致性。然而,在循环中调用事务方法时,每个调用都可以被视为独立的事务,除非特别配置以允许跨多个方法调用共享同一事务。

1. Java 方法中循环调用具有事务的具体方法示例

下面,我将提供一个使用Spring框架的示例,其中包含一个服务层方法,该方法在循环中调用另一个具有事务注解的方法。请注意,默认情况下,Spring的@Transactional注解在每个方法调用时都会开启一个新的事务,除非配置为使用不同的传播行为。

1.1 示例环境

(1)Spring Boot 2.x;

(2)Maven 项目。

1.2 Maven 依赖

首先,确保我们的pom.xml文件中包含必要的Spring Boot和数据库相关依赖。这里只列出核心依赖:

<dependencies>  
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-data-jpa</artifactId>  
    </dependency>  
    <dependency>  
        <groupId>com.h2database</groupId>  
        <artifactId>h2</artifactId>  
        <scope>runtime</scope>  
    </dependency>  
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-web</artifactId>  
    </dependency>  
</dependencies>

1.3 实体类

假设我们有一个简单的User实体类:

import javax.persistence.Entity;  
import javax.persistence.GeneratedValue;  
import javax.persistence.GenerationType;  
import javax.persistence.Id;  
  
@Entity  
public class User {  
    @Id  
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    private Long id;  
    private String name;  
  
    // 省略构造方法、getter和setter  
}

1.4 仓库接口

import org.springframework.data.jpa.repository.JpaRepository;  
  
public interface UserRepository extends JpaRepository<User, Long> {  
}

1.5 服务层

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Transactional;  
  
@Service  
public class UserService {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    // 假设这个方法需要在循环中调用另一个事务方法  
    public void processUsers() {  
        for (int i = 0; i < 10; i++) {  
            // 每次循环调用一个事务方法  
            createUser("User" + i);  
        }  
    }  
  
    @Transactional  
    public void createUser(String name) {  
        User user = new User();  
        user.setName(name);  
        userRepository.save(user);  
        // 这里可以模拟一些业务逻辑,如果抛出异常,则当前事务会回滚  
        // throw new RuntimeException("Failed to create user");  
    }  
}

1.6 控制器

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
@RestController  
public class UserController {  
  
    @Autowired  
    private UserService userService;  
  
    @GetMapping("/process")  
    public String processUsers() {  
        userService.processUsers();  
        return "Users processed successfully!";  
    }  
}

1.7 注意

(1)在上面的例子中,createUser方法被@Transactional注解标记,意味着它将在自己的事务中运行。由于processUsers方法没有@Transactional注解,所以循环中的每次createUser调用都将独立开启和关闭事务。

(2)如果需要所有createUser调用在同一个事务中执行(例如,要求所有用户创建成功或全部失败),我们需要将@Transactional注解移动到processUsers方法上,并可能调整传播行为(尽管在这种情况下,默认的传播行为REQUIRED应该就足够了)。

1.8 结论

这个示例演示了如何在Java(特别是Spring框架中)循环调用具有事务的方法。根据我们的具体需求,我们可能需要调整事务的传播行为或边界。

2.其他方法示例

在Java中,特别是在使用Spring框架时,管理事务的方式不仅仅是通过@Transactional注解。虽然@Transactional是Spring中最常用和推荐的方式,但还有其他几种方法可以实现类似的功能。以下是一些替代方案:

2.1 编程式事务管理

编程式事务管理允许我们通过代码直接控制事务的开始、结束以及异常时的回滚。Spring提供了TransactionTemplatePlatformTransactionManager来帮助进行编程式事务管理。

示例:使用TransactionTemplate

@Autowired  
private TransactionTemplate transactionTemplate;  
  
@Autowired  
private UserRepository userRepository;  
  
public void processUsers() {  
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
        @Override  
        protected void doInTransactionWithoutResult(TransactionStatus status) {  
            for (int i = 0; i < 10; i++) {  
                try {  
                    createUser("User" + i);  
                } catch (RuntimeException ex) {  
                    // 可以在这里决定是回滚整个事务还是只处理当前异常  
                    status.setRollbackOnly();  
                    throw ex; // 可选,根据需要抛出或处理异常  
                }  
            }  
        }  
  
        private void createUser(String name) {  
            User user = new User();  
            user.setName(name);  
            userRepository.save(user);  
        }  
    });  
}

注意:在这个例子中,整个循环被包裹在一个事务中,这意味着如果循环中的任何createUser调用失败,整个事务将回滚。

2.2 声明式事务管理(除了@Transactional

虽然@Transactional是声明式事务管理的典型方式,但Spring也支持通过XML配置来实现相同的功能。不过,在现代Spring应用中,这种方式已经不太常见了。

示例XML配置(简化版):

<beans ...>  
  
    <!-- 事务管理器配置 -->  
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource" ref="dataSource"/>  
    </bean>  
  
    <!-- 声明事务代理 -->  
    <tx:advice id="txAdvice" transaction-manager="transactionManager">  
        <tx:attributes>  
            <tx:method name="*" propagation="REQUIRED"/>  
        </tx:attributes>  
    </tx:advice>  
  
    <!-- 定义切入点 -->  
    <aop:config>  
        <aop:pointcut id="serviceOperation" expression="execution(* com.example.service.*.*(..))"/>  
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/>  
    </aop:config>  
  
    <!-- 其他bean定义... -->  
  
</beans>

注意:这个XML配置需要与Spring的AOP命名空间一起使用,并且dataSource bean 也需要被定义。

2.3 使用AOP(面向切面编程)手动创建事务

我们可以通过Spring的AOP框架手动拦截方法调用,并在调用前后添加事务管理逻辑。这通常比直接使用@TransactionalTransactionTemplate更复杂,因此不推荐除非有特殊需求。

使用AOP手动管理事务通常不是推荐的做法,因为它涉及到底层事务API的直接调用,这可能会使代码变得复杂且难以维护。不过,为了说明目的,我们可以想象一个切面,它在方法调用前后分别开启和提交/回滚事务。

示例AOP切面(概念性):

@Aspect  
@Component  
public class TransactionAspect {  
  
    @Autowired  
    private PlatformTransactionManager transactionManager;  
  
    @Around("execution(* com.example.service.*.*(..))")  
    public Object manageTransaction(ProceedingJoinPoint pjp) throws Throwable {  
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());  
        try {  
            Object result = pjp.proceed(); // 执行方法  
            transactionManager.commit(status);  
            return result;  
        } catch (RuntimeException e) {  
            transactionManager.rollback(status);  
            throw e;  
        }  
    }  
}

注意:这个示例非常简化,并且没有处理事务传播行为、只读事务等高级特性。此外,它也没有考虑事务的同步和并发问题。

2.4 数据库层面的事务

在某些情况下,我们也可以依赖数据库本身的事务支持。例如,使用JDBC时,我们可以手动管理Connection对象的setAutoCommit(false)来开启事务,并在完成所有数据库操作后调用commit()rollback()。然而,这种方法通常与Spring的事务管理集成不佳,并且容易出错。

在数据库层面管理事务通常涉及使用JDBC的Connection对象。这不是Spring特有的,但Spring提供了对JDBC的封装(如JdbcTemplate),尽管它通常与Spring的事务管理一起使用。

JDBC示例(非Spring特有):

Connection conn = dataSource.getConnection();  
try {  
    conn.setAutoCommit(false);  
    // 执行SQL语句...  
    conn.commit();  
} catch (SQLException e) {  
    if (conn != null) {  
        try {  
            conn.rollback();  
        } catch (SQLException ex) {  
            // 处理回滚异常  
        }  
    }  
    throw e; // 重新抛出异常  
} finally {  
    if (conn != null) {  
        try {  
            conn.close();  
        } catch (SQLException e) {  
            // 处理关闭连接异常  
        }  
    }  
}

2.5 使用分布式事务

如果我们的应用需要跨多个数据库或服务进行事务管理,那么我们可能需要使用分布式事务解决方案,如JTA(Java Transaction API)、Atomikos、Bitronix或Spring Cloud的分布式事务支持(如通过Spring Cloud Data Flow)。

分布式事务涉及多个服务或数据库之间的协调。Spring Cloud提供了对分布式事务的支持,但通常依赖于外部服务(如Atomikos、Bitronix或基于JTA的实现)。

示例(概念性,使用Spring Cloud和Atomikos):

这通常涉及在Spring Boot应用中配置Atomikos作为JTA事务管理器,并在需要分布式事务的服务中注入该事务管理器。然后,我们可以使用@Transactional注解来标记需要分布式事务支持的方法。但是,具体的配置将取决于我们使用的Spring Cloud版本和分布式事务解决方案。

2.6 结论

对于大多数Spring应用来说,@Transactional注解是管理事务的首选方法。然而,根据我们的具体需求和场景,我们可能需要考虑上述其他方法。在选择时,请权衡每种方法的优缺点,并选择最适合我们应用需求的方案。

由于篇幅和复杂性的限制,我将为每种方法提供一个简化的代码示例框架或思路,而不是完整的可运行代码。这些示例旨在说明概念和方法,而不是直接用于生产环境。

标签:事务,调用,Java,示例,Spring,Transactional,方法
From: https://blog.csdn.net/m0_72958694/article/details/140205265

相关文章

  • 基于java+springboot+vue实现的图书商城管理系统(文末源码+Lw)283
     摘 要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本图书商城管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理......
  • 基于java+springboot+vue实现的旅游推荐系统(文末源码+Lw)280
    摘 要传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装旅游推荐系统软件来发挥其高效地信息处理的作用,可以规范信息管理流程,让管理工作可以系统化和程序化,同时,旅游推荐系统的有效......
  • 基于java+springboot+vue实现的流浪动物管理系统(文末源码+Lw)277
     摘    要在如今社会上,关于信息上面的处理,没有任何一个企业或者个人会忽视,如何让信息急速传递,并且归档储存查询,采用之前的纸张记录模式已经不符合当前使用要求了。所以,对流浪动物信息管理的提升,也为了对流浪动物信息进行更好的维护,流浪动物管理系统的出现就变得水到渠成......
  • 基于java+springboot+vue实现的药店管理系统(文末源码+Lw)285
    摘   要传统信息的管理大部分依赖于管理人员的手工登记与管理,然而,随着近些年信息技术的迅猛发展,让许多比较老套的信息管理模式进行了更新迭代,药品信息因为其管理内容繁杂,管理数量繁多导致手工进行处理不能满足广大用户的需求,因此就应运而生出相应的药店管理系统。本药店......
  • 基于java+springboot+vue实现的药店管理系统(文末源码+Lw)285
    摘   要传统信息的管理大部分依赖于管理人员的手工登记与管理,然而,随着近些年信息技术的迅猛发展,让许多比较老套的信息管理模式进行了更新迭代,药品信息因为其管理内容繁杂,管理数量繁多导致手工进行处理不能满足广大用户的需求,因此就应运而生出相应的药店管理系统。本药店......
  • Java面试八股之MySQL存储引擎都有哪些
    MySQL存储引擎都有哪些MySQL提供了多种存储引擎,每种引擎都有其独特的特性和用途。以下是一些常用的MySQL存储引擎:InnoDB默认存储引擎(自MySQL5.5版本起)。支持事务(ACID属性)、行级锁定和外键约束。使用B+树作为索引结构。适合需要高并发的事务处理和高可靠性的场景。MyISA......
  • js中数组方法总结
    改变原数组的方法有:栈方法push:数组末尾追加任意数量的元素,返回修改后数组的长度pop:数组末尾移除最后一项,返回移除的项队列方法unshift:数组前端添加任意个项并返回新数组的长度shift:移除数组中的第一项并返回改该项重排序方法sort:默认情况按照升序排列数组reserve:翻......
  • 一种尽可能减小内存占用的数据结构设计方法
         背景:以三维点为例,随着采集设备的日新月异,三维点的属性信息也越来越多(例如颜色、强度、回波信息、gps时间等);导致点云数据在处理时加载到计算机中所需要的内存空间也越来越大,但是有些数据往往只有x、y、z三个坐标值,则不需要为其开辟多余的内存空间,那一套统一的数据结......
  • Java计算机毕业设计信阳市多目的地同步导航系统(开题+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信阳市城市化进程的加快,交通网络日益复杂,市民出行需求日益多样化,对导航系统的要求也愈发提高。传统单一目的地的导航系统已难以满足市民在日常出......
  • 每天一道Java面试题系列之--Spring事务的实现原理
    面试题描述Spring事务的实现原理,并解释以下概念:PlatformTransactionManager 接口的作用是什么?什么是事务的传播行为?声明式事务和编程式事务有什么区别?@Transactional 注解是如何工作的?题解1. PlatformTransactionManager 接口PlatformTransactionManager是Spring事务......