zoukankan      html  css  js  c++  java
  • (二十四)JDBC应用的事务管理(转账事例)

    目录


    利用 Dbutils 进行事务操作(以转账为例)

        我们只在dao层进行增删改查操作。切忌在dao层直接进行转账的业务逻辑 ;
    
    
        我们在services层开启一个连接,在连接上开启事务,然后在这个链接上进行多条sql操作 ;
    
    
    
    ·老方说,其实上面那样写,还是不优雅的;优雅的写法:spring事务管理或者使用 ThreadLocal 类 ;
    
                            (备注:经常听到spring的大名,很好奇spring到底是怎样的存在呢,现在还没学,很少好奇!)
    

    转账实现方式(不优雅的方式)

    dao层代码:

    public class EmployeeDao1 {
    
        public void setConnection(Connection connection) {
            this.connection = connection;
        }
    
        private Connection connection;
    
        public EmployeeDao1() {
        }
    
        public EmployeeDao1(Connection connection) {
            this.connection = connection;
        }
    
        public void update(Employee e) throws SQLException {
    //        不要将连接池作为参数,这里我们要用事务
            QueryRunner runner = new QueryRunner();
            String sql = "update employee set money = ? where name = ?";
            Object[] params = {e.getMoney(), e.getName()};
            runner.update(connection, sql, params);
        }
    
        public Employee find(int id) throws SQLException {
    
            QueryRunner runner = new QueryRunner();
            String sql = "select * from employee where id =?";
            Employee employee = runner.query(connection,sql,new BeanHandler<Employee>(Employee.class),id);
            return employee;
        }
    
    }

    services层进行调用:

    /**
         * 转账的基础版本(不优雅的实现)
         *
         * @param sourceId 源账户的id
         * @param targetId 目标账户的id
         * @param money    转换的金额
         */
        public void transaction1(int sourceId, int targetId, int money) throws SQLException {
            Connection connection = null;
            try {
    //            获取连接
                connection = JdbcDataSourceUtils.getConnection();
                System.out.println(" --- "+connection);
    //            开启事务
                connection.setAutoCommit(false);
    //            获取Dao层对象,将开启事务的连接 传递进去
                EmployeeDao1 dao1 = EmployeeFactory.getDao1(connection);
    //          查找用户
                Employee s = dao1.find(sourceId);
                Employee t = dao1.find(targetId);
    //            转账逻辑 a减b增
                s.setMoney(s.getMoney() - money);
                t.setMoney(t.getMoney() + money);
    //            进行转换
                dao1.update(s);
                dao1.update(t);
    //            提交事务
                connection.commit();
            } finally {
                connection.close();
            }
        }

    我们可以看到,我们在调用dao层,需要传进去连接,还需要在连接上,开启事务,最后还有提交。每次进行类似的逻辑,都需要 重复 这样的逻辑代码。


    ThreadLocal 类

        其实内部就是一个Map集合 ;
    
        ·ThreadLocal 类在 J2EE 中很重要,它可以在线程内共享数据 ;以后在services、dao层之间传递数据,就不需要再利用方法参数传递了
    
        利用方法参数,太死板了;我们就可以选择 ThreadLocal 类来完成了。用法:写一个工具类,内部维护着一个 ThreadLocl 对象;
    
        不要说什么域,存数据,我就问下,dao层有request对象吗,能取到域中数据吗。。。
    
        ·许多框架使用了这个类 ;
    
    
        ·以后学框架,不但要学会用法,也要学习下源码,实现原理。。
    
    
        ·现在我们学的时事务,是一个services里面的多个dao操作的事务。
    
        ·以后还要学习多个services为一个事务,就需要用到过滤器了,可以保证一次请求以内的所有操作,都在一个事务里面。
    

    转账实现方式(优雅的方式)

    dao层代码:(很简短)

    public class EmployeeDao2 {
    
    
        public void update(Employee e) throws SQLException {
    //        不要将连接池作为参数,这里我们要用事务
            QueryRunner runner = new QueryRunner();
            String sql = "update employee set money = ? where name = ?";
            Object[] params = {e.getMoney(), e.getName()};
            runner.update(JdbcDataSourceUtils.getConnection(), sql, params);
        }
    
        public Employee find(int id) throws SQLException {
    
            QueryRunner runner = new QueryRunner();
            String sql = "select * from employee where id =?";
            Employee employee = runner.query(JdbcDataSourceUtils.getConnection(),sql,new BeanHandler<Employee>(Employee.class),id);
            return employee;
        }
    
    }

    services层代码:

    /**
         * 转账逻辑 优雅的实习方式
         * @param sourceId 源客户id
         * @param targetId 目标客户id
         * @param money 转账金额
         * @throws SQLException
         */
        public void transaction2(int sourceId, int targetId, int money) throws SQLException {
            try{
                //        在线程上绑定已经开启事务的连接
                JdbcDataSourceUtils.startTransaction();
    //       获取dao层实例
                EmployeeDao2 dao2 = EmployeeFactory.getDao2() ;
    //      查找用户
                Employee s = dao2.find(sourceId) ;
                Employee t = dao2.find(targetId) ;
    //            转账逻辑 a减b增
                s.setMoney(s.getMoney() - money);
                t.setMoney(t.getMoney() + money);
    //            进行转换
                dao2.update(s);
                dao2.update(t);
    //        提交事务
                JdbcDataSourceUtils.commitTransaction();
            }finally {
                JdbcDataSourceUtils.closeConnetion();
            }
        }

    其中涉及到的工具类代码:(ThreadLoacl类的使用)

    /**
         * 获取连接,如若线程上已经绑定连接,则返回线程上绑定的连接,否则则从连接池中获取一个连接返回
         *
         * @return 连接
         */
        public static Connection getConnection() {
            //      先判断线程上是否有Connection
            Connection connection = tl.get();
            if (null == connection) {
                try {
                    connection = dataSource.getConnection();
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
            return connection;
        }
    
    
        /**
         * 为当前线程绑定一个开启事务的连接
         */
        public static void startTransaction() {
    //      先判断线程上是否有Connection
    //        get 也是由执行这个方法的线程调用
            Connection connection = tl.get();
            if (null == connection) {
                try {
                    connection = dataSource.getConnection();
                    tl.set(connection);
                    //        开启事务
                    connection.setAutoCommit(false);
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        }
    
    
        /**
         * 提交事务
         */
        public static void commitTransaction() {
            try {
                Connection connection = tl.get();
                if (connection != null) {
                    connection.commit();
                }
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    
    
        /**
         * 关闭连接
         */
        public static void closeConnetion() {
            try {
                Connection connection = tl.get();
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            } finally {
    //            哪一个线程调用这个方法,就按照线程为key去移除对应的Connection
                tl.remove();
            }
        }
  • 相关阅读:
    typescript学习记录-联合类型(14)
    typescript学习记录-元组(13)
    typescript学习记录-Map(对象)(12)
    typescript学习记录-Array(数组)(11)
    typescript学习记录-String(10)
    typescript学习记录-Number(9)
    typescript学习记录-函数(8)重点重点
    typescript学习记录-循环(7)
    SQL注入基础知识及绕过方式
    暴力破解攻击方式及思路
  • 原文地址:https://www.cnblogs.com/young-youth/p/11665697.html
Copyright © 2011-2022 走看看