美文网首页
2018-06-29 事务的提交与回滚

2018-06-29 事务的提交与回滚

作者: 培根好吃 | 来源:发表于2018-06-29 10:31 被阅读0次
    05-ThreadLocal对象_[00-10-53][20180629-104717416].jpg

    原子性,一致性,隔离性,持久性
    原子性(Atomicity)
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
    一致性(Consistency)
    事务前后数据的完整性必须保持一致。
    隔离性(Isolation)
    事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
    持久性(Durability)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

    1.什么是事务
    一件事情有n个组成单元 要不这n个组成单元同时成功 要不n个单元就同时失败
    就是将n个组成单元放到一个事务中

    2.mysql的事务
    默认的事务:一条sql语句就是一个事务 默认就开启事务并提交事务
    手动事务:
    1)显示的开启一个事务:start transaction
    2)事务提交:commit代表从开启事务到事务提交 中间的所有的sql都认为有效 真正的更新数据库
    3)事务的回滚:rollback 代表事务的回滚 从开启事务到事务回滚 中间的所有的 sql操作都认为无效数据库没有被更新

    3、JDBC事务操作
    默认是自动事务:
    执行sql语句:executeUpdate() ---- 每执行一次executeUpdate方法 代表 事务自动提交
    通过jdbc的API手动事务:
    开启事务:conn.setAutoComnmit(false);
    提交事务:conn.commit();
    回滚事务:conn.rollback();
    注意:控制事务的connnection必须是同一个
    执行sql的connection与开启事务的connnection必须是同一个才能对事务进行控制

    4、DBUtils事务操作
    无参构造:QueryRunner runner = new QueryRunner();
    无参的构造没有将数据源(连接池)作为参数传入QueryRunner,那么我们在使 用QueryRunner对象操作数据库时要使用有Connection参数的方法

    https://blog.csdn.net/lutianfeiml/article/details/51424084

    银行转账有个问题::

    我的connection要在dao层设置连接,但是connection的事务控制要在service层,并且service层和dao层的connection要是同一个。下面代码是不对的。

    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import javax.sql.DataSource;
    
    import com.ryan.dao.TransferDao;
    import com.ryan.utils.DataSourceUtils;
    
    public class TransferService {
    
        public boolean transfer(String out, String in, double money)  {
            TransferDao dao=new TransferDao();
            boolean isTransferSuccess=true;
            Connection connection=null;
            /*方法结束,池子会自动帮你关闭连接。  池子关上了,代表什么都没有了,事务结束了
             * 这个时候,如果别人拿到这个conn,要重新开启一个新的连接,需要重新开启新的事务*/
            try {
                connection=DataSourceUtils.getConnection();
                connection.setAutoCommit(false);
                dao.out(connection,out,money);
                
                dao.in(connection,in,money);[图片上传中...(05-ThreadLocal对象_[00-10-53][20180629-104717416].jpg-25ff8c-1530240471553-0)]
    
            } catch (SQLException e) {
                try {
                    isTransferSuccess=false;
                    connection.rollback();
                } catch (SQLException ex) {             
                    ex.printStackTrace();
                }           
                e.printStackTrace();
            }finally {
                try {
                    connection.commit();
                } catch (SQLException e) {              
                    e.printStackTrace();
                }
            }
            
            return isTransferSuccess;
        }
    
    }
    

    所以要解决这个问题 要用 ThreadLocal

    每一个ThreadLocal都是一个map,只是它的key就是当前线程的名字,值就是ThreadLocal存的内容。

    package com.itheima.transfer.service;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import com.itheima.transfer.dao.TransferDao;
    import com.itheima.utils.DataSourceUtils;
    import com.itheima.utils.MyDataSourceUtils;
    
    public class TransferService {
    
        public boolean transfer(String out, String in, double money) {
            
            TransferDao dao = new TransferDao();
            
            boolean isTranferSuccess = true;
            //Connection conn = null;
            try {
                
                //开启事务
                //conn = DataSourceUtils.getConnection();
                //conn.setAutoCommit(false);
                
                //开启事务
                MyDataSourceUtils.startTransaction();
                
                //转出钱的方法  
                dao.out(out,money);
                //int i = 1/0;
                //转入钱的方法
                dao.in(in,money);
                
            } catch (Exception e) {
                isTranferSuccess = false;
                //回滚事务
                try {
                    MyDataSourceUtils.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
                e.printStackTrace();
            } finally{
                try {
                    MyDataSourceUtils.commit();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            
            return isTranferSuccess;
            
        }
    
    }
    
    
    package com.itheima.transfer.dao;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import org.apache.commons.dbutils.QueryRunner;
    
    import com.itheima.utils.DataSourceUtils;
    import com.itheima.utils.MyDataSourceUtils;
    
    public class TransferDao {
    
        public void out(String out, double money) throws SQLException {
            QueryRunner runner = new QueryRunner();
            Connection conn = MyDataSourceUtils.getCurrentConnection();
            String sql = "update account set money=money-? where name=?";
            runner.update(conn, sql, money,out);
        }
    
        public void in(String in, double money) throws SQLException {
            QueryRunner runner = new QueryRunner();
            Connection conn = MyDataSourceUtils.getCurrentConnection();
            String sql = "update account set money=money+? where name=?";
            runner.update(conn, sql, money,in);
        }
    
    }
    
    
    package com.itheima.utils;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class MyDataSourceUtils {
    
        //获得Connection ----- 从连接池中获取
        private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
        
        //创建ThreadLocal
        private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
        
        //开启事务
        public static void startTransaction() throws SQLException{
            Connection conn = getCurrentConnection();
            conn.setAutoCommit(false);
        }
        
        //获得当前线程上绑定的conn
        public static Connection getCurrentConnection() throws SQLException{
            //从ThreadLocal寻找 当前线程是否有对应Connection
            Connection conn = tl.get();
            if(conn==null){
                //获得新的connection
                conn = getConnection();
                //将conn资源绑定到ThreadLocal(map)上
                tl.set(conn);
            }
            return conn;
        }
        
        public static Connection getConnection() throws SQLException{
            return dataSource.getConnection();
        }
    
        //回滚事务
        public static void rollback() throws SQLException {
            getCurrentConnection().rollback();
        }
    
        //提交事务
        public static void commit() throws SQLException {
            Connection conn = getCurrentConnection();
            conn.commit();
            //将Connection从ThreadLocal中移除
            tl.remove();
            conn.close();
            
        }
    
    }
    
    

    相关文章

      网友评论

          本文标题:2018-06-29 事务的提交与回滚

          本文链接:https://www.haomeiwen.com/subject/rhwoyftx.html