目录
一、初识事务
二、mysql数据库的事务处理
三、JDBC封装mysql的事务处理
四、spring中的事务处理
五、分布式事务
一、初识事务
事务其实是针对的具体的数据库操作,以电商平台中的提交订单、扣减库存为例,这里会同时操作两张表写订单表、修改库存表:如果提交订单成功,但扣减库存失败,就会导致订单中的商品数大于库存的实际商品数,导致部分订单因为没有库存而无法出库;如果提交订单失败,但扣减库存成功,会导致实际库存大于0,但库存表显示为0的情况,导致实际有库存,但用户无法下单。这时就必须借助事务控制,把提交订单、扣减库存两个操作封装为一个原子操作,即要么两个操作都成功,否则对其中一个执行成功的操作进行回滚。
事务控制的本质是“并发控制”的最小单位,在“同一个数据库中”,要求对一批记录进行 insert、update、delete操作,要么全部成功,否则回滚已经执行的insert、update、delete操作,是一个不可分割的工作单位。
上述内容描述了事务的两个注意点:
1、事务处理是针对的数据库,或者说传统的关系型数据库,如msyql、oracle等。对于nosql数据的事务处理,只能通过自己写代码实现类似的“rollback”操作。
2、事务处理是在同一个数据库中。跨数据库的事务处理,其实就是我们平常说的“分布式事务”,归根结底还是要具体到每个数据库中分别进行事务处理。
事务的4大特性-ACID:
A、原子性(Atomicity):事务是数据库的逻辑工作单位,事务中包括的所有操作要么全做,要么全不做。
B、一致性(Consistency):事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
C、隔离性(Isolation):一个事务的执行被其他事务干扰的程度,可以设置不同的隔离级别来完成。4种隔离级别:
RU(Read Uncommitted最低,什么都不做,事务之间相互干扰);
RC(Read Committed 只有在事务提交后,其更新结果才会被其他事务看见,解决脏读);
RR(Repeated Read 在一个事务中,对于同一份数据的读取结果总是相同的,解决脏读、不可重复读)mysql事务的默认隔离级别(通过执行select @@tx_isolation;查看,也可以更改);
Serialization(所有事务都按照串行化执行,隔离级别最高,但并发性最差)。
D、持久性(Durability)一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
事务与锁的关系:事务的隔离级性,是通过对表或记录进行加锁实现的,通过控制锁的粒度来实现不同的隔离级别(RU不加锁,Serialization对整个过程进行加锁)。
事务与线程的关系:一个事务属于一个线程,一个线程里可以有多个事务,所谓事务的传播性,指的是采用spring管理事务时,在同一个线程中如果遇到多个事务切面,是否新开事务的策略,spring事务处理部分再详细讲解。也就是说所谓的事务传播性,其实是spring对数据库事务处理的一种封装。
二、mysql数据库的事务处理
这里以msyql数据库的事务处理为例进行分析。
首先mysql默认情况下是 事务是“自动提交的”,可以通过执行set autocommit = 0;改为手动提交,也就是开启事务,当然也可以使用start transction或者begin 开启事务,如果事务中中一条语句执行失败,则执行rollback命令 进行回滚,否则执行commit提交事务:
1、begin;//开启事务
2、执行多条insert、update、delete语句
3、判断结果执行commit或者rollback
If(error)
commit;
else
rollback;
对于rollback还可以设置回滚点,默认是全部回滚,这里就不详细讲解。
在java中,其实是对这个过程的一个封装,有一个try catch 对整个过程进行包裹,如果出现异常,就执行rollback,否则执行commit。下面来看下jdbc事务处理:
三、JDBC封装mysql的事务处理
JDBC的事务处理,实际上是对mysql事务处理的封装,本质上最终还是会解释成上述mysql事务处理语句,在msyql服务端执行。上述msyql对应的事务处理命令,会被一些java代码代替:
public class JDBCTrans { public void test1(){ Connection conn = null; PreparedStatement ps1 = null; PreparedStatement ps2 = null; Savepoint sp = null; try{ Class.forName("com.MySQL.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql:///xxxxx", "root", "root"); conn.setAutoCommit(false);//开启事务 ps1 = conn.prepareStatement("update xxxx set xxxx where id=?"); ps1.setInt(1, 1); ps1.executeUpdate(); int i = 1/0;//模拟异常 ps2 = conn.prepareStatement("update xxxx set xxxx where id=?"); ps2.setInt(1, 2); ps2.executeUpdate(); conn.commit(); //统一提交事务 }catch (Exception e) { e.printStackTrace(); try { conn.rollback(); //事务回滚 } catch (SQLException e1) { e1.printStackTrace(); } }finally{ // close connect source } } }
Jdbc事务处理代码:
1、conn.setAutoCommit(false); 开启事务类似 最终翻译成mysql中的 “set autocommit = 0;”命令。
2、conn.commit();统一提交事务,最终翻译成mysql中的 “commit;”命令。
3、conn.rollback();事务回滚,最终翻译成msyql中的“rollback;”命令。
四、spring中的事务处理
我们可以看到JDBC的事务处理方式非常繁琐,开启事务、提交事务、回滚事务这些代码与数据库连接、数据库操作、异常处理等代码逻辑混在一起,难以复用和维护。Spring为了简化这些操作,提供了统一的事务处理解决方案。
Spring中的事务处理有两种方式:声明式事务处理、编程式事务处理。其本质是对jdbc事务处理的再一次封装。相对于编程式事务处理,声明式事务处理的好处是,业务代码与事务处理配置完全分离。
Spring事务处理演进过程:
1、编程式:直接通过TransactionDefinition、PlatformTransactionManager、TransactionStatus 通过编程的方式完成事务处理。这种代码的写法有点类似直接使用jdbc做事务处理:
private PlatformTransactionManager txManager; public void testPlatformTransactionManagerForLowLevel1() { DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); Connection conn = xxxxxx;//jdbc Connection try { PreparedStatement pstmt = conn.prepareStatement("update xxxxxx"); pstmt.setString(1, "test"); pstmt.execute(); conn.prepareStatement("DELETE xxxxxx").execute(); txManager.commit(status); } catch (Exception e) { status.setRollbackOnly(); txManager.rollback(status); } finally { //释放资源; } }
Xml 配置里需要定义txManager对象,可以使用DataSourceTransactionManager(是PlatformTransactionManager的孙子实现类)。这种方式几乎不会有人去使用。
2、编程式:使用TransactionTemplate,其实是对第一种方式的改进,通过过观察第一种方式,可以发现事务处理,都是类似的模式(开启事务,提交事务,回滚事务,异常处理,资源释放),可以把这些内容抽象成一个模板,而不用每次都去编写类似的代码。TransactionTemplate使用示例:
private PlatformTransactionManager txManager; public TransactionTemplate getDataSourceTransactionManager() { return new TransactionTemplate(this.txManager); } public void doTrans(){ TransactionTemplate template = getDataSourceTransactionManager(); template.execute(new TransactionCallback() { @Override public Object doInTransaction(TransactionStatus status) { try { //一系列需要在同一个事务中处理的操作 //插入表 //修改表 } catch (Exception e) { status.setRollbackOnly(); throw new RuntimeException(e); } return status; } }); }
Xml配置跟第一种方式一样,需要定义txManager对象,可以使用DataSourceTransactionManager(是PlatformTransactionManager的孙子实现类):
<bean id=" txManager " class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref bean="dataSource" /> //数据源配置省略 </property> </bean>
Java代码相对于第一种方式简洁了很多,目前在一些老系统中,还有很多事物处理是采用的TransactionTemplate。
可以看到编程式事务处理,事务代码与业务代码严重耦合。接下来的声明式事务处理,主要就是解决代码耦合性问题,整体上更加简洁。
3、声明式:TransactionInterceptor,对每个需要事务处理的对象,通过配置的方式创建代理对象:
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="txManager"/> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop> //事务传播性配置 </props> </property> </bean> <bean id="userService" class="com.xxx.UserServiceImpl"> </bean> <bean id="userServiceTrans" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="userService"/> <property name="interceptorNames"> <list> <idref bean="transactionInterceptor"/> </list> </property> </bean>
4、声明式:TransactionProxyFactoryBean,这种方式是对第三种方式的优化,第三种方式需要为每一个需要做事务处理的对象创建三个bean(transactionInterceptor、userService、userServiceTrans)。TransactionProxyFactoryBean可以把transactionInterceptor和userServiceTrans合二为一:
<bean id="userService" class="com.xxx.UserServiceImpl"> </bean> <bean id="userServiceTrans" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target" ref="userService"/> <property name="transactionManager" ref="txManager"/> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
第三、四种都是声明式事务处理,这两种方式本质上是一样的,基于拦截器 责任链模式只是这里的责任链只有1个,在责任链只有1个的时候,其实就退化为代理模式。总所周知,spring里处理代理模式有个高大上的名字-----spring aop。接下来讲解的两种事务处理,都是基于spring aop的方式,也是目前最为推荐的事务处理方式。
5、声明式:spring aop <tx>命名空间,xml配置方式如下:
<tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED"/> <tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="myPointcut" expression="execution(* com.xxx.xxx.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="bankPointcut"/> </aop:config>
<tx:advice>用于定义通知,并在aop配置<aop:config>中引用这个通知,<aop:config>是标准的Spring aop配置方式,详细了解spring aop请点击这里http://moon-walker.iteye.com/blog/2381532。
这里重点说下<tx:method name="*">,这里的name属性用于指定方法名,可以使用”*”进行前缀、后缀匹配。另外<tx:method>还有几个重要的属性:
propagation:指定spring事务的传播性,可选配置见本节末尾。
isolation:指定事务的隔离级别,文章开头已经讲解。
read-only:表示是否为只读事务。
<tx>命名空间的配置方式,可以实现与业务代码完全解耦,没有任何侵入性。而且配置简单,在事务处理类比较集中的情况下(比如事务处理类都在同一个目录下),强烈推荐使用。
6、声明式:@Transactional注解,这种方式本质上也是基于spring aop,这种在方法上加注解的方式,可以更加细粒度的控制事务,如果需要事务处理的类比较分散,采用第五种方式不是很好统一制定切面的时候,采用@Transactional注解,也不失为一种好方式,xml配置很简单,启动注解即可:
<tx:annotation-driven transaction-manager="txManager"/>
在需要进行事务处理的方法上进行注解:
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED, readOnly = false,timeout = 100) public void transfer(Object bean) { //一系列的在同一个事务中进行的操作 //修改 //插入 //删除 }
@Transactional注解支持的参数与前面<tx:method>的支持的属性完全相同,除了这里提到的propagation、Isolation、readOnly、timeout 还有rollbackFor、noRollbackFor(指定回滚异常)等,具体可以打开Transactional的注解定义看下源码。
这里把spring支持的propagation传播行为,及说明列举如下:
a、PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
b、PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
c、PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
d、PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
e、PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
f、PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
g、PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。
spring默认为PROPAGATION_REQUIRED,假设m1、m2、m3三个方法都存在事务处理,在同一个线程内先执行m1调用m2再调用m3,采用PROPAGATION_REQUIRED配置,会在同一个事务中进行。
tips:建议使用第五、六两种spring声明式事务处理方式,这里对前面四种的讲解只是为了描述spring事务处理演进过程。其实第五、六两种事务处理的区别,就是spring aop基于xml配置与基于注解的区别。
五、分布式事务
最后简单说下分布式事务(不是本次总结的重点),spring支持的jta的分布式事务处理(不要跟spring jpa搞混了),它包括事务管理器(Transaction Manager)和一个或多个支持 XA 协议的资源管理器 ( Resource Manager ) 两部分,本质上,是把事务处理分配到各个数据库节点上,有一个统一的调度者同步各个节点的数据库操作状态,再统一通知各个节点进行事务提交。
基于XA协议的分布式实现,又分为两阶段提交和三阶段提交。但都无法很好的解决一致性问题,同时由于存在等待所有节点同步状态等问题,资源开销也比较大。
另外也可以基于zookeeper实现分布式式事务处理,可以很好的解决一直性问题,zookeeper底层主要通过实现Paxos算法来实现分布式的一直性问题。但也存在开销大的问题。
目前在高并发环境下,主流的做法是通过各种补偿机制实现最终一致性,比如通过mq消息通知延迟处理,通过加日志,手工处理,或者定时worker处理等。其实就是根据具体业务对一致性的容忍程度,采取补偿措施,这些操作已经不是传统意义上的事务处理了,也就是所谓的“柔性事务”处理。如果继续扩展的话还会扯到比较火热的BASE理论、CAP原则,其实都是一些理论上的东西,这里就不再进行深入扩展。
相关推荐
这篇博客将深入探讨Spring 2.0中的事务处理机制,以及如何通过`applicationContext.xml`配置文件来设置和管理事务。 首先,让我们理解什么是事务。事务是一组数据库操作,这些操作被视为一个单一的工作单元,要么...
Spring框架的声明式事务处理是Java企业级应用中不可或缺的一部分,它为开发者提供了一种方便、高效的方式来管理事务。在Spring中,事务管理分为编程式和声明式两种方式,而声明式事务处理则是通过配置来控制事务的...
### AOP与Spring事务处理详解 #### 一、引言:为什么使用框架和设计模式? 在软件开发领域,设计模式和框架是两个重要的概念。设计模式作为一种指导思想,能够帮助开发者更好地解决常见的软件设计问题,确保系统...
这是在java里使用到spring的配置文件里,添加事务处理过程,以至于可以回滚事务,当中使用到拦截器。
在企业级Java应用开发中,事务处理是核心功能之一,确保数据的一致性和完整性。本篇文章将深入探讨Spring与Hibernate整合下的事务管理,帮助开发者理解并掌握这一关键知识点。 首先,Spring框架提供了声明式事务...
Spring 中的事务处理可以分为两种方式:声明式事务处理和编程式事务处理。声明式事务处理通过 AOP 的实现,把事务管理代码作为方面封装到业务代码中,使得事务管理代码和业务代码解藕。这使得事务管理变得更加灵活...
Spring事务管理的目的是确保数据的一致性和完整性,尤其是在多操作、多资源的环境中。本Demo将深入探讨Spring如何实现事务的管理。 首先,Spring提供了两种主要的事务管理方式:编程式事务管理和声明式事务管理。 ...
本篇文章将聚焦于Spring事务处理中ThreadLocal的使用,以及如何通过源码理解和应用这个工具。 首先,了解Spring事务管理的基本概念。在多线程环境中,事务管理是至关重要的,它负责确保一组数据库操作要么全部成功...
Spring 提供了声明式事务管理,允许开发者在不编写事务管理代码的情况下实现事务控制,极大地简化了事务处理。 实验环境主要包括 Eclipse 或 MyEclipse 开发工具,以及 Spring 4.0 及以上版本,JDK 1.7 及以上版本...
在现代软件开发中,事务处理是非常关键的一部分,特别是在涉及多个数据操作时。Spring框架提供了强大的事务管理能力,可以方便地集成到应用程序中。Spring支持两种类型的事务管理:编程式事务管理和声明式事务管理。...
总的来说,Spring事务管理是一个强大的工具,它简化了事务处理的复杂性,使得开发者能够专注于业务逻辑,而无需过多关注事务的细节。通过理解和使用Spring事务流程图,我们可以更好地设计和优化我们的应用程序,确保...
Spring框架的声明式事务处理是其企业级应用中的核心特性之一,它允许开发者通过配置来管理事务,而无需在代码中显式地控制事务的开始、提交和回滚。这种方式极大地提高了代码的可读性和可维护性。在这个"spring声明...
总的来说,Spring事务管理通过其强大的声明式事务处理能力和对各种事务策略的支持,使得开发者能够轻松地在应用程序中实现高效、一致的事务处理。通过理解并合理运用上述知识点,开发者可以构建出稳定、健壮的分布式...
Spring 事务处理是Java开发中一个至关重要的概念,特别是在企业级应用中,它确保了数据的一致性和完整性。Spring 提供了两种主要的事务管理方式:编程式事务管理和声明式事务管理。本篇将深入探讨Spring声明式事务...
本文将深入探讨Spring事务管理的源码,理解其背后的实现机制。 首先,Spring事务管理有两种主要模式:编程式事务管理和声明式事务管理。编程式事务管理通过调用`PlatformTransactionManager`接口提供的方法进行显式...
本教程将深入探讨如何在Spring框架中利用`TransactionInterceptor`进行声明式事务管理,与Hibernate集成实现高效的数据库事务控制。 首先,了解事务管理是至关重要的。事务是一组数据库操作,这些操作要么全部成功...
本文将深入探讨在"spring事务操作试验"中涉及的关键知识点,并结合提供的资源进行详细阐述。 首先,Spring事务管理的核心概念是ACID(原子性、一致性、隔离性和持久性),这是所有事务系统的基础。在Spring中,事务...
Spring框架的事务管理机制是在Java开发环境中非常重要的一个组成部分,它能够帮助开发者简化事务处理的复杂度,提高应用程序的一致性和可靠性。Spring事务管理的核心是基于AOP(面向切面编程)来实现的。 **Spring...
其中,Spring的分布式事务管理是其核心特性之一,它允许开发者在分布式系统环境中处理复杂的事务逻辑。本篇将深入探讨Spring如何实现分布式事务,以及涉及到的相关技术。 首先,分布式事务是指在多个数据库或者服务...
本教程将深入探讨如何在Spring中实现自定义事务管理器、编程式事务处理以及声明式事务`@Transactional`的使用。 首先,让我们了解事务管理的基本概念。事务是一组数据库操作,这些操作要么全部执行,要么全部回滚,...