java长事务的@Transactional 详解
发布时间:2025-05-19 10:49:37 发布人:远客网络
一、java长事务的@Transactional 详解
1、Java中,通过@Transactional注解进行的事务管理通常被称为声明式事务。这个注解本质上是通过AOP(面向切面编程)在目标方法执行前后进行拦截,确保事务的开始、执行和管理。Spring在检测到@Transactional时,会自动获取数据库连接,开启事务,并将其绑定到ThreadLocal中。然而,如果方法中包含耗时操作,如第三方接口调用或复杂业务逻辑处理,可能导致数据库连接长时间占用,进而引发数据库连接池资源耗尽的问题,特别是当事务过长时,即所谓的“长事务”。
2、长事务是指运行时间较长,未及时提交的事务,也称为大事务。长事务可能导致的问题包括:数据库资源紧张,影响其他事务的执行,可能导致并发问题,甚至可能引发死锁。此外,数据一致性风险增大,因为事务可能在长时间内未完成,期间可能发生数据变化,但未被提交或回滚。
3、要避免长事务,可以采用编程式事务管理,通过TransactionTemplate类手动控制事务的开始和结束,这样可以更灵活地控制事务的范围,避免不必要的资源占用。
4、同时,合理划分方法也是避免长事务的有效手段。将事务相关的逻辑(如saveData())与非事务操作(如query()和validate())分开,避免在同一个类的同个方法中直接调用。注意,由于@Transactional注解的声明式事务依赖于Spring的代理机制,直接在类内方法间调用可能导致事务不生效,因此,正确的拆分方式应该是创建独立的代理对象或者在不同类的方法中进行调用。
二、@Transactional 详解
@Transactional是声明式事务管理编程中使用的注解
1)接口实现类或接口实现方法上,而不是接口类中。2)访问权限:public的方法才起作用。@Transactional注解应该只被应用到 public方法上,这是由 Spring AOP的本质决定的。系统设计:将标签放置在需要进行事务管理的方法上,而不是放在所有接口实现类上:只读的接口就不需要事务管理,由于配置了@Transactional就需要AOP拦截及事务的处理,可能影响系统性能。
1.接口中A、B两个方法,A无@Transactional标签,B有,上层通过A间接调用B,此时事务不生效。2.接口中异常(运行时异常)被捕获而没有被抛出。默认配置下,spring只有在抛出的异常为运行时 unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出 checked异常则不会导致事务回滚。可通过@Transactional rollbackFor进行配置。3.多线程下事务管理因为线程不属于 spring托管,故线程不能默认使用 spring的事务,也不能获取spring注入的 bean。在被 spring声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制。一个使用了@Transactional的方法,如果方法内包含多线程的使用,方法内部出现异常,不会回滚线程中调用方法的事务。
2.声明式事务管理实现方式:基于 tx和 aop名字空间的 xml配置文件
//基本配置// MyBatis自动参与到 spring事务管理中,无需额外配置,只要 org.mybatis.spring.SqlSessionFactoryBean引用的数据源与 DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。//标签的声明,是在 Spring内部启用@Transactional来进行事务管理,使用@Transactional前需要配置。
3.@Transactional注解@Transactional实质是使用了 JDBC的事务来进行事务控制的@Transactional基于 Spring的动态代理的机制
@Transactional实现原理:1)事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入 DataSource实例的某个与 DataSourceTransactionManager相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该 connection连接数据库,执行所有数据库命令。[不使用该 connection连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚](物理连接 connection逻辑上新建一个会话session; DataSource与 TransactionManager配置相同的数据源)2)事务结束时,回滚在第1步骤中得到的代理 connection对象上执行的数据库命令,然后关闭该代理 connection对象。(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)
4.声明式事务的管理实现本质:事务的两种开启方式:显示开启 start transaction| begin,通过 commit| rollback结束事务关闭数据库中自动提交 autocommit set autocommit= 0;MySQL默认开启自动提交;通过手动提交或执行回滚操作来结束事务
Spring关闭数据库中自动提交:在方法执行前关闭自动提交,方法执行完毕后再开启自动提交
// org.springframework.jdbc.datasource.DataSourceTransactionManager.java源码实现// switch to manual commit if necessary. this is very expensive in some jdbc drivers,// so we don't want to do it unnecessarily(for example if we've explicitly// configured the connection pool to set it already). if(con.getautocommit()){ txobject.setmustrestoreautocommit(true); if(logger.isdebugenabled()){ logger.debug("switching jdbc connection ["+ con+"] to manual commit");} con.setautocommit(false);}
关闭自动提交后,若事务一直未完成,即未手动执行 commit或 rollback时如何处理已经执行过的SQL操作?
C3P0默认的策略是回滚任何未提交的事务 C3P0是一个开源的JDBC连接池,它实现了数据源和 JNDI绑定,支持 JDBC3规范和 JDBC2的标准扩展。目前使用它的开源项目有 Hibernate,Spring等 JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互
------------------------------------------------------------------------------------------------------------------------------- 5. spring事务特性 spring所有的事务管理策略类都继承自 org.springframework.transaction.PlatformTransactionManager接口
public interface PlatformTransactionManager{ TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException;}
事务的隔离级别:是指若干个并发的事务之间的隔离程度
1.@Transactional(isolation= Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读,不可重复读)基本不使用 2.@Transactional(isolation= Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读) 3.@Transactional(isolation= Isolation.REPEATABLE_READ):可重复读(会出现幻读) 4.@Transactional(isolation= Isolation.SERIALIZABLE):串行化
事务传播行为:如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为
1. TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。3. TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。5. TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。6. TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。7. TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
1. value:主要用来指定不同的事务管理器;主要用来满足在同一个系统中,存在不同的事务管理器。比如在Spring中,声明了两种事务管理器txManager1, txManager2。然后,用户可以根据这个参数来根据需要指定特定的txManager。2. value适用场景:在一个系统中,需要访问多个数据源或者多个数据库,则必然会配置多个事务管理器。3. REQUIRED_NEW:内部的事务独立运行,在各自的作用域中,可以独立的回滚或者提交;而外部的事务将不受内部事务的回滚状态影响。4. ESTED的事务,基于单一的事务来管理,提供了多个保存点。这种多个保存点的机制允许内部事务的变更触发外部事务的回滚。而外部事务在混滚之后,仍能继续进行事务处理,即使部分操作已经被混滚。由于这个设置基于 JDBC的保存点,所以只能工作在 JDB C的机制。5. rollbackFor:让受检查异常回滚;即让本来不应该回滚的进行回滚操作。6. noRollbackFor:忽略非检查异常;即让本来应该回滚的不进行回滚操作。
三、使用@Transactional和不加@Transactional 有什么区别
1、transactional是Java的Spring框架中的一个注解,其设计理念是低侵入性,旨在更好地利用面向切面编程(AOP)技术。当使用transactional时,表明该类将被Spring框架处理或声明,具体操作依据transactional的配置而定。
2、反之,如果不使用transactional注解,Spring框架将不会自动识别该类,需要通过IoC容器进行反向控制。transactional注解的应用范围非常广泛,要深入了解其具体用法,还需要查阅更多相关资料。
3、使用transactional注解的优势在于,它能够帮助开发者更好地管理数据库事务,简化代码逻辑,提高代码的可读性和可维护性。而如果不使用transactional注解,开发者需要手动管理事务的开始、提交和回滚,这可能会导致代码变得复杂和难以维护。
4、此外,transactional注解还可以结合Spring的其他特性,如声明式事务管理,使得事务管理更加灵活和强大。因此,合理使用transactional注解,可以显著提升应用程序的开发效率和质量。
5、总之,transactional注解为开发者提供了一种便捷的方式来管理事务,使得代码更加简洁和易于维护。对于使用Spring框架进行开发的项目来说,合理利用transactional注解是非常重要的。
6、尽管transactional注解的应用范围广泛,但开发者仍需根据具体需求和项目特点,灵活选择是否使用该注解。过度依赖transactional注解可能会导致代码变得过于复杂,因此在实际开发中,需要权衡利弊,做出合理的选择。