• 使用ThreadLocal实现转账事务


    先我们准备此次实验所需jar包

    commons-dbcp2-2.8.0.jar commons-dbutils-1.7.jar commons-logging-1.2.jar  commons-pool2-2.9.0.jar mysql-connector-java-5.1.47.jar

    既然是转账,我们新建一个账户类

    package TrancatePack;
    
    public class Account {
        private int id ;
        private String name ;
        private int Balance;
        
        
        public Account() {
        }    
        
        public Account(int id, String name, int balance) {
    
            this.id = id;
            this.name = name;
            Balance = balance;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getBalance() {
            return Balance;
        }
        public void setBalance(int balance) {
            Balance = balance;
        }
        
        
    }

    同时,在数据库新建账户表,并插入两条数据

    create table Account(id int,name varchar(20),balance int);
    insert into Account values(1,'ZS',10000),(2,'LS',10000);

    然后我们编写新建Connection处理类

    package TrancatePack;
    
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Properties;
    
    import org.apache.commons.dbcp2.BasicDataSource;
    import org.apache.commons.dbcp2.BasicDataSourceFactory;
    
    public class ThreadLocalDemo {
    
        private static ThreadLocal<Connection> tlc  ;
        
        public static  Connection getConn() {
            if(tlc==null) {
                tlc = new ThreadLocal<Connection>();
            }
            //首先从ThreadLocal获取连接
            
            Connection conn = tlc.get();
    
            if(conn == null)
            {
                //获取dbcp连接池
                BasicDataSourceFactory bds = new BasicDataSourceFactory();
                Properties p = new Properties();
                //以文件以流的方式加载
                InputStream in =  new ThreadLocalDemo().getClass().getClassLoader().getSystemResourceAsStream("pro.properties");
                try {
                    p.load(in);
                    //从连接池获取连接
                    conn = bds.createDataSource(p).getConnection();
                    //将连接放入ThreadLocal池
                    tlc.set(conn);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            
             return conn;
        }
        
        //开启事务
        
        public static void beginTrancate() {
            Connection conn = getConn();
            try {
                conn.setAutoCommit(false);
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        //正常结束事务
        public static void commitTrancate() {
            Connection conn = getConn();
            try {
                conn.commit();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
        }
        
        //回滚事务
        public static void rollbackTrancute() {
            Connection conn = getConn();
            try {
                conn.rollback();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //关闭连接
        public static void close() {
            Connection conn = getConn();
            try {
                conn.close();
                tlc.remove();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            
        }
        
    }

    然后我们创建账户处理类,首先新建一个接口,我们的DML处理均继承接口

    package TrancatePack;
    
    public interface ServiceDao {
    
        int update(Account from) ;
        Account getAccount(int id);
    }

    实现上面接口

    package TrancatePack;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.List;
    
    import org.apache.commons.dbutils.DbUtils;
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.BeanListHandler;
    
    public class ServiceDaoImpl implements ServiceDao {
    
        //实现DButils对象
        static QueryRunner qr = new QueryRunner();
        //返回更新结果
        @Override
        public int update(Account from) {
            //获取连接副本
            Connection conn = new ThreadLocalDemo().getConn();
            try {
                return qr.update(conn, "update Account set balance = ? where id=?", new Object[]{from.getBalance(),from.getId()} );
            } catch (SQLException e) {        
                e.printStackTrace();
                return 0;
            }
        
            
        }
    
        //返回查询结果
        @Override
        public Account getAccount(int id) {
            Account account = null;
            //获取连接副本
            Connection conn = new ThreadLocalDemo().getConn();    
            try {
                List<Account> list =  qr.query(conn, "select * from Account where id=?", new BeanListHandler<Account>(Account.class), id);
                for(Account account1:list) {
                    account = account1;
                }
                return account;
            } catch (SQLException e) {
                
                e.printStackTrace();
                return null;
            }
        }
    
    }

    上面我们就准备好了实验数据,下面进行事务性测试

    package TrancatePack;
    
    public class Transfer {
            
        public static void TransferTest() {
            
            try {
                //开启事务
                ThreadLocalDemo.beginTrancate();
                //获取两个账户
                ServiceDao sd = new ServiceDaoImpl();
                
                Account zs = sd.getAccount(1);
                Account ls = sd.getAccount(2);
                
                //开始转账 ZS ----> LS 1000
                int Transaction_amount =  1000; 
                 if(zs.getBalance()>Transaction_amount)
                 {
                     //张三减去1000
                     zs.setBalance(zs.getBalance()-Transaction_amount);
                     int a = sd.update(zs);
                     
                     //李四加上1000
                     ls.setBalance(ls.getBalance()+Transaction_amount);
                     int b = sd.update(ls);
                     
                    if(a==0 || b==0)
                    {
                        throw new Exception("系统故障");
                    }
                    ThreadLocalDemo.commitTrancate();
                     System.out.println("转账完成");
                 }else {
                     System.out.println("余额不足");
                 }
                 
            }catch (Exception e) {
                e.printStackTrace();
                ThreadLocalDemo.rollbackTrancute();
                ThreadLocalDemo.close();
            }
            
        }
        
        public static void main(String[] args) {
            TransferTest();
        }
        
    }

    当上面转账事务操作过程中抛出任何异常,包括更新返回受影响行数为0,均会回滚事务,保持数据的稳定性。

  • 相关阅读:
    转 SpringCloud服务注册中心比较:Consul vs Zookeeper vs Etcd vs Eureka
    转 微服务的4个设计原则和19个解决方案
    骑士问题
    种树(洛谷P1250)
    你的飞碟在这儿(洛谷P1200)
    Hello world
    [zt][cocos2dxwin32] 安装部署流程整理
    (ZT)关于IAP防止破解的几点
    (ZT)UIImage应用与内存管理
    在Mac上发布QT的程序
  • 原文地址:https://www.cnblogs.com/aierben/p/14537341.html
Copyright © 2020-2023  润新知