代码改变世界
[登录 · 注册]
  • 一个MVC架构的线程安全的银行转账案例(事务控制)
  • mvc结构:

    准备阶段:jar包 ,dbcpconfig.propertie(数据源配置文件 ) ,DBCPUtil。

    jar包:

    dbcp配置文件:

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/zhl
    username=root
    password=root
    
    initialSize=10
    
    maxActive=50
    
    maxIdle=20
    
    minIdle=5
    
    maxWait=60000
    
    
    connectionProperties=useUnicode=true;characterEncoding=utf8
    
    defaultAutoCommit=true
    
    defaultReadOnly=
    
    defaultTransactionIsolation=REPEATABLE_READ

     DBCPUti类:

    package com.chensi.test;
    
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import org.apache.commons.dbcp.BasicDataSourceFactory;
    
    public class DBCPUtil {
        private static DataSource ds;
        
        public static DataSource getDs() {
            return ds;
        }
        public static void setDs(DataSource ds) {
            DBCPUtil.ds = ds;
        }
        static{
            try {
                InputStream in = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
                Properties props = new Properties();
                props.load(in);
                ds = BasicDataSourceFactory.createDataSource(props);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        public static Connection getConnection(){
            try {
                return ds.getConnection();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        public static void release(ResultSet rs,Statement stmt,Connection conn){
            if(rs!=null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                rs = null;
            }
            if(stmt!=null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                stmt = null;
            }
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn = null;
            }
        }
    }

     业务代码:

    实体类账户类:

    package com.chensi.domain;
    
    public class Account {
    
        private String Id;
        private String name;
        private int money;
        public String getId() {
            return Id;
        }
        public void setId(String id) {
            Id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getMoney() {
            return money;
        }
        public void setMoney(int money) {
            this.money = money;
        }
        @Override
        public String toString() {
            return "Account [Id=" + Id + ", name=" + name + ", money=" + money + "]";
        }
    }

    dao层:

    package com.chensi.dao.impl;
    
    import java.sql.SQLException;
    
    import org.apache.commons.dbutils.DbUtils;
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.BeanHandler;
    
    import com.chensi.dao.TransformDao;
    import com.chensi.domain.Account;
    import com.chensi.test.DBCPUtil;
    
    import com.chensi.utils.ManagerThreadLocal;
    
    public class TransformDaoImpl implements TransformDao {
    
        public Account findAccountByUsername(String username) throws SQLException {
            QueryRunner queryRunner = new QueryRunner(DBCPUtil.getDs());
            Account account = queryRunner.query(ManagerThreadLocal.getConnection(),"select * from Account where name=?", new BeanHandler<Account>(Account.class),username);
            return account;
        }
    
        public void TransfromMoney(Account account) throws SQLException {
            QueryRunner queryRunner = new QueryRunner(DBCPUtil.getDs());
            queryRunner.update(ManagerThreadLocal.getConnection(),"update account set money=? where name=?",account.getMoney(),account.getName());
        }
    }

    service层:

    package com.chensi.service.impl;
    
    import java.sql.SQLException;
    
    import com.chensi.dao.TransformDao;
    import com.chensi.dao.impl.TransformDaoImpl;
    import com.chensi.domain.Account;
    import com.chensi.service.TransfromService;
    import com.chensi.utils.ManagerThreadLocal;
    
    public class TransfromServiceInmpl implements TransfromService {
    
        TransformDao dao = new TransformDaoImpl(); 
        //转账的方法
        public void tranFromMoney(String fromUsername, String toUserName, int money) throws SQLException {
            
            try {
                ManagerThreadLocal.startTransaction();
                Account fromAccount = dao.findAccountByUsername(fromUsername);
                Account toAccount = dao.findAccountByUsername(toUserName);
                
                fromAccount.setMoney(fromAccount.getMoney()-money);
                 
                toAccount.setMoney(toAccount.getMoney()+money);
                
                dao.TransfromMoney(toAccount);
                dao.TransfromMoney(fromAccount);
                ManagerThreadLocal.commit();
            } catch (Exception e) {
                ManagerThreadLocal.rollback();
            }finally{
                ManagerThreadLocal.close();
            }
        }
    
    }

    controller层(测试用的,所以写的比较简单):

    package com.chensi.controller;
    
    import java.sql.SQLException;
    
    import org.junit.Test;
    
    import com.chensi.service.TransfromService;
    import com.chensi.service.impl.TransfromServiceInmpl;
    
    public class TransFromController {
    
        public static void main(String[] args) throws SQLException {
            TransfromService service = new TransfromServiceInmpl();
            service.tranFromMoney("zhl", "chensi", 200);
        }
    }

    控制线程安全的类(自己实现的线程内部类)

    package com.chensi.utils;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import com.chensi.test.DBCPUtil;
    
    //用ThreadLocal线程内部类 来控制 取到的connection是一个线程中的connection,这样可以-
    //安全的控制事务
    public class ManagerThreadLocal {
    
        private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
        
        //获取到一个连接 (static 是为了  类名. 调用)
        public static Connection getConnection(){
             Connection conn = tl.get();
             if(conn==null){
                 conn = DBCPUtil.getConnection();
                 tl.set(conn); //从DBCP线程池中取出一个connn放入到ThreadLocal的Map之中
             }
            return conn;
        }
        
        //开启事务
        public static void startTransaction(){
            Connection connection = getConnection();
            try {
                connection.setAutoCommit(false); //开启事务 (这个连接是从当前线程取出来的)
            } catch (SQLException e) {
                e.printStackTrace();
            } 
        }
        //提交
        public static void commit(){
            try {
                getConnection().commit(); //提交事务
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //回滚 
        public static void rollback(){
            try {
                getConnection().rollback(); //回滚事务
            } catch (SQLException e) {
                e.printStackTrace();
            } 
        }
        //释放,也即是将conn从ThreadLocal中的Map中移出出去
        public static void close(){
            try {
                getConnection().close(); //连接关闭,然后将threadLocal中的connection 清除
                tl.remove(); //threadLocal(当前线程中清除掉connection)
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
    }

    效果:转账前:

    转账之后:

  • 【推广】 阿里云小站-上云优惠聚集地(新老客户同享)更有每天限时秒杀!
    【推广】 云服务器低至0.95折 1核2G ECS云服务器8.1元/月
    【推广】 阿里云老用户升级四重礼遇享6.5折限时折扣!
  • 原文:https://www.cnblogs.com/zhanghaoliang/p/5749840.html
Copyright 2008-2020 晋ICP备12007731号-1