Spring的資料訪問---------------事務管理
ThreadLocal
ThreadLocal為每一個使用該變數的執行緒分配一個變數副本,所以每一個執行緒在改變自己的副本時,不會改變其他執行緒的副本資訊。該類主要包含四個方法:
public void set(Object obj)
public Object get()
public void remove()
protected Object InitialValue()
package thread; public class ThreadLocalTest { private static final ThreadLocal<Integer> local = ThreadLocal.withInitial(()->0); public static void main(String[] args) throws InterruptedException { for(int i = 0; i < 10;i++){ new MyThread(i).start(); } } static class MyThread extends Thread{ private int end; public MyThread(int end) { this.end = end; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " start, local = " + local.get()); for(int i = 0; i <= end;i++){ local.set(local.get() + i); //計算(end+1)*end/2的值 } System.out.println(Thread.currentThread().getName() + " end, local = " + local.get()); } } }
JDBC對事務的支援
JDBC在進行資料庫連線時,可以通過DataSource連線資料庫,並將PreparedStatement預處理關閉,並將資料庫連線關閉。例項如下:
package com.kingdee.opensys.common.base.imp; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.springframework.jdbc.support.JdbcUtils; public class CommonDao { private IPage page; public IPage getPage() { return page; } public void setPage(IPage page) { this.page = page; } //建立資料庫連線池 private static ConnectionProvider connectionProvider; //建立執行緒池 private static ThreadLocal<Connection> conWrapper = new ThreadLocal<Connection>(); //開啟連線 private static Connection getConnection() throws SQLException{ Connection con = connectionProvider.getConnection(); if(con != null || !con.isClosed()){ return con; } //從執行緒池中獲取資料庫連線 con = connectionProvider.getConnection(); if(con == null){ throw new SQLException("����ȡ��ݿ�����"); } //對新建立的資料庫連線放到執行緒池中 conWrapper.set(con); return con; } //關閉連線 public static void closeConnection() throws SQLException{ //從執行緒池中獲取連線池 Connection con = conWrapper.get(); if(con != null){ con.close(); } conWrapper.remove(); } //關閉ResultSet和Statement private void cleanup(ResultSet resultSet,PreparedStatement pre){ if(resultSet != null){ try { resultSet.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(pre != null){ try { pre.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 關閉PreparedStatement * @param pre */ private void cleanup(PreparedStatement pre){ if(pre != null){ try { pre.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
Spring對事務管理的支援
事務管理關鍵抽象
TransactionDefinition:主要用來定義事務的屬性,例如:事務隔離級別、超時時間、是否為只讀事務等。
PlatformTransactionManager:根據事務指定的屬性,建立事務。
TransactionStatus:事務狀態。
事務管理器實現類
Spring在不同的框架實現了事務管理器介面PlatformTransactionManager,其中Spring為基於DataSource資料來源的持久化技術例如SpringJDBC和iBats的事務管理器類為DataSourceTransactionManager。
基於DataSource資料來源的事務管理器
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>oracle.jdbc.driver.OracleDriver</value> </property> <property name="url"> <value>jdbc:oracle:thin:@localhost:1521:orcl</value> </property> <property name="username"> <value>test</value> </property> <property name="password"> <value>test</value> </property> <property name="maxActive"> <value>255</value> </property> <property name="maxIdle"> <value>2</value> </property> <property name="maxWait"> <value>120000</value> </property> </bean> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="datasource" ref="dataSource"/> </bean>
DataSourceTransactionManager使用DataSource的Connect的commit()和rollBack()等方法管理事務。
事務同步管理器
在訪問資料庫時建立的連線或者會話統稱為資源,該資源在同一時間無法實現多執行緒共享,Spring的事務同步管理器TransactionSynchronizationManager通過ThreadLocal為不同的事務執行緒提供獨立的資源副本。無論宣告式事務還是程式設計式事務都需要事務同步管理器。
程式設計式事務管理
Spring為程式設計式事務管理提供模板類TransactionTemplate,可以在多個業務類中共享TransactionTemplate例項進行事務管理。由於Spring事務管理是基於TransactionSynchronizationManager進行工作的,所以如果在回撥介面方法顯示訪問底層資料連線,必須通過資源工具類得到執行緒繫結的資料連線。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- Connection Info --> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- Connection Pooling Info --> <property name="maxActive" value="3"/> <property name="defaultAutoCommit" value="false"/> <!-- 連線Idle一個小時後超時 --> <property name="timeBetweenEvictionRunsMillis" value="3600000"/> <property name="minEvictableIdleTimeMillis" value="3600000"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
PlatformTransactionManager程式設計式事務管理的例項:
public void testPlatformTransactionManager() { DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); Connection connection = DataSourceUtils.getConnection(dataSource); try{ connection.prepareStatement(CREATE_TABLE_SQL).execute(); PreparedStatement pstmt = connection.prepareStatement(INSERT_SQL); pstmt.setString(1, "test"); pstmt.execute(); connection.prepareStatement(DROP_TABLE_SQL).execute(); txManager.commit(status); }catch(Exception ex){ status.setRollbackOnly(); txManager.rollback(status); }finally{ DataSourceUtils.releaseConnection(connection, dataSource); } }
TransactionTemplate程式設計式事務管理的例項:
建立TransactionTemplate類
public class TransactionTemplateUtils { private static TransactionTemplate getTransactionTemplate( PlatformTransactionManager txManager, int propagationBehavior, int isolationLevel) { TransactionTemplate transactionTemplate = new TransactionTemplate(txManager); transactionTemplate.setPropagationBehavior(propagationBehavior); transactionTemplate.setIsolationLevel(isolationLevel); return transactionTemplate; } public static TransactionTemplate getDefaultTransactionTemplate(PlatformTransactionManager txManager) { return getTransactionTemplate(txManager, TransactionDefinition.PROPAGATION_REQUIRED, TransactionDefinition.ISOLATION_READ_COMMITTED); } }
通過TransactionTemplate類設定事務管理
public void save(final User user){ TransactionTemplate transactionTemplate = TransactionTemplateUtils.getDefaultTransactionTemplate(txManager); transactionTemplate.execute(new TransactionCallbackWithoutResult(){ @Override protected void doInTransactionWithoutResult(TransactionStatus arg0) { userDao.save(user); user.getAddress().setUserId(user.getId()); try { addressService.save(user.getAddress()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); }
XML配置宣告式事務管理
Spring的宣告式事務管理是通過SpringAOP實現的,Spring負責將事務管理器增強邏輯織入到業務方法連線點。
宣告式事務管理的過程:
1、建立目標類。
2、在xml中配置資料來源。
3、宣告事務管理器。
4、使用事務代理工廠類目標業務類織入增強。
例項:
<!-- 配置JDBC資料來源的區域性事務管理器,使用DataSourceTransactionManager類,該類實現了PlatformTransactionManager介面,是針對採用資料來源連線的特定實現 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 配置TransactionManager時需要注入資料來源引用 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 下面這個是前面定義的業務Bean --> <bean id="newsDao" class="com.abc.dao.impl.NewsDaoImpl"> <!-- 為業務Bean注入屬性 --> <property name="dataSource" ref="dataSource" /> </bean> <bean id="newsDaoTransProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 為事務代理工廠Bean注入事務管理器 --> <property name="transactionManager" ref="transactionManager" /> <!-- 要在哪個Bean上面建立事務代理物件 --> <property name="target" ref="newsDao" /> <!-- 指定事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
業務邏輯中使用事務
package com.abc.dao.impl; public class NewsDaoImpl implements NewsDao { private DataSource dataSource; public void setDataSource(DataSrouce dataSource) { this.dataSource = dataSource; } public void insert(String title, String content) { JdbcTemplate template = new JdbcTemplate(dataSource); template.update("insert into news_table values (....)"); //兩次相同的操作,將違反主鍵約束 template.update("insert into news_table values (....)"); } }
註解配置宣告式事務管理
註解事務管理器的過程
1、在目標類註解事務@Transaction。
2、在xml中配置資料來源。
3、配置事務管理器。
4、通過<tx:annotation-driven>織入標註@Transaction註解的Bean進行加工處理事務管理器切面。
@Transaction屬性
ioslation:事務隔離級別
readOnly:事務讀寫屬性
timeOut:超時屬性
rollbackFor:遇到一組異常,需要回滾。
@Transaction標註位置
@Transaction可以標註在類上或者方法上,如果只標註在類上表示整個類中的方法都會使用該事務。如果只標註在方法上表示該事務只對單獨的方法起作用。如果在類上或者方法上都存在事務,方法上的事務會覆蓋類上的事務。
使用不同事務管理器
在同一個類中不同的方法上會標註不同的事務管理器,不同的事務管理器所嵌入的資料來源不同。
例項:
<!-- PlatformTransactionMnager1-->
<bean id="txManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1" />
<qualifier value="transaction1"/>
</bean>
<!-- enable transaction annotation support -->
<tx:annotation-driven transaction-manager="txManager1" />
<!-- PlatformTransactionMnager2 -->
<bean id="txManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource2" />
<qualifier value="transaction2"/>
</bean>
<!-- enable transaction annotation support -->
<tx:annotation-driven transaction-manager="txManager2" />
對業務處理進行事務註解
@Transactional (value="transaction1") public class TestServiceBean implements TestService { private TestDao dao; public void setDao(TestDao dao) { this.dao = dao; } @Transactional(value="transaction2",propagation =Propagation.NOT_SUPPORTED) public List getAll() { return null; } }