Spring之事务Transaction管理

1.什么是事务(Transaction)

    一组业务逻辑(如:ABCDEF)要么全部执行成功,要么全部执行不成功的操作就叫做事务。

  1.1事务的特性:

     原子性(Atomicity):一个事务就如同一个原子,一个不可分割的部分。

     一致性(Consistency):如同执行数据库操作后,数据不会破坏。(假设给A账户转钱,A账户扣了钱,不可能B账户没有增加钱)

     隔离性(Transaction isolation Level):关于事务的并发。(假设我们编写了一条update语句对数据库进行操作,同时别 人也编写了一条delete对相同的数据进行操作,可以想象,如果先执行了delete后,再执行update就会报错)

     持久性(Durability):当我们对数据库执行了insert操作后,数据库就应该保证有一条数据永久性的存放在磁盘中。

  1.2隔离性问题:

       脏读(Dirty Read):一个事务读到了另一个事务没有提交的数据。(例如:在T1时刻事务A开始事务。在T2时刻事务A查询账户余额为1000,同时在T2时刻事务B开始事务。在T3时刻事务A从账户取出100元并把余额改为900。在T4时刻,事务B查询账户余额为900。在T5时刻事务A撤销事务,并把余额恢复为1000。在T6时刻事务B向账户存入100,并把余额改为1000。在T7时刻事务B提交事务。然而此时账户余额应该为1100,而实际上账户余额只有1000,这样就违背了事务的一致性

       不可重复读(Unrepeatable Read):一个事务读取到了另一个事务已经提交了的数据(update)。(例如:在T1时刻事务A开始事务。在T2时刻事务B开始事务。在T3时刻事务B查询账户余额为1000。在T4时刻事务A查询账户余额为1000。在T5时刻事务B从账户中存入100。在T6时刻事务B提交事务。在T7时刻事务A从账户取出100。在T8时刻事务A撤销事务将余额恢复为1000。然而事实上事务B向账户存入了100,账户余额为1100。而当事务A回滚的时候将账户余额改为了1000

       幻读(Phantom Read):一个事务读取到了另一个事务已经提交了的数据(insert)。(例如:在T1时刻事务A开始事务,在T2时刻事务B开始事务。在T3时刻事务A统计总存款为1000。在T4时刻事务B取出100,并把余额改为900。在T5时刻事务B提交事务。在T6时刻事务A向账号中汇入100并把余额改为1100。在T7时刻事务A提交事务。然而实际上事务B已经取出了100,事务A存入100是在事务B取出100前进行读取余额为1000,这样就导致了数据增多

  1.3隔离级别:

    Read Committed(读已提交):解决了脏读问题,而没有解决不可重复读和幻读。

    Read Uncommitted(读未提交):存在3个问题

    Repeatable Read(可重复读):解决脏读和不可重复读。存在一个问题。

    Serializable(串行化):解决了所有问题。

   JDBC也提供了这四种事务的隔离级别。四种隔离级别的执行效率按(Read Uncommitted》Read Committed》Repeatable Read》Serializable)递减。

2.Spring对事务的管理介绍

  2.1需要导入jar包(Spring-tx.jar)

  2.2spring-tx.jar包中的三个顶级接口介绍:

       PlatformTransactionManager(平台事务管理器):要通过Spring管理事务就必须使用事务管理器,因此就必须配置事务管理器。

由于Spring-tx.jar包中就只定义了平台事务管理器的接口,并没实现事务管理,因此需要提供实现类,因此就需要导入以下jar包(spring-jdbc.jar这是Spring整合jdbc开发用的。spring-orm.jar这是Spring整合hibernate开发用的)。常见的事管理器有:DataSourceTransactionManager(jdbc开发时事务管理器)、HibernateTransactionManager(hibernate开发时事务管理器)

     

       TransactionDefinition(事务详情、事务定义、事务属性):Spring用于确定事务的具体详情,例如:隔离级别、是否只读、超时时间等。

       Spring在事务详情中提供了事务的传播行为(两个业务之间如何共享事务):

       

        PROPAGATION_REQUIRED , required , 必须  【默认值】

   支持当前事务,A如果有事务,B将使用该事务。

   如果A没有事务,B将创建一个新的事务。

        PROPAGATION_SUPPORTS supports ,支持

   支持当前事务,A如果有事务,B将使用该事务。

   如果A没有事务,B将以非事务执行。

        PROPAGATION_MANDATORYmandatory ,强制

   支持当前事务,A如果有事务,B将使用该事务。

   如果A没有事务,B将抛异常。

        PROPAGATION_REQUIRES_NEW requires_new ,必须新的

   如果A有事务,将A的事务挂起,B创建一个新的事务

   如果A没有事务,B创建一个新的事务

        PROPAGATION_NOT_SUPPORTED not_supported ,不支持

   如果A有事务,将A的事务挂起,B将以非事务执行

   如果A没有事务,B将以非事务执行

        PROPAGATION_NEVER never,从不

   如果A有事务,B将抛异常

   如果A没有事务,B将以非事务执行

        PROPAGATION_NESTED nested ,嵌套

        AB底层采用保存点机制,形成嵌套事务。

       TransactionStatus(事务状态):Spring用于记录当前事务运行状态。例如:是否有保存点、事务是否完成,Spring底层就会根据状态进行相应的操作。

3.通过TransactionTemplate手动方式管理事务

    以转账为例进行以下程序的编写。

    dao层接口:

public interface IAccountDao {
	/**
	 * 收款方
	 * @param inner
	 * @param money
	 */
	public void in(String inner,int money);
	/**
	 * 汇款方
	 * @param outer
	 * @param money
	 */
	public void out(String outer,int money);
}

     dao层接口实现类:dao层需要访问数据库,因此在dao层实现类中继承了JdbcDaoSupport的支持。因此在配置文件中需要对dao层的实现类注入DataSource。

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{

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

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

}

     service层的接口:

public interface IAccountService {
	/**
	 * 转账
	 * @param outer
	 * @param inner
	 * @param money
	 */
	public void transfer(String outer,String inner,int money);
}
    service层接口的实现类:service层为业务逻辑层,因此在Service的实现类中需要注入dao层。由于service层需要事务管理,在这里我们采用的是TransactionTemplate来手动方式管理事务,因此也需要给service实现类中注入TransactionTemplate。
public class AccountServiceImpl implements IAccountService{

	private IAccountDao accountDao;
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	//通过spring容器注入事务模板
		private TransactionTemplate transactionTemplate;
		public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
			this.transactionTemplate = transactionTemplate;
		}

	@Override
            //通过transactionTemplate的execute(TransactionCallbackWithoutResult arg0)方法来管理事务
         public void transfer(final String outer, final String inner,final int money) {
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus status) {
				accountDao.out(outer, money);
				int a = 1/0;
				accountDao.in(inner, money);
			}});
	}

}

      applicationContext.xml配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       					   http://www.springframework.org/schema/beans/spring-beans.xsd
       					   http://www.springframework.org/schema/aop 
       					   http://www.springframework.org/schema/aop/spring-aop.xsd">
	<!-- 注册数据源并注入数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql:///spring_study_day02"></property>
		<property name="user" value="root"></property>
		<property name="password" value="12345678"></property>
	</bean>
	
	<!-- 注册accountDao -->
	<bean id="accountDao" class="com.lc.dao.impl.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 注册accountService -->
	<bean id="accountService" class="com.lc.service.impl.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
		<!-- 注入事务模板 -->
		<property name="transactionTemplate" ref="transactionTemplate"></property>
	</bean>
	
	<!-- 注册事务模板 -->
	<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
		<property name="transactionManager" ref="transactionManager"></property>
	</bean>
	
	<!-- 注册事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
</beans>

4.通过TransactionProxyFactoryBean来实现半自动管理事务

     dao层接口:

public interface IAccountDao {
	/**
	 * 收款方
	 * @param inner
	 * @param money
	 */
	public void in(String inner,int money);
	/**
	 * 汇款方
	 * @param outer
	 * @param money
	 */
	public void out(String outer,int money);
}

     dao层接口实现类:dao层需要访问数据库,因此在dao层实现类中继承了JdbcDaoSupport的支持。因此在配置文件中需要对dao层的实现类注入DataSource。

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{

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

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

}

   service层的接口:

public interface IAccountService {
	/**
	 * 转账
	 * @param outer
	 * @param inner
	 * @param money
	 */
	public void transfer(String outer,String inner,int money);
}

   service接口的实现类:由于service是业务逻辑层,因此这里需要注入dao层。因为采用TransactionProxyFactoryBean半自动方式来管理事务,因此这里不需要注入其他。只需要在applicationContext.xml配置文件中使用TransactionProxyFactoryBean来生成service层的代理类来增强事务的管理。注意在使用service层的时候是使用Service的代理类也就是proxyAccountService

public class AccountServiceImpl implements IAccountService{

	private IAccountDao accountDao;
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}
	


	@Override
	public void transfer(final String outer, final String inner,final int money) {
		accountDao.out(outer, money);
//		int a = 1/0;
		accountDao.in(inner, money);
	}
	
	

}
   applicationContext.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       					   http://www.springframework.org/schema/beans/spring-beans.xsd
       					   http://www.springframework.org/schema/aop 
       					   http://www.springframework.org/schema/aop/spring-aop.xsd">
	<!-- 注册数据源并注入数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql:///spring_study_day02"></property>
		<property name="user" value="root"></property>
		<property name="password" value="12345678"></property>
	</bean>
	
	<!-- 注册accountDao -->
	<bean id="accountDao" class="com.lc.dao.impl.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 注册accountService -->
	<bean id="accountService" class="com.lc.service.impl.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean>
	<!--  
		4.1 proxyInterfaces 接口 
		4.2 target 目标类
		4.3 transactionManager 事务管理器
		4.4 transactionAttributes 事务属性(事务详情)
				prop.key :确定哪些方法使用当前事务配置
				prop.text:用于配置事务详情
					格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
						传播行为		隔离级别	是否只读		异常回滚		异常提交
					例如:
						<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> 默认传播行为,和隔离级别
						<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop> 只读
						<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,+java.lang.ArithmeticException</prop>  有异常扔提交
	-->
	<bean id="proxyAccountService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<property name="target" ref="accountService"></property>
		<property name="proxyInterfaces" value="com.lc.service.IAccountService"></property>
		<property name="transactionManager" ref="transactionManager"></property>
		<property name="transactionAttributes">
			<props>
				<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
			</props>
		</property>
	</bean>
	
	<!-- 注册事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
</beans>

5.通AOP来实全自动管理事务

     dao层:

public interface IAccountDao {
	/**
	 * 收款方
	 * @param inner
	 * @param money
	 */
	public void in(String inner,int money);
	/**
	 * 汇款方
	 * @param outer
	 * @param money
	 */
	public void out(String outer,int money);
}

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{

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

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

}

  service层:

public interface IAccountService {
	/**
	 * 转账
	 * @param outer
	 * @param inner
	 * @param money
	 */
	public void transfer(String outer,String inner,int money);
}
public class AccountServiceImpl implements IAccountService{

	private IAccountDao accountDao;
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}
	


	@Override
	public void transfer(final String outer, final String inner,final int money) {
		accountDao.out(outer, money);
//		int a = 1/0;
		accountDao.in(inner, money);
	}
	
	

}

   applicationContext.xml:这里需要在applicationContext的头文件中加入事务的命名空间:

xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation=http://www.springframework.org/schema/tx 
       					   http://www.springframework.org/schema/tx/spring-tx.xsd
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       					   http://www.springframework.org/schema/beans/spring-beans.xsd
       					   http://www.springframework.org/schema/aop 
       					   http://www.springframework.org/schema/aop/spring-aop.xsd
       					   http://www.springframework.org/schema/tx 
       					   http://www.springframework.org/schema/tx/spring-tx.xsd">
	<!-- 注册数据源并注入数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql:///spring_study_day02"></property>
		<property name="user" value="root"></property>
		<property name="password" value="12345678"></property>
	</bean>
	
	<!-- 注册accountDao -->
	<bean id="accountDao" class="com.lc.dao.impl.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 注册accountService -->
	<bean id="accountService" class="com.lc.service.impl.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean>
	
	<!-- 4 事务管理 -->
	<!-- 4.1 事务管理器 -->
	<bean id="txManage" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 4.2 事务详情(事务通知)  , 在aop筛选基础上,对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等
		<tx:attributes> 用于配置事务详情(事务属性)
			<tx:method name=""/> 详情具体配置
				propagation 传播行为 , REQUIRED:必须;REQUIRES_NEW:必须是新的
				isolation 隔离级别
	-->
	<tx:advice id="txAdvice" transaction-manager="txManage">
		<tx:attributes>
			<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
			<tx:method name="find*"  read-only="true"/>
		</tx:attributes>
	</tx:advice>
	<!-- 4.3 AOP编程,目标类有ABCD(4个连接点),切入点表达式 确定增强的连接器,从而获得切入点:ABC --> 
	<aop:config >
		<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lc.service.impl.AccountServiceImpl.*(..))"/>
	</aop:config>
	
	
	
</beans>

6.通过注解的方式实现管理事务

   dao层:

public interface IAccountDao {
	/**
	 * 收款方
	 * @param inner
	 * @param money
	 */
	public void in(String inner,int money);
	/**
	 * 汇款方
	 * @param outer
	 * @param money
	 */
	public void out(String outer,int money);
}

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{

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

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

}

  service层:

public interface IAccountService {
	/**
	 * 转账
	 * @param outer
	 * @param inner
	 * @param money
	 */
	public void transfer(String outer,String inner,int money);
}

  service层的实现类:在Service实现类中需要加上@Transactional(propagation="事务的传播行为",isolation="事务的隔离界别")注解代表该类需要管理事务。

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

	private IAccountDao accountDao;
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}
	


	@Override
	public void transfer(final String outer, final String inner,final int money) {
		accountDao.out(outer, money);
		int a = 1/0;
		accountDao.in(inner, money);
	}
	
	

}


  applicationContext.xml需要配置让事务注解生效,只要需要事务,都需要配置事务管理器。

让@Transactional注解生效代码

<tx:annotation-driven transaction-manager="transactionManager" />
<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/beans 
       					   http://www.springframework.org/schema/beans/spring-beans.xsd
       					   http://www.springframework.org/schema/context 
       					   http://www.springframework.org/schema/context/spring-context.xsd
       					   http://www.springframework.org/schema/aop 
       					   http://www.springframework.org/schema/aop/spring-aop.xsd
       					   http://www.springframework.org/schema/tx 
       					   http://www.springframework.org/schema/tx/spring-tx.xsd">
	<!-- 注册数据源并注入数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql:///spring_study_day02"></property>
		<property name="user" value="root"></property>
		<property name="password" value="12345678"></property>
	</bean>
	
	<!-- 注册accountDao -->
	<bean id="accountDao" class="com.lc.dao.impl.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 注册accountService -->
	<bean id="accountService" class="com.lc.service.impl.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean>
	
	<!-- 4 事务管理 -->
	<!-- 4.1 事务管理器 -->
	<bean id="transactionManager" 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="transactionManager" />
	
	
</beans>





猜你喜欢

转载自blog.csdn.net/ititii/article/details/79439059