一个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();
        }
    }
    
}

效果:转账前:

转账之后:

原文地址:https://www.cnblogs.com/zhanghaoliang/p/5749840.html