《Spring框架事务笔记》

基础概念

   事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。
   数据库向用户提供保存当前程序状态的方法,叫事务提交(commit);当事务执行过程中,使数据库忽略当前的状态并回到前面保存的状态的方法叫事务回滚(rollback)
事务特性(ACID)

  • 原子性(atomicity):将事务中所做的操作捆绑成一个原子单元,即对于事务所进行的数据修改等操作,要么全部执行,要么全部不执行。
  • 一致性(Consistency):事务在完成时,必须使所有的数据都保持一致状态,而且在相关数据中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构都应该是正确的。
  • 隔离性(Isolation):由并发事务所做的修改必须与任何其他事务所做的修改相隔离。事务查看数据时数据所处的状态,要么是被另一并发事务修改之前的状态,要么是被另一并发事务修改之后的状态,即事务不会查看由另一个并发事务正在修改的数据。这种隔离方式也叫可串行性。
  • 持久性(Durability):事务完成之后,它对系统的影响是永久的,即使出现系统故障也是如此。

   事务隔离意味着对于某一个正在运行的事务来说,好像系统中只有这一个事务,其他并发的事务都不存在一样。大部分情况下,很少使用完全隔离的事务。但不完全隔离的事务会带来如下一些问题

  • 脏读(Dirty Read):一个事务读取到另一个事务没有提交的数据。如果第二个应用程序使用了第一个应用程序修改过的数据,而这个数据处于未提交状态,这时就会发生脏读。第一个应用程序随后可能会请求回滚被修改的数据,从而导致第二个事务使用的数据被损坏,即所谓的“变脏”。
  • 不可重读(Unrepeatable Read):一个事务读到另一个事务已提交的数据(update)。一个事务两次读同一行数据,可是这两次读到的数据不一样,就叫不可重读。如果一个事务在提交数据之前,另一个事务可以修改和删除这些数据,就会发生不可重读。
  • (虚读)幻读(Phantom Read):一个事务读到另一个事务已提交的数据(insert)。一个事务执行了两次查询,发现第二次查询结果比第一次查询多出了一行,这可能是因为另一个事务在这两次查询之间插入了新行。针对由事务的不完全隔离所引起的上述问题,提出了一些隔离级别,用来防范这些问题。

事务隔离级别四种状态

  • 读未提交(Read Uncommitted):读取未提交的数据是允许的。说明一个事务在提交前,其变化对于其他事务来说是可见的。这样脏读、不可重读和幻读都是允许的。当一个事务已经写入一行数据但未提交,其他事务都不能再写入此行数据;但是,任何事务都可以读任何数据。这个隔离级别使用排写锁实现。
  • 读已提交(Read Committed):解决脏读,存在两个问题。读取未提交的数据是不允许的,它使用临时的共读锁和排写锁实现。这种隔离级别不允许脏读,但不可重读和幻读是允许的。
  • 可重复读(Repeatable Read):解决脏读,不可重复读,存在1个问题。说明事务保证能够再次读取相同的数据而不会失败。此隔离级别不允许脏读和不可重读,但幻读会出现。
  • 串行化(Serializable):单事务,解决了所有问题。提供最严格的事务隔离。这个隔离级别不允许事务并行执行,只允许串行执行。这样,脏读、不可重读或幻读都可发生。

手动实现事务实例

   新建了两个类Account,Log,并创建表,使用mybatis框架逆向工程生成mapper和bean。
工程代码:可以查看我的下载,https://download.csdn.net/download/weixin_41262453/11223484 不知道怎么修改积分,不能改成免费,如果有需要可以留言。
在这里插入图片描述
   AccountServiceImpl中增加切面功能。
在这里插入图片描述
   首先AccountServiceImpl中没有事务回滚时:int i =1/0会出错,但是不影响插入Account
在这里插入图片描述
  运行结果:
在这里插入图片描述
   我applicationContext.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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    <!-- spring包 bean的扫描  -->   
	<context:component-scan base-package="com.bjpowernode">
	</context:component-scan>
	
	<context:property-placeholder location="classpath:dbconfig.properties" />
	<bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="123***Yqh"></property>
	</bean>
	
	<!-- 声明切面类对象 -->
   	<bean id="myAspect" class="com.bjpowernode.util.MyAspect"></bean>
   	
   	<!--================== 配置和MyBatis的整合=============== -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 指定mybatis全局配置文件的位置 -->
		<property name="configLocation" value="classpath:mybatis-config.xml"></property>
		<property name="dataSource" ref="pooledDataSource"></property>
		<!-- 指定mybatis,mapper文件的位置 -->
		<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
	</bean>

	<!-- 配置扫描器,将mybatis接口的实现加入到ioc容器中 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!--扫描所有dao接口的实现,加入到ioc容器中,如果扫描多个包,采用半角,分割符 -->
		<property name="basePackage" value="com.bjpowernode.dao"></property>

	</bean>
	
		<!-- 配置一个可以执行批量的sqlSession -->
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
		<constructor-arg name="executorType" value="BATCH"></constructor-arg>
	</bean>
   	<!-- 声明自动代理生成器:创建代理对象 -->
   	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

   手动实现一个事务工具:
在这里插入图片描述
   同时applicationContext中的配置增加事务的管理:

<?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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    <!-- spring包 bean的扫描  -->   
	<context:component-scan base-package="com.bjpowernode">
	</context:component-scan>
	
	<context:property-placeholder location="classpath:dbconfig.properties" />
	<bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="123***Yqh"></property>
	</bean>
	
	<!-- 声明切面类对象 -->
   	<bean id="myAspect" class="com.bjpowernode.util.MyAspect"></bean>
   	
   	<!--================== 配置和MyBatis的整合=============== -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 指定mybatis全局配置文件的位置 -->
		<property name="configLocation" value="classpath:mybatis-config.xml"></property>
		<property name="dataSource" ref="pooledDataSource"></property>
		<!-- 指定mybatis,mapper文件的位置 -->
		<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
	</bean>

	<!-- 配置扫描器,将mybatis接口的实现加入到ioc容器中 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!--扫描所有dao接口的实现,加入到ioc容器中,如果扫描多个包,采用半角,分割符 -->
		<property name="basePackage" value="com.bjpowernode.dao"></property>

	</bean>
	
		<!-- 配置一个可以执行批量的sqlSession -->
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
		<constructor-arg name="executorType" value="BATCH"></constructor-arg>
	</bean>
   	<!-- 声明自动代理生成器:创建代理对象 -->
   	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
   	
	<!-- ===============事务控制的配置 ================-->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!--控制住数据源  -->
		<property name="dataSource" ref="pooledDataSource"></property>
	</bean>
	<!--开启基于注解的事务,使用xml配置形式的事务(必要主要的都是使用配置式)  -->
	<aop:config>
		<!-- 切入点表达式 -->
		<aop:pointcut expression="execution(* com.bjpowernode.service..*(..))" id="txPoint"/>
		<!-- 配置事务增强 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>

	</aop:config>
	
	<!--配置事务增强,事务如何切入  -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 所有方法都是事务方法 -->
			<tx:method name="*"/>
			<!--get开始的所有方法  -->
			<tx:method name="get*" read-only="true"/>
		</tx:attributes>
	</tx:advice>
</beans>

   在AccountServiceImpl的insertAccount中新增事务回滚功能:运行后,报错,说明Account、log两者具有原子性,绑定在一起,事务回滚成功。
在这里插入图片描述

使用Spring AOP实现手动实现的事务实例

   上一个例子中事务是在AccountServiceImpl中硬编码,可以利用Spring AOP的环绕通知、异常通知。
在这里插入图片描述
  事务代码从AccountService Impl中解耦。
在这里插入图片描述
运行结果:事务回滚成功。
在这里插入图片描述
注意使用AOP时事务中的业务不能自己try catch:否则事务以为你自己捕获了异常,就不会进行回滚。AOP的环绕通知执行正常,但是异常通知没有执行,因为被你自己捕获了,所以没有抛出异常,进而没有进行事务回滚,插入Account成功了。
在这里插入图片描述

声明式事务实例

  声明式事务使用非常简单,不需要使用自己编写环绕通知异常通知,也不需要自己在Service中硬编码事务的提交、回滚过程,只需要在插入方法中加入@Transactional
  把前面几个例子的Aspect AOP注释掉
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此时:在insertAccount上增加事务声明即可
在这里插入图片描述
同样:若在事务声明的方法中进行了捕获,那么会事务回滚失效
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41262453/article/details/90747856