- 事務簡介:
事務管理是企業級應用開發中必不可少的技術,主要用來確定資料的完整性和一緻性,
事務:就是一系列動作,它們被當作一個獨立的工作單元,這些動作要麼全部完成,要麼全部不起作用.
- Spring中使用事務:
作為一個受歡迎的企業應用架構,Spring在不同的事務管理API上定義了一個抽象層,而開發時不必了解底層的事務管理API,就可以使用Spring的事務管理機制.
Spring既支援程式設計式的事務管理,也支援聲明式的事務管理,大多數情況我們選擇後者.
程式設計式事務管理:将事務管理代碼嵌入到業務代碼中來控制事務的送出和復原,在程式設計式事務管理中,必須在每一個事務操作中包含額外的事務管理代碼.
聲明式事務管理:将事務管理代碼從業務方法中分離出來,以聲明的方式來實作事務管理,事務管理作為一種橫切關注點,可以通過AOP的方式子產品化.
下面介紹一下在Spring中使用注解方式開啟聲明式事務的配置檔案代碼(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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!-- 導入資源檔案 -->
<context:property-placeholder location="db.properties"
ignore-resource-not-found="false" />
<!-- 配置c3p0連接配接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!-- 配置jdbcTemplate -->
<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>
步驟三: 配置完成以後,在需要開始事務的方法添加@Transactional注解,即可在該方法中開啟并使用事務.
- 事務的傳播屬性:
當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播,例如:方法可以繼續在現有事務中運作,也可以開啟一個新的事務,在新的事務中執行.
在@Transactional中,有一個參數叫propagation,用于指定事物的傳播行為,預設值為REQUIRED,即使用調用方法的事務.
@Transactional(propagation=Propagation.REQUIRED):不開啟新的事務
@Transactional(propagation=Propagation.REQUIRED_NEW):開啟新的事務
那麼應該如何了解呢,假設我現在有100塊錢,我需要買兩本書,A和B,A賣60塊錢,庫存還有10本,B賣50塊錢,庫存也有十本,在方法buyBook()中,需要同時調用方法BuyBookA(),和buyBookB();
如果選擇REQUIRED的話,結果是一本也買不了,因為在買完A後,我隻剩40塊錢,在買B的時候,因為錢不夠,是以購買失敗,事務復原到buyBook方法最初的位置(buyBookA方法之前),即A,B都沒有買成.
如果選擇的是REQUIRED_NEW的話,那麼會成功購買A,而無法購買B,因為在buyBook方法中,當調用buyBookA()時,會開啟一個新事務,在購買成功時,事務送出,資料庫更新,這時候我隻剩40塊錢,再調用buyBookB()時自然會因為錢不夠而事務復原.最後的結果就是,A購買成功,B購買失敗.
- 事務的其他屬性:
使用isolation指定事務的隔離級别,最常用的取值為READ_COMMITTED
使用readOnly指定事物是否為隻讀,表示這個事務隻讀取資料而不更新資料,這樣可以幫助資料庫引擎優化事務,
使用timeOut指定事務復原之前可以占用的時間.
@Transactional(isolation=Isolation.READ_COMMITTED,readOnly=true,timeout=5)
以上都是使用注解的方式啟用聲明式事務,下面介紹一下使用xml檔案配置事務的方式:
beans.xml(配置bean的過程省略):
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!-- 導入資源檔案 -->
<context:property-placeholder location="db.properties"
ignore-resource-not-found="false" />
<!-- 配置c3p0連接配接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!-- 配置jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--步驟1: 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 步驟2:配置事務屬性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--根據方法名指定事物的屬性-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 步驟3:配置事物切入點,把事物切入點和事務屬性關聯起來 -->
<aop:config>
<aop:pointcut expression="execution(* com.wang.dao.UserDao.*(..))" id="pointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>