JdbcUtils(內部使用c3p0獲得鏈接池對象datasource)的第三次修改---完成事務的處理

  把事務處理的方法從DAO層中抽離出來,寫到Service層中,還有就是Service層中不能有Connection相關的東西,因此要對獲得同一鏈接作相應的封裝處理。java

進一步封裝JdbcUtils,使其具備事務的功能(即,有開啓事務,提交事務,回滾事務功能),而且支持多線程(ThreadLocal的應用)。sql

該數據庫鏈接池使用c3p0數據庫

 1.JdbcUtils.java:

package com.xjs.jdbcutils;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtils {
    //使用文件的默認配置,要求必須給出c3p0-config.xml
    private static ComboPooledDataSource dataSource=new ComboPooledDataSource();
    //它是事務專用鏈接---容許多線程訪問
    private static ThreadLocal<Connection> tl=new ThreadLocal<Connection>();
    
    //使用鏈接池返回一個鏈接對象
    public static Connection getConnection() throws SQLException{
        //獲得本身線程的con,解決多線程問題
        Connection con=tl.get();
        //當con不爲null時,說明已經調用過beginTransaction方法,表示開啓了事務
        if(con != null){
            return con;
        }
        //若是是簡單的獲取鏈接的話,就返回鏈接池建立的鏈接
        return dataSource.getConnection();
    }
    
    //返回鏈接池對象
    public static DataSource getDataSource(){
        return dataSource;
    }
    /**
     * 開啓事務
     * 1.獲取一個connection,設置它的setAutoCommit(false)
     * 2.還有保證DAO中使用的鏈接是咱們剛剛建立的!
     * ------------------
     * 1.建立一個Connection,設置爲手動提交(事務)
     * 2.把這個Connection給DAO用!
     * 3.還要讓commitTransaction或rollbackTransaction能夠獲取到!
     * @throws SQLException 
     */
    public static void beginTransaction() throws SQLException{
        Connection con=tl.get();
        if(con != null) throw new SQLException("已經開啓了事務,就不要重複開啓了!");
        /*
         * 1.給con賦值!
         * 2.給con設置手動提交!
         */
        con=getConnection();
        con.setAutoCommit(false);
        
        tl.set(con);//把當前線程的;鏈接保存起來
    }
    /**
     * 提交事務
     * 1.獲取beginTransaction提供的Connection,而後調用commit方法
     * @throws SQLException 
     */
    public static void commitTransaction() throws SQLException{
        Connection con=tl.get();//獲取當前線程的專用鏈接
        if(con == null) throw new SQLException("尚未開啓了事務,不能提交!");
        //直接使用con.commit()
        con.commit();
        con.close();//在這只是把鏈接歸還給鏈接池了,這時con不爲null;防止後面再次使用時出錯,把con賦爲null
        tl.remove();//從tl中移除鏈接
    }
    /**
     * 回滾事務
     * 1.獲取beginTransaction提供的Connection,而後調用rollback方法
     * @throws SQLException 
     */
    public static void rollbackTransaction() throws SQLException{
        Connection con=tl.get();
        if(con == null) throw new SQLException("尚未開啓了事務,不能關閉!");
        //直接使用con.rollback()
        con.rollback();
        con.close();
        //把它設置爲null,表示事務已經結束了!下次在調用getConnection()返回的就不是con了!
        
        tl.remove();
    }
    /**
     * 釋放鏈接
     * @param connection
     * @throws SQLException 
     */
    public static void releaseConnection(Connection connection) throws SQLException{
        Connection con=tl.get();
        /*
         * 判斷它是否是事務專用,若是是,就不關閉!
         * 若是不是事務專用,那麼就要關閉!
         */
        //若是con=null,說明如今沒有事務,那麼connection必定不是事務專用!
        if(con == null) connection.close();
        
        //若是con!=null,說明有事務,那麼須要判斷參數鏈接是否與con相等,若不等,說明參數鏈接不是事務專用鏈接
        if(con != connection) connection.close();
        
    }

    
}

  QueryRunner類是apache對數據庫操做進行的封裝,對事務的實現不完善,本身寫一個TxQueryRunner類繼承QueryRunner類,重寫父類中(參數中帶Connection)的方法。在開啓事務以後,TxQueryRunner類中的方法都已經獲得事務專用的connection(使用時都須要傳遞參數Connection)都支持事務。apache

2.TxQueryRunner.java:

package com.xjs.jdbcutils;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;

/**
 * 這個類中的方法,本身類處理鏈接的問題
 * 無需外界傳遞!
 * 怎樣處理的呢?
 *     經過JdbcUtils.getConnection()獲得鏈接!又多是事務鏈接,也有多是普通鏈接!
 *     JdbcUtils.releaseConnection()完成對鏈接的釋放!若是是普通鏈接,關閉之(即把con歸還給池)!
 * @author hp
 *
 */
public class TxQueryRunner extends QueryRunner{

    /**
     * 繼承父類,重寫父類中不帶connection參數的方法,本身給那些不帶con參數的方法加上con,
     * 在方法前面獲得con,方法後面釋放con
     * 這樣能保證這個類中的全部方法都使用Connection對象
     */
    @Override
    public int[] batch(String sql, Object[][] params) throws SQLException {
        /*
         * 1.獲得鏈接
         * 2.執行父類方法,傳遞鏈接對象
         * 3.釋放鏈接
         * 4.返回值
         */ 
        Connection con=JdbcUtils.getConnection();
        int[] result=super.batch(con, sql, params);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T query(String sql, Object param, ResultSetHandler<T> rsh)
            throws SQLException {
        Connection con=JdbcUtils.getConnection();
        T result=super.query(con, sql, param, rsh);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T query(String sql, Object[] params, ResultSetHandler<T> rsh)
            throws SQLException {
        Connection con=JdbcUtils.getConnection();
        T result=super.query(con, sql, params, rsh);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)
            throws SQLException {
        Connection con=JdbcUtils.getConnection();
        T result=super.query(con, sql, rsh, params);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
        Connection con=JdbcUtils.getConnection();
        T result=super.query(con, sql, rsh);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql) throws SQLException {
        Connection con=JdbcUtils.getConnection();
        int result=super.update(con, sql);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql, Object param) throws SQLException {
        Connection con=JdbcUtils.getConnection();
        int result=super.update(con, sql, param);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql, Object... params) throws SQLException {
        Connection con=JdbcUtils.getConnection();
        int result=super.update(con, sql, params);
        JdbcUtils.releaseConnection(con);
        return result;
    }
}

3.模擬Dao層:

package com.xjs.jdbcutils;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
//Dao層--轉帳
public class AccountDao {

    public static void update(String name,double money) throws SQLException{
        //使用QueryRunner(對操做數據庫一些相同步驟進行了封裝),完成對數據庫的操做---使用commons-dbutils包中的類
        QueryRunner qr=new TxQueryRunner();
        String sql="update account set balance=balance+? where name=?";
        Object[] params={money,name};
        qr.update(sql, params);//沒有con,在方法的內部有con
    }
}

該Dao層中使用的TxQueryRunner類中的方法,其方法中有事務專用的鏈接con,以及con的釋放。注意:其實在這的這個事務專用鏈接con沒有關閉,也沒有歸還給池。由於在下邊的其餘方法中還需用到它對數據庫操做。多線程

4.模擬service層:---調用dao層

package com.xjs.jdbcutils;

import java.sql.SQLException;

import org.junit.Test;

//模擬service層
public class Demo1 {
    private AccountDao dao = new AccountDao();

    @Test
    public void serviceMethod() throws Exception {
        try {
            JdbcUtils.beginTransaction();

            dao.update("zs", -100);
            /*if (true){
                throw new RuntimeException("拋出異常。。。");
            }*/
            dao.update("ls", 100);

            JdbcUtils.commitTransaction();
        } catch (Exception e) {
            try {
                JdbcUtils.rollbackTransaction();
            } catch (SQLException e1) {
            }
            throw e;
        }
    }
}

這樣能夠完成一次轉帳的操做。ide

數據庫:spa

 各種的使用:線程

相關文章
相關標籤/搜索