zoukankan      html  css  js  c++  java
  • Service 事务(JdbcUtils 升级)

    1. DAO 事务

    // 在 DAO 中处理事务真是"小菜一碟"
    
    public void xxx(){
        Connection con = null;
        try{
            con = JdbcUtils.getConnection();
            con.setAutoCommit(false);  // 开启事务
    
            QueryRunner qr = new QueryRunner();
            String sql = ...;
            Object[] params = ...;
            qr.update(con,sql,params);
    
            sql = ...;
            Object[] params = ...;
            qr.update(con,sql,params);
            con.commit();  // 提交事务
        } catch(Exception e){
                try{
                    // 回滚事务
                    if(con != null) {con.rollback();}
                }catch(Exception e){}
    
        }finally{
            try{
                con.close();    
            }catch(Exception e){}
        }
    }
    

    2. Service 才是处理事务的地方

    • DAO 中不是处理事务的地方,因为 DAO 中的每个方法都是对数据库的一次操作, 而 Service 中的方法才是
      对应一个业务逻辑,也就是我们需要在 Service 中的一方法中调用 DAO 的多个方法,而这些方法应该在一个
      事务中.
    // 事务需要保证为同一个 Connection
    // 可以通过向 DAO 中传递 Connection, 来保证 DAO 的多个方法使用相同的 Connection
    
    public class XXXService(){
        private XXXDao dao = new XXXDao();
        public void serviceMethod(){
    
            // 但是 Connection 对象只能出现在 DAO 中, 因为它是 JDBC 的东西,
            // JDBC 的东西是用来连接数据库的, 连接数据库是由 DAO负责, 而事务却
            // 应该由 Service 负责.
    
            Connection con = null;
            try{
                con = JdbcUtils.getConnection();
                con.setAutoCommit(false);
    
                // 向 DAO 中传递 Connection
                dao.daoMethod2(con,...);
                dao.datMethod3(con,...);
                con.commit();
            }catch(Exception e){
                try{
                    con.rollback();
                } catch(Exception e){}
            }finally{
                try{
                    con.close();
                }catch(Exception e){}
            }
        }
    }
    

    3. 修改 JdbcUtils

    1. 把对事物的开启和关闭放到 JdbcUtils 中,在 Service 中调用 JdbcUtils 的方法来完成事务的处理,
      但在 Service 中就不会再出现 Connection 了.
    2. DAO 中的方法不用再让 Service 来传递 Connection 了, DAO 会主动从 JdbcUtils 中获取 Connection
      对象, 这样, JdbcUtils 成为了 DAO 和 Service 的中介!

    // Service 中的代码
    public class XXXService(){
        private XXXDao dao = new XXXDao();
        public void serviceMethod(){
            try{
                JdbcUtils.beginTransaction();
                dao.daoMethod2();
                dao.daoMethod3();
                JdbcUtils.commitTransaction();
            }catch(Exception e){
                JdbcUtils.rollbackTransaction();
            }
        }
    }
    
    
    // JdbcUtils 代码
    
    public class JdbcUtils{
        private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
    
        // 它是事务专用连接, 并且每个线程分配一个Connection
        private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    
        public static Connection getConnection() throws SQLException{
    
            Connection con = tl.get(); // 获取当前线程的 con
    
            // 当 con 不等于 null, 说明已经调用过 beginTransaction() 方法了.表示开启了事务.
            if(con != null) return con;
            return dataSource.getConnection();
        }
    
        public static DataSource getDataSource(){
            return dataSource();
        }
    
        // 添加开启事务的方法
        // 获取一个 Connection, 设置它的 setAutoCommit(false)
        // 还要保证 DAO 中使用的连接是我们刚刚创建的!!
    
        /*
         * 1. 创建一个 Connection, 设置为手动提交
         * 2. 把这个 Connection 给 DAO 用!
         * 3. 还要让 commitTransaction 或 rollbackTransaction 可以获取到!!
         */
        public static void beginTransaction() throws SQLException {
    
            Connection con = tl.get();
    
            if(con != null) throw new SQLException("已经开启了事务,就不要重复开启了!");
    
            con = getConnection();  // 给 con 赋值, 表示事务已经开启了.
            con.setAutoCommit(false);
    
            tl.set(con); // 把当前线程的连接保存起来.
        }
    
        // 添加提交事务的方法
        // 获取 beginTransaction 提供的 Connection, 然后调用 commit 方法
        public static void commitTransaction() throws SQLException {
    
            Connection con = tl.get(); // 获取当前线程的专用连接
    
            if(con == null) throw new SQLException("还没有开启事务,不能提交!");
    
            con.commit();
            con.close();
            // 把它设置为 null, 表示事务已经结束了.
            // 下次再去调用 getconnection(),返回的就不是 con 了.
            // con = null;  因为有了线程, 所以将 con 直接从线程中移除即可
    
            tl.remove(); // 从 tl 中移除连接
        }
    
        // 添加回滚事务的方法
        // 获取 beginTransaction 提供的 Connection, 然后调用 rollback 方法.
        public static void rollbackTransaction() throws SQLException {
    
            Connection con = tl.get(); // 获取当前线程的专用连接
    
            if(con == null) throw new SQLException("还没有开启事务,不能回滚!");
    
            con.rollback();
            con.close();
    
            tl.remove();
        }
    
        // 释放连接
        public static void releaseConnection(Connection con) throws SQLException{
    
            Connection con = tl.get(); // 获取线程中的事务
    
            // 判断 connection 是不是事务专用, 如果是, 就不关闭
            // 如果不是事务专用, 那么就要关闭
            // con 是事务专用连接, 如果 con 为 null,表示没有事务.
            // 那么, connection 肯定不是事务专用的.
            if(con == null) connection.close();
    
            // 如果 con != null, 说明有事务,那么需要判断参数连接是否与 con 相等,
            // 如果不相等, 说明 connection 不是事务专用连接.
            if(con != connection) connection.close();
    
        }
    }
    
    // DAO 层代码
    public class AccountDao{
        public void update(String name, double money) throws SQLException{
            QueryRunner qr = new QueryRunner();
            String sql = "UPDATE account SET balance=balance+? WHERE name=?";
            Object[] params = {money,name};
    
            // 我们需要自己来提供连接, 保证在同一事务中, 多次调用使用的是同一个连接!!
            Connection con = JdbcUtils.getConnection();
            qr.update(con,sql,params);
    
            // 关闭连接
            JdbcUtils.releaseConnection(connection);
        }
    }
    

    参考资料:

  • 相关阅读:
    MySQL — 优化之explain执行计划详解(转)
    项目中常用的MySQL 优化
    通过 MySQL 存储原理来分析排序和锁(转)
    java内存模型(转)
    数据库索引的创建原则
    MySQL中的几种日志了解
    一条SQL语句在MySQL中如何执行的
    SQL
    SQL
    SqlServer性能优化 查询和索引优化(十二)
  • 原文地址:https://www.cnblogs.com/linkworld/p/7627253.html
Copyright © 2011-2022 走看看