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;
	}
}

未完待續。。。。。

相關文章
相關標籤/搜索