DAO中的事务
其实在DAO中处理事务真的是“小菜一碟”
try{
con.commit();
}catch(Exception e){
con.rollback();
}
但是dao层中只能是对账户金额的修改而不是业务的处理
service层中也可以使用刚才的格式,使用con会暴露出service直接使用数据库的问题

我们希望这样来处理事务:
public class XXXService(){
private XXXDao dao=new XXXDao();
public void serviceMethod(){
try{
JdbcUtils.beginTransaction();
dao.daoMethod1(...);
dao.daoMethod2(...);
JdbcUtils.commitTransaction();
}catch(Exception e){
JdbcUtils.rollbackTransaction();
}
}
}
修改后:

package cn.itcast.cn;

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtils {
    /*
     * 配置文件的恶魔人配置!要求你必须给出c3p0-config。xnl!
     */
    private static ComboPooledDataSource dataSource=new ComboPooledDataSource("oracle-config");
    /**
     * 它是事务专用连接
     */
    private static Connection con=null;
    /**
     * 使用连接池返回一个连接对象
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException{
        //当con!=null,表示已经调用过beginTransaction方法了
        if(con!=null) return con;
        return dataSource.getConnection();
    }
    
    /**
     * 返回连接池对象
     * @return
     */
    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 beignTransaction() throws SQLException{
        if(con!=null) throw new SQLException("已经开始了事务,就不要继续开启事务了!");
        con=getConnection();
        con.setAutoCommit(false);
    }
    /**
     * 提交事务
     * 获取之前开启的Connection,兵提交
     * @throws SQLException 
     */
    public static void commitTransaction() throws SQLException{
        if(con==null) throw new SQLException("还没有开启事务,不能提交!");
        con.commit();
        con.close();
        con=null;//因为前面的close()不会销毁连接而是放回连接池
    }
    /**
     * 回滚事务
     * 获取之前开启的Connection,兵回滚
     * @throws SQLException 
     */
    public static void rollbackTransaction() throws SQLException{
        if(con==null) throw new SQLException("还没有开启事务,不能提交!");
        con.rollback();
        con.close();
        con=null;//因为前面的close()不会销毁连接而是放回连接池
    }
}

 

package cn.itcast.cn;

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

import org.apache.commons.dbutils.QueryRunner;

public class AccountDao {
    public static void update(String name,double money) throws SQLException{
        QueryRunner qr=new QueryRunner();
        String sql="UPDATE account SET balance=balance+? WHERE aname=?";
        Object[] params={money,name};
        
        //我们需要自己来提供连接,保证多次调用使用的是同一个连接
        Connection con=JdbcUtils.getConnection();
        qr.update(con, sql, params);
    }
}

 

package cn.itcast.cn;

import java.sql.SQLException;

import org.junit.Test;

@SuppressWarnings("static-access")
public class Demo1 {
    private AccountDao dao=new AccountDao();
    @Test
    public void serviceMethod(){
        try{
            JdbcUtils.beignTransaction();
            dao.update("zs", -1000);
            dao.update("lisi", +1000);
            JdbcUtils.commitTransaction();
    }catch(Exception e){
        try {
            JdbcUtils.rollbackTransaction();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
    }

    }
}

 

针对前面的针对多线程并发问题和代码复杂度问题作出的再次优化:

package cn.itcast.cn;

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtils {
    /*
     * 配置文件的恶魔人配置!要求你必须给出c3p0-config。xnl!
     */
    private static ComboPooledDataSource dataSource=new ComboPooledDataSource();
    /**
     * 它是事务专用连接
     */
    private static ThreadLocal<Connection> t1=new ThreadLocal<Connection>();
    /**
     * 使用连接池返回一个连接对象
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException{
        //当con!=null,表示已经调用过beginTransaction方法了
        Connection con=t1.get();
        if(con!=null) return con;
        return dataSource.getConnection();
    }
    
    /**
     * 返回连接池对象
     * @return
     */
    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 beignTransaction() throws SQLException{
        Connection con=t1.get();
        if(con!=null) throw new SQLException("已经开始了事务,就不要继续开启事务了!");
        con=getConnection();
        con.setAutoCommit(false);
        t1.set(con);//把连接保存起来
    }
    /**
     * 提交事务
     * 获取之前开启的Connection,兵提交
     * @throws SQLException 
     */
    public static void commitTransaction() throws SQLException{
        Connection con=t1.get();
        if(con==null) throw new SQLException("还没有开启事务,不能提交!");
        con.commit();
        con.close();
//        con=null;//因为前面的close()不会销毁连接而是放回连接池
        t1.remove();//从t1中移除连接
    }
    /**
     * 回滚事务
     * 获取之前开启的Connection,兵回滚
     * @throws SQLException 
     */
    public static void rollbackTransaction() throws SQLException{
        Connection con=t1.get();
        if(con==null) throw new SQLException("还没有开启事务,不能提交!");
        con.rollback();
        con.close();
//        con=null;//因为前面的close()不会销毁连接而是放回连接池
        t1.remove();
    }
    
    
    public static void releaseConnection(Connection connection) throws SQLException{
        /*
         *判斷它是不是中事務專用,如果是就不關閉
         *如果不是就要關閉
         */
        //如果con==null,說明沒有事務,那麼connection一定不是事務專用的
        Connection con=t1.get();
        if(con==null)    connection.close();
        if(con!=connection) connection.close();
        
    }
}

 

package cn.itcast.cn;

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

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
/**
 * 这个类中的方法自己来处理连接的问题
 * 无需外界传递
 * 怎么处理的呢?
 *     通过JdbcUtils.getConnection()得到连接!有可能是事务连接也有可能是普通连接
 * JdbcUtils.releaseConnection()完成连接的释放
 * @author Administrator
 *
 */
public class TxQueryRunner extends QueryRunner{

    @Override
    public int[] batch(String sql, Object[][] params) throws SQLException {
        /**
         * 得到连接
         * 执行父类方法
         * 释放连接
         * 返回值
         */
        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;
    }
    
}

 

package cn.itcast.cn;

import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;

public class AccountDao {
    public static void update(String name,double money) throws SQLException{
//        QueryRunner qr=new QueryRunner();
        QueryRunner qr=new TxQueryRunner();
        String sql="UPDATE account SET balance=balance+? WHERE aname=?";
        Object[] params={money,name};
        
        //我们需要自己来提供连接,保证多次调用使用的是同一个连接
//        Connection con=JdbcUtils.getConnection();
//        qr.update(con, sql, params);
//        JdbcUtils.releaseConnection(con);
        qr.update(sql,params);
    }
}

package cn.itcast.cn;

import java.sql.SQLException;

import org.junit.Test;

@SuppressWarnings("static-access")
public class Demo1 {
    private AccountDao dao=new AccountDao();
    @Test
    public void serviceMethod() throws Exception{
        try{
            JdbcUtils.beignTransaction();
            dao.update("zs", -1000);
            if(true) throw new RuntimeException("不好依稀");
            dao.update("lisi", +1000);
            JdbcUtils.commitTransaction();
    }catch(Exception e){
        try {
            JdbcUtils.rollbackTransaction();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        throw e;
    }

    }
}