spring事务管理(Transaction)

事务概述

        数据库事务(Transaction) 指做为单个逻辑工做单元执行的一系列操做。将一组相关操做组合为一个要么所有成功要么所有失败的单元,使应用程序更加可靠。java

事务必须知足ACID:mysql

  • n原子性(Atomicity),对于其数据修改,要么全都执行,要么全都不执行
  • n一致性(Consistency) 事务在完成时,必须使全部数据保持一致。
  • n隔离性(Isolation) 并发事务所做修改须与任何其它并发事务所做的修改隔离
  • n持久性(Durability) 在事务完成后,该事务对数据库所做更改持久保存在数据库中,并不会回滚。即便出现任何事故好比断电等,事务一旦提交也持久化保存在数据库。

Spring事务管理方式

编程式事务管理

        可清楚定义事务边界,实现细粒度的事务控制,好比可经过程序代码来控制事务什么时候开始、什么时候结束等,可实现细粒度事务控制。spring

声明式事务管理

         如不须要细粒度的事务控制,可以使用声明式事务,只需在Spring配置文件作一些配置便可将操做归入到事务管理中,解除与代码的耦合, 对应用代码影响最小。当不需事务管理时,可直接从Spring配置文件中移除该设置。sql

事务管理器

        spring不直接管理事务,而将管理事务责任委托给JTA或相应持久性机制提供的特定平台的事务实现数据库

事务管理器实现 场合

org.springframework.jdbc.datasource.DataSourceTransactionManagerexpress

在单一JDBC Datasource中管理事务apache

org.springframework.orm.hibernate3.HibernateTransactionManager编程

持久化机制为hibernate后端

org.springframework.jdo.JdoTransactionManager并发

持久化机制为Jdo

org.springframework.transaction.jta.JtaTransactionManager

使用一个JTA实现来管理事务。一个事务跨越多个资源时必须使用

org.springframework.orm.ojb.PersistenceBrokerTransactionManager

持久化机制为apache的ojb

事务属性(propagation、isolation、read-only)

事务传播规则(propagation)

        传播行为:定义关于客户端和被调用方法的事务边界

传播行为

含    义

REQUIRED

业务方法需在一个事务中运行。如方法运行时已处在一事务中,则加入到该事务,不然为本身建立一个新事务

NOT_SUPPORTED

声明方法不需事务。如方法没有关联到一事务,容器不会为它开启事务;如方法在一事务中被调用,该事务被挂起,在方法调用结束后,原事务恢复执行

REQUIRESNEW

不论是否存在事务,业务方法总会为本身发起一新事务。如方法已运行在一个事务中,则原有事务会被挂起,新的事务会被建立,直到方法执行结束,新事务才算结束,原事务才恢复执行

MANDATORY

指定业务方法只能在一个已存在事务中执行,业务方法不能发起本身事务。如业务方法在没有事务的环境调用,容器抛出异常。

SUPPORTS

如业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如业务方法在事务范围外被调用,则方法在没有事务的环境下执行

Never

指定业务方法绝对不能在事务范围内执行,如业务方法在某个事务中执行,容器抛出异常,只有业务方法没有关联到任何事务才能正常执行

NESTED

如一活动事务存在,则运行在一嵌套事务中. 如没有活动事务, 则按REQUIRED属性执行。使用一单独的事务, 该事务拥有多个可回滚保存点。内部事务回滚不会对外部事务形成影响。只对DataSourceTransactionManager事务管理器起效

隔离级别

隔离级别

含义

DEFAULT

使用后端数据库默认的隔离级别

READ_UNCOMMITED

容许读取还未提交的已改变数据,可能致使脏、幻、不可重复读

READ_COMMITTED

容许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生

REPEATABLE_READ

对相同字段的屡次读取一致,除非数据被事务自己改变。可防止脏、不可重复读,但幻读仍可能发生。

SERIALIZABLE

彻底服从ACID的隔离级别,确保不发生脏、幻、不可重复读。在全部隔离级别中最慢的,典型的经过彻底锁定事务涉及的数据表。

注意:随着隔离级别的提升并发性能下降,具体使用那种隔离级别须要视状况而定。

补充:

  • 脏读:一个事务读取了另外一个事务改写但还未提交的数据,如这些数据被回滚,则所读数据无效。
  • 不可重复读:在同一事务中,屡次读取同一数据返回结果不一样,后续读取可读到另外一事务已提交更新数据。“可重复读”在同一事务中屡次读取数据时,可以保证所读数据同样。
  • 幻读:一个事务读取了几行记录后,另外一个事务插入一些记录,再查询时第一个事务会发现原来没有的记录

read-only

        表示该方法是否只读,默认是false(可写)。对数据库执行增、删、改,事务必定要定义可写,对数据库执行查询的时候,此时能够定为只读(true)

声明式事务的配置

        咱们采用的是声明式事务管理,配置事务时,需在xml配置文件引入声明事务的tx标签

    事务的配置方式:

  • 基于XML配置方式
  • 注解方式

Spring+JDBC组合开发(配置事务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/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

	<!-- 链接池基本配置信息 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="url" value="jdbc:mysql://localhost:3306/springdb?useUnicode=true&amp;characterEncoding=utf8"></property>
		<property name="username" value="root"></property>
		<property name="password" value="liuxin950326"></property>
		
		<!-- 链接池启动时的初始值 -->
		<property name="initialSize" value="10"></property>
		<!-- 链接池的最大值 -->
		<property name="maxActive" value="80"></property>
		<!-- 最大空闲值.通过一个高峰时间,链接池可将已用不到链接慢慢释放一部分,一直减小到maxIdle为止 -->
		<property name="maxIdle" value="5"></property>
		<property name="minIdle" value="2"></property>
	</bean>
	
	
	<!-- 配置JDBC模板 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 声明式事务管理_xml配置_用通知管理事务 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!-- 定义事务关联属性 -->
		<tx:attributes>
			<!-- 定义事务关联的方法
				transfer* 表示transfer开头的方法
          		isolation(隔离性)
          		propagation(传播性)
          		read-only(只读性)
			 -->
			<tx:method name="transfer*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
			<tx:method name="*" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	
	
	<aop:config>
		<!-- 定义切入点 -->
		<aop:pointcut expression="execution(* www.enfp.lx_04_jdbc.lx_01_transaction_xml..*.*(..))" id="pointcutTransfer"/>
		<!-- 切入点与(通知形式)的事务相关联 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcutTransfer"/>
	</aop:config>
	
	<bean id="bankAccountDao" class="www.enfp.lx_04_jdbc.lx_01_transaction_xml.dao.BankAccountDaoImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
	
	<bean id="bankAccountService" class="www.enfp.lx_04_jdbc.lx_01_transaction_xml.service.BankAccountServiceImpl">
		<property name="bankAccountDao" ref="bankAccountDao"></property>
	</bean>
	
</beans>

 

Spring+JDBC组合开发(注解方式配置事务)

参考代码以下:

beans.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/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

	<!-- 链接池基本配置信息 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="url" value="jdbc:mysql://localhost:3306/springdb?useUnicode=true&amp;characterEncoding=utf8"></property>
		<property name="username" value="root"></property>
		<property name="password" value="liuxin950326"></property>
		
		<!-- 链接池启动时的初始值 -->
		<property name="initialSize" value="10"></property>
		<!-- 链接池的最大值 -->
		<property name="maxActive" value="80"></property>
		<!-- 最大空闲值.通过一个高峰时间,链接池可将已用不到链接慢慢释放一部分,一直减小到maxIdle为止 -->
		<property name="maxIdle" value="5"></property>
		<property name="minIdle" value="2"></property>
	</bean>
	
	<!-- 注释组件自动扫描 -->
	<context:component-scan base-package="www.enfp.lx_04_jdbc.lx_01_transaction_annotation"></context:component-scan>
	
	<!-- 配置JDBC模板 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 启动以注解的方式配置事务管理器 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

BankAccountServiceImpl.java

package www.enfp.lx_04_jdbc.lx_01_transaction_annotation.service;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import www.enfp.lx_04_jdbc.lx_01_transaction_xml.dao.IBankAccountDao;
import www.enfp.lx_04_jdbc.lx_01_transaction_xml.domain.BankAccount;

@Service("bankAccountService")
@Transactional(readOnly = true)
public class BankAccountServiceImpl implements IBankAccountService
{
	@Resource(name = "bankAccountDao")
	private IBankAccountDao bankAccountDao = null;

	public IBankAccountDao getBankAccountDao()
	{
		return bankAccountDao;
	}

	public void setBankAccountDao(IBankAccountDao bankAccountDao)
	{
		this.bankAccountDao = bankAccountDao;
	}

	@Override
	@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false)
	public boolean transferBankAccount(String firstBankAccountName,
			String secondBankAccountName, double money)
	{
		try
		{
			if (money < 0)
			{
				throw new RuntimeException("转帐金额必须为正数");
			}

			BankAccount accountA = this.bankAccountDao
					.queryBankAccountByAccountName(firstBankAccountName);
			if (accountA.getBalance() < money)
			{
				throw new RuntimeException("余额不足");
			}
			BankAccount accountB = this.bankAccountDao
					.queryBankAccountByAccountName(secondBankAccountName);

			accountA.setBalance(accountA.getBalance() - money);
			accountB.setBalance(accountB.getBalance() + money);

			this.bankAccountDao.updateBankAccount(accountA);
			this.bankAccountDao.updateBankAccount(accountB);
			return true;
		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return false;
	}
}

未完待续。。。。。

相关文章
相关标签/搜索