數據庫事務(Transaction) 指做爲單個邏輯工做單元執行的一系列操做。將一組相關操做組合爲一個要麼所有成功要麼所有失敗的單元,使應用程序更加可靠。java
事務必須知足ACID:mysql
可清楚定義事務邊界,實現細粒度的事務控制,好比可經過程序代碼來控制事務什麼時候開始、什麼時候結束等,可實現細粒度事務控制。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 |
傳播行爲:定義關於客戶端和被調用方法的事務邊界
傳播行爲 |
含 義 |
---|---|
REQUIRED |
業務方法需在一個事務中運行。如方法運行時已處在一事務中,則加入到該事務,不然爲本身建立一個新事務 |
NOT_SUPPORTED |
聲明方法不需事務。如方法沒有關聯到一事務,容器不會爲它開啓事務;如方法在一事務中被調用,該事務被掛起,在方法調用結束後,原事務恢復執行 |
REQUIRESNEW |
不論是否存在事務,業務方法總會爲本身發起一新事務。如方法已運行在一個事務中,則原有事務會被掛起,新的事務會被建立,直到方法執行結束,新事務纔算結束,原事務才恢復執行 |
MANDATORY |
指定業務方法只能在一個已存在事務中執行,業務方法不能發起本身事務。如業務方法在沒有事務的環境調用,容器拋出異常。 |
SUPPORTS |
如業務方法在某個事務範圍內被調用,則方法成爲該事務的一部分。如業務方法在事務範圍外被調用,則方法在沒有事務的環境下執行 |
Never |
指定業務方法絕對不能在事務範圍內執行,如業務方法在某個事務中執行,容器拋出異常,只有業務方法沒有關聯到任何事務才能正常執行 |
NESTED |
如一活動事務存在,則運行在一嵌套事務中. 如沒有活動事務, 則按REQUIRED屬性執行。使用一單獨的事務, 該事務擁有多個可回滾保存點。內部事務回滾不會對外部事務形成影響。只對DataSourceTransactionManager事務管理器起效 |
隔離級別 |
含義 |
---|---|
DEFAULT |
使用後端數據庫默認的隔離級別 |
READ_UNCOMMITED |
容許讀取還未提交的已改變數據,可能致使髒、幻、不可重複讀 |
READ_COMMITTED |
容許在併發事務已經提交後讀取。可防止髒讀,但幻讀和 不可重複讀仍可發生 |
REPEATABLE_READ |
對相同字段的屢次讀取一致,除非數據被事務自己改變。可防止髒、不可重複讀,但幻讀仍可能發生。 |
SERIALIZABLE |
徹底服從ACID的隔離級別,確保不發生髒、幻、不可重複讀。在全部隔離級別中最慢的,典型的經過徹底鎖定事務涉及的數據表。 |
注意:隨着隔離級別的提升併發性能下降,具體使用那種隔離級別須要視狀況而定。
補充:
表示該方法是否只讀,默認是false(可寫)。對數據庫執行增、刪、改,事務必定要定義可寫,對數據庫執行查詢的時候,此時能夠定爲只讀(true)
咱們採用的是聲明式事務管理,配置事務時,需在xml配置文件引入聲明事務的tx標籤
事務的配置方式:
參考代碼以下:
<?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&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>
參考代碼以下:
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&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; } }
未完待續。。。。。