1. 事務管理
1.1 概述
事務的屬性,ACID
java
|
1.2 JDBC事務管理
JDBC基於鏈接進行事務管理,默認開啓自動提交。缺點在於不能跨數據庫;且在使用鏈接池時,難以保證線程安全,可能出現死鎖等問題;若是不使用鏈接池,又容易出現效率問題。
數據庫
try { conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/demoDb", username, userpassword); if(conn.getAutoCommit()) conn.setAutoCommit(false); // 禁止自動提交,設置回滾點 stmt = conn.createStatement(); stmt.executeUpdate("alter table …"); //數據庫更新操做1 stmt.executeUpdate("insert into table …"); //數據庫更新操做2 conn.commit(); //事務提交 }catch(Exception ex) { ex.printStackTrace(); try { conn.rollback(); //操做不成功則回滾 } catch(Exception ex) { ex.printStackTrace(); } }
1.3 JTA事務管理
[參考]
express
- Java Transaction API,Java事務API,支持分佈式事務服務。即在兩個或多個網絡計算機資源上訪問而且更新數據,這些數據能夠分佈在多個數據庫上。JDBC驅動程序的JTA支持極大地加強了數據訪問能力。 apache
- 若是使用 JTA 界定事務,就須要有一個實現 javax.sql.XADataSource 、javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驅動程序。一個實現了這些接口的驅動程序將能夠參與 JTA 事務。一個 XADataSource 對象就是一個 XAConnection 對象的工廠。XAConnections 是參與 JTA 事務的 JDBC 鏈接。須要用應用服務器的管理工具設置 XADataSource 。從應用服務器和 JDBC 驅動程序的文檔中能夠了解到相關的指導。J2EE 應用程序用 JNDI 查詢數據源。一旦應用程序找到了數據源對象,它就調用 javax.sql.DataSource.getConnection() 以得到到數據庫的鏈接。 編程
- XA 鏈接與非 XA 鏈接不一樣。XA 鏈接參與了 JTA 事務。這意味着 XA 鏈接不支持 JDBC 的自動提交功能。應用程序應該使用 UserTransaction.begin()、 UserTransaction.commit() 和 serTransaction.rollback() ,而不能對 XA 鏈接調用 java.sql.Connection.commit() 或者 java.sql.Connection.rollback() 。安全
- JTA事務管理缺點在於實現複雜,侵入性大
1.4 Java EE容器事務
- 容器事務主要是Java EE應用服務器提供的,大多基於JTA,須要JNDI的支持,實現複雜。
- 相對編碼實現JTA 事務管理,能夠經過EJB容器提供的容器事務管理機制(CMT)完成同一個功能,這項功能由Java EE應用服務器提供,能夠簡單的指定將哪一個方法加入事務,一旦指定,容器將負責事務管理任務。經過這種方式能夠將事務代碼排除在邏輯編碼以外,同時將全部困難交給 Java EE容器去解決。
- 使用EJB CMT的另一個好處就是程序員無需關心JTA API的編碼,不過,理論上必須使用EJB。
1.5 Spring事務管理
1.5.1 優勢
- 爲不一樣的事務API提供統一的編程模型,事務API包括JTA、JDBC、Hibernate、JPA、JDO
- 支持聲明式事務管理
- 提供比JTA更簡單的編程式事務管理API
- 自然集成Spring數據訪問抽象
1.5.2 Spring事務管理基礎
- Spring並不直接管理事務,而是把事務管理的職責委託給JTA或其餘持久化機制提供的平臺相關的事務實現。
- PlatformTransactionManager是Spring事務的基礎,可經過其實現管理事務,其實現例如:DataSourceTransactionManager、HibernateTransactionManager、JpaTransactionManager、JtaTransactionManager、JmsTransactionManager
public interface PlatformTransactionManager { TransactionStatus /* 線程狀態設置和讀取 */ getTransaction(TransactionDefinition definition /* 定義txn的隔離、傳播、超時、只讀參數 */) throws TransactionException; // 非檢查型的Exception void commit(TransactionStatus status) throws TransactionException; // 提交 void rollback(TransactionStatus status) throws TransactionException; // 回滾 } public interface TransactionStatus extends SavepointManager { boolean isNewTransaction(); boolean hasSavepoint(); void setRollbackOnly(); boolean isRollbackOnly(); void flush(); boolean isCompleted(); }
<!-- 配置各類TxnManager --> <!-- JDBC數據源 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- Hibernate數據源 --> <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- JTA數據源 --> <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
// 使用TxnManager DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setName("SomeTxName"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); try { // 業務邏輯代碼 ... } catch (MyException ex) { txManager.rollback(status); throw ex; } txManager.commit(status);
1.5.3 使用事務同步資源
1.5.3.1 低層次方法
- 使用DataSourceUtils (JDBC), EntityManagerFactoryUtils (JPA), SessionFactoryUtils (Hibernate), PersistenceManagerFactoryUtils (JDO)
// 當前線程中只會取得一個鏈接 // 而dataSource.getConnection()不一樣,每次可能取得不一樣的鏈接,視數據源例如鏈接池而定 Connection conn = DataSourceUtils.getConnection(dataSource); DataSourceUtils.releaseConnection(conn, dataSource);
- 使用TransactionAwareDataSourceProxy,它是DataSource的代理,相似於JNDI dataSource。是很是底層的實現,不建議使用
1.5.3.2 高層次方法
- 使用基於Spring數據訪問模板例如JdbcTemplate的持久化集成API
- 使用事務感知工廠Bean或代理來調用ORM自身的API
1.5.4 編程式事務管理
- 使用TransactionTemplate,更爲推薦
public class UserService implements Service { private TransactionTemplate txnTempl; public UserService(PlatformTransactionManager txnMgr) { this.txnTempl = new TransactionTemplate(txnMgr); } // 有返回值的操做 public Object someAction1() { return txnTempl.execute(new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { updateOperation1(); return resultOfUpdateOperation2(); } }; } // 無返回值的操做 public Object someAction2() { return txnTempl.execute(new TransactionCallbackWithoutResult() { public Object doInTransaction(TransactionStatus status) { updateOperation1(); updateOperation2(); } }; } }
- 使用PlatformTransactionManager,例子見1.5.2
1.5.5 聲明式事務管理
- Spring聲明式事務管理相似於EJB CMT,但既能夠支持JTA事務,也能夠支持JDBC、JPA、Hibernate等;能夠應用於非EJB的類;提供聲明式的回滾設置;還能夠爲指定異常類型配置回滾機制;不支持跨遠程調用(用戶線程)的事務管理
- Spring 2.0以後無需使用TransactionProxyFactoryBean
- 經過AOP Proxy實現
1.5.5.1 設置方法
<!-- XML設置方法 --> <!-- 業務方法 --> <bean id="stockService" class="x.y.z.service.DefaultStockService"/> <!-- 數據源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 事務通知,這是xml設置方式,還支持tx註解,啓用方法<tx:annotation-driven/> --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <!-- read-only,事務只讀;rollback-for,默認爲非檢查型例如RuntimeException回滾,檢查型不回滾 --> <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/> <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/> <!-- propagation,傳播性,默認爲Required;timeout默認爲-1即永不超時 --> <tx:method name="*" propagation="REQUIRED" timeout="30" /> </tx:attributes> </tx:advice> <!-- 事務切面 --> <aop:config> <aop:pointcut id="stockServiceOperation" expression="execution(* x.y.service.StockService.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="stockServiceOperation"/> </aop:config>
// 啓用註解 @EnableTransactionManagement @Configuration public AppConfig { // ... } // 註解設置方法 // 在public方法上設置,AspectJ支持非public方法 @Transactional(propagation = Propagation.REQUIRED, timeout=30, isolation=Isolation.DEFAULT) public void updateStock() { // ... } // 推薦爲具體類而非接口設置註解 @Transactional(readOnly = true) public class QueryStockService implements Service { }
1.5.5.2 多事務管理器支持
各方法運行在各自的事務管理器中
public class TransactionalService { @Transactional("order") public void setSomething(String name) { ... } @Transactional("account") public void doSomething() { ... } }
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> <qualifier value="order"/> </bean> <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> <qualifier value="account"/> </bean>
1.5.5.3 自定義註解快捷方式
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Transactional("order") public @interface OrderTx { } @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Transactional("account") public @interface AccountTx { } @OrderTx public void setSomething(String name) { ... } @AccountTx public void doSomething() { ... }
1.5.6 事務傳播性Propagation
類型 | 說明 |
PROPAGATION_REQUIRED | 若是存在一個事務,則在當前事務中運行。無則開啓;Spring默認 |
PROPAGATION_SUPPORTS | 若是存在一個事務,則在當前事務中運行。無則無事務運行 |
PROPAGATION_MANDATORY | 若是存在一個事務,則在當前事務中運行。無則拋出異常 |
PROPAGATION_REQUIRES_NEW | 若是存在一個事務,則掛起,並開啓一個新事務運行 |
PROPAGATION_NOT_SUPPORTED | 若是存在一個事務,則掛起,並沒有事務運行 |
PROPAGATION_NEVER | 若是存在一個事務,則拋出異常 |
PROPAGATION_NESTED | 若是存在一個事務,則開啓一個嵌套的事務運行,嵌套事務不影響外部的事務;無則開啓事務運行 |
1.5.7 隔離級別Isolation Level
- Spring框架提供程序級別的隔離特性,有別於數據庫自身提供的隔離特性
Dirty Reads 髒讀,讀到了其餘事務未提交的更新數據 |
Non-Repeatable Reads 不可重複讀,在一次事務中,先後讀取的數據不一致,一般是後面一次讀取到了其餘事務的更新數據 |
Phantom Reads 幻讀,在一次事務中,先後讀取的數據不一致,一般是後面一次讀取到了其餘事務的新增數據 |
|
Serializable | N | N | N |
Repeatable Read | N | N | Y |
Read Committed (Spring默認) | N | Y | Y |
Read Uncommitted | Y | Y | Y |
- 大多數數據庫的默認隔離級別爲Read Commited,如Sql Server、Oracle
- 少數數據庫默認的隔離級別爲Repeatable Read, 如MySQL InnoDB存儲引擎