Spring基础学习(十一)——Spring的事务管理

    首先,什么是事务?

    事务就是我们在完成一件事情的过程中所执行的一系列动作,只有所有的动作都成功了,事务才算成功,中间的任何一个动作发生

了错误,那么事务就会回滚到什么都没做的状态。比如我们在银行转账的过程,我向某某转账,我的账户要扣钱,然后某某的账户要

加钱,只有扣钱和加钱都成功了,事务才算成功,如果我的钱没有扣成功,或者我的钱扣了,但是某某的钱没增加,那么事务都算失

败,并且会回滚到我没扣钱,某某没加钱的状态。

         

    理解了事务,那么事务管理就容易理解多了。事物管理就是对事务的一系列操作,防止事务在执行过程中出现错误而没有回滚,或

者说出现错误仍然对数据库的数据进行了修改。

事务具有四个特性(ACID):

    原子性(Atomicity):可以理解为完整性,即事务时一个原子操作,由一系列动作组成,确保动作要么全部完成,要么完全不起

用。

    一致性(Consistency):是完成后的数据对比,一旦事物完成(不论成败),系统必须确保他所建模的业务处于一致状态,而不

会是部分完成部分失败,具体的数据不会被破坏。

    隔离性(Isolation):是针对并发的情况,即当许多事物同时处理相同的数据时,每个事务都应该与其他事务相隔离开来,防止数

据混乱

    持久性(Durability):即结果是持久的,不会因为缓存或者程序等的关闭而改变。一旦事务完成,无论发生什么系统错误,它的

果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事物的结果被写到持久化存储器中。


常见的事务问题:

脏读:一个事务读到另一个事务没有提交的数据

不可重复读:一个事务读到另一个事务已经提交的更新数据(update)

幻读:一个事务读到另一个事务已经提交的插入数据(insert)


常见的事务问题都是发生在并发情况下的,即是有多个事务同时处理相同的数据,这于事务的隔离性有关。

事务的隔离性分为五种级别,详情如下:

隔离级别 含义
ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的

Spring的事务管理:

    首先说明,虽然说是Spring的事务管理,但是Spring本身不直接管理事务,spring提供了许多事务管理器接口,具体的事物管理

职责是由具体的持久层框架或者JDBC等相关平台的事务实现的。

    Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个

台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。此接口的内容如下:

Public interface PlatformTransactionManager()...{  
    // 由TransactionDefinition得到TransactionStatus对象
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 
    // 提交
    Void commit(TransactionStatus status) throws TransactionException;  
    // 回滚
    Void rollback(TransactionStatus status) throws TransactionException;  
    } 

常见的事务管理器接口:

DataSourceTransactionManager ,jdbc开发时事务管理器,采用JdbcTemplate

HibernateTransactionManager,hibernate开发时事务管理器,整合hibernate



除此之外,还有两个接口也十分重要:

TransactionDefinition:事务详情(事务定义、事务属性),spring用于确定事务具体详情,例如:隔离级别、是否只读、超时时间 等,进行事务配置时,必须配置事务详情。spring将配置项封装到该对象实例。接口详情如下:


TransactionStatus:事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。 spring底层根据状态进行相应操作。接口详情如下:





接下来介绍两种常见的spring事务配置方式(以JDBC事务管理器接口,事务为转账为例):

创建表:

create table account(
  id int primary key auto_increment,
  username varchar(50),
  money int
);
insert into account(username,money) values('jack','10000');
insert into account(username,money) values('rose','10000');

基于XML的AOP事务配置:

Dao层:

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

	@Override
	public void out(String outer, Integer money) {
		this.getJdbcTemplate().update("update account set money = money - ? where username = ?", money,outer);
	}

	@Override
	public void in(String inner, Integer money) {
		this.getJdbcTemplate().update("update account set money = money + ? where username = ?", money,inner);
	}

}

Service层:

public class AccountServiceImpl implements AccountService {

	private AccountDao accountDao;
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	@Override
	public void transfer(String outer, String inner, Integer money) {
		accountDao.out(outer, money);
		//断电
//		int i = 1/0;
		accountDao.in(inner, money);
	}

}

  

测试类: 

    @Test
	public void demo01(){
		String xmlPath = "applicationContext.xml";
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
		AccountService accountService =  (AccountService) applicationContext.getBean("accountService");
		accountService.transfer("jack", "rose", 1000);
	}

  

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"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
	  <!--  dao  -->
	<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!--  service -->
	<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean>
<!-- 连接properties -->
	  <bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:confing/dbconfig.properties</value>
			</list>
		</property>
	</bean>    
	
	<!-- 阿里 druid数据库连接池 -->
 	 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">  
        <!--  数据库基本信息配置 -->
		<property name="url" value="${url}" />
		<property name="username" value="${username}" />
		<property name="password" value="${password}" />
		<property name="driverClassName" value="${driverClassName}" />
		<property name="filters" value="${filters}" />  
   		<!--  最大并发连接数 -->
		<property name="maxActive" value="${maxActive}" />
         <!-- 初始化连接数量 -->
		<property name="initialSize" value="${initialSize}" />
       <!--   配置获取连接等待超时的时间 -->
		<property name="maxWait" value="${maxWait}" />
   <!--       最小空闲连接数 -->
		<property name="minIdle" value="${minIdle}" />  
   		<!--  配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
		<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
         <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
		<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
		<property name="validationQuery" value="${validationQuery}" />
		<property name="testWhileIdle" value="${testWhileIdle}" />
		<property name="testOnBorrow" value="${testOnBorrow}" />
		<property name="testOnReturn" value="${testOnReturn}" />
		<property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}" />
         <!-- 打开removeAbandoned功能 -->
		<property name="removeAbandoned" value="${removeAbandoned}" />
        <!--  1800秒,也就是30分钟 -->
		<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
        <!--  关闭abanded连接时输出错误日志 -->
		<property name="logAbandoned" value="${logAbandoned}" />
	</bean>
 <!--  事务管理器 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!--  事务详情(事务通知)  , 在aop筛选基础上,对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等
		<tx:attributes> 用于配置事务详情(属性属性)
		<tx:method name=""/> 详情具体配置
		propagation 传播行为 , REQUIRED:必须的;REQUIRES_NEW:必须是新的
		isolation 隔离级别
	-->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
		</tx:attributes>
	</tx:advice>
	<!--  AOP编程,目标类有ABCD(4个连接点),切入点表达式 确定增强的连接器,从而获得切入点:ABC -->
	<aop:config>
		<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"/>
	</aop:config>
</beans>

基于注解的事务配置:

Dao层与上面的相同,Service层添加注释:

@Transactional
public class AccountServiceImpl implements AccountService {

如果需要对事务进行具体配置,则Service层可如下添加注释

@Transactional(propagation=Propagation.REQUIRED , isolation = Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService {

也可以只对具体方法添加事务,与对Service层添加注释相同,在方法前添加@Transactional即可

事务详情配置如下:

测试类与上面相同

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"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
	
  
	<!-- 连接properties -->
	  <bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:confing/dbconfig.properties</value>
			</list>
		</property>
	</bean>    
	
	<!-- 阿里 druid数据库连接池 -->
 	 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">  
        <!--  数据库基本信息配置 -->
		<property name="url" value="${url}" />
		<property name="username" value="${username}" />
		<property name="password" value="${password}" />
		<property name="driverClassName" value="${driverClassName}" />
		<property name="filters" value="${filters}" />  
   		<!--  最大并发连接数 -->
		<property name="maxActive" value="${maxActive}" />
         <!-- 初始化连接数量 -->
		<property name="initialSize" value="${initialSize}" />
       <!--   配置获取连接等待超时的时间 -->
		<property name="maxWait" value="${maxWait}" />
   <!--       最小空闲连接数 -->
		<property name="minIdle" value="${minIdle}" />  
   		<!--  配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
		<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
         <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
		<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
		<property name="validationQuery" value="${validationQuery}" />
		<property name="testWhileIdle" value="${testWhileIdle}" />
		<property name="testOnBorrow" value="${testOnBorrow}" />
		<property name="testOnReturn" value="${testOnReturn}" />
		<property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}" />
         <!-- 打开removeAbandoned功能 -->
		<property name="removeAbandoned" value="${removeAbandoned}" />
        <!--  1800秒,也就是30分钟 -->
		<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
        <!--  关闭abanded连接时输出错误日志 -->
		<property name="logAbandoned" value="${logAbandoned}" />
	</bean>
	<!-- 4.1 事务管理器 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 4.2 将管理器交予spring 
		* transaction-manager 配置事务管理器
		* proxy-target-class
			true : 底层强制使用cglib 代理
	-->
	<tx:annotation-driven transaction-manager="txManager"/>



</beans>

猜你喜欢

转载自blog.csdn.net/m0_37673753/article/details/79298104
今日推荐