zoukankan      html  css  js  c++  java
  • JdbcUtils(内部使用c3p0得到连接池对象datasource)的第三次修改---完成事务的处理

      把事务处理的方法从DAO层中抽离出来,写到Service层中,还有就是Service层中不能有Connection相关的东西,所以要对得到同一连接做相应的封装处理。

    进一步封装JdbcUtils,使其具有事务的功能(即,有开启事务,提交事务,回滚事务功能),并且支持多线程(ThreadLocal的应用)。

    该数据库连接池使用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)都支持事务。

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

    这样可以完成一次转账的操作。

    数据库:

     各类的使用:

  • 相关阅读:
    Linux development tools
    Windows Live Mail: getting fewer ads
    美国签证(B1)经验总结
    谁要windows live messenger(msn8.0)的邀请?
    Use Google Calendar in Office
    C#中的ReaderWriterLock和LockFree Data Structure
    第一次看到“谷歌”出现在google.cn上
    解决SQL安装时提示挂起的方法
    asp 常见错误 不能打开注册表关键字 的处理方法
    Apache Web服务器安全配置全攻略
  • 原文地址:https://www.cnblogs.com/xjs1874704478/p/10946420.html
Copyright © 2011-2022 走看看