1. 程式人生 > >Spring的資料訪問---------------事務管理

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