动力节点旗下在线教育品牌  |  咨询热线:400-8080-105 学Java全栈,上蛙课网
首页 > 文章

Apache_DBUtils框架教程学习笔记(四)_事务处理

07-12 11:17 928浏览
举报 T字号
  • 大字
  • 中字
  • 小字

4.3、使用ThreadLocal进行线程绑定对象来处理事务

  上面的在businessService层这种处理事务的方式依然不够优雅,为了能够让事务处理更加优雅,我们使用ThreadLocal类进行改造,ThreadLocal一个容器,向这个容器存储的对象,在当前线程范围内都可以取得出来,向ThreadLocal里面存东西就是向它里面的Map存东西的,然后ThreadLocal把这个Map挂到当前的线程底下,这样Map就只属于这个线程了

  ThreadLocal类的使用范例如下:

 

package wkcto.com.test;
//线程绑定对象
public class ThreadLocalTest {

    public static void main(String[] args) {
        //得到程序运行时的当前线程
        Thread currentThread = Thread.currentThread();
        System.out.println(currentThread);
        //ThreadLocal一个容器,向这个容器存储的对象,在当前线程范围内都可以取得出来
        ThreadLocal t = new ThreadLocal();
        //把某个对象绑定到当前线程上 对象以键值对的形式存储到一个Map集合中,对象的的key是当前的线程,如: map(currentThread,"aaa")
        t.set("aaa");
        //获取绑定到当前线程中的对象
        String value = t.get();
        //输出value的值是aaa
        System.out.println(value);
    }
}

 

 

使用使用ThreadLocal类进行改造数据库连接工具类JdbcUtils,改造后的代码如下:

 

package wkcto.com.util;

import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
* @ClassName: JdbcUtils2
* @Description: 数据库连接工具类
*/ 
public class JdbcUtils2 {
    
    private static ComboPooledDataSource ds = null;
    //使用ThreadLocal存储当前线程中的Connection对象
    private static ThreadLocal threadLocal = new ThreadLocal();
    
    //在静态代码块中创建数据库连接池
    static{
        try{
            //通过代码创建C3P0数据库连接池
            /*ds = new ComboPooledDataSource();
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbcstudy");
            ds.setUser("root");
            ds.setPassword("root");
            ds.setInitialPoolSize(10);
            ds.setMinPoolSize(5);
            ds.setMaxPoolSize(20);*/
            
            //通过读取C3P0的xml配置文件创建数据源,C3P0的xml配置文件c3p0-config.xml必须放在src目录下
            //ds = new ComboPooledDataSource();//使用C3P0的默认配置来创建数据源
            ds = new ComboPooledDataSource("MySQL");//使用C3P0的命名配置来创建数据源
            
        }catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }
    
    /**
    * @Method: getConnection
    * @Description: 从数据源中获取数据库连接
    * @return Connection
    * @throws SQLException
    */ 
    public static Connection getConnection() throws SQLException{
        //从当前线程中获取Connection
        Connection conn = threadLocal.get();
        if(conn==null){
            //从数据源中获取数据库连接
            conn = getDataSource().getConnection();
            //将conn绑定到当前线程
            threadLocal.set(conn);
        }
        return conn;
    }
    
    /**
    * @Method: startTransaction
    * @Description: 开启事务
    *
    */ 
    public static void startTransaction(){
        try{
            Connection conn =  threadLocal.get();
            if(conn==null){
                conn = getConnection();
                 //把 conn绑定到当前线程上
                threadLocal.set(conn);
            }
            //开启事务
            conn.setAutoCommit(false);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
    * @Method: rollback
    * @Description:回滚事务
    *
    */ 
    public static void rollback(){
        try{
            //从当前线程中获取Connection
            Connection conn = threadLocal.get();
            if(conn!=null){
                //回滚事务
                conn.rollback();
            }
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
    * @Method: commit
    * @Description:提交事务
    */ 
    public static void commit(){
        try{
            //从当前线程中获取Connection
            Connection conn = threadLocal.get();
            if(conn!=null){
                //提交事务
                conn.commit();
            }
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
    * @Method: close
    * @Description:关闭数据库连接(注意,并不是真的关闭,而是把连接还给数据库连接池)
    *
    */ 
    public static void close(){
        try{
            //从当前线程中获取Connection
            Connection conn = threadLocal.get();
            if(conn!=null){
                conn.close();
                 //解除当前线程上绑定conn
                threadLocal.remove();
            }
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
    * @Method: getDataSource
    * @Description: 获取数据源
    * @return DataSource
    */ 
    public static DataSource getDataSource(){
        //从数据源中获取数据库连接
        return ds;
    }
}

 

 

对AccountDao进行改造,数据库连接对象不再需要service层传递过来,而是直接从JdbcUtils2提供的getConnection方法去获取,改造后的AccountDao如下:

 

package wkcto.com.dao;

import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import wkcto.com.domain.Account;
import wkcto.com.util.JdbcUtils;
import wkcto.com.util.JdbcUtils2;

/*
create table account(
    id int primary key auto_increment,
    name varchar(40),
    money float
)character set utf8 collate utf8_general_ci;

insert into account(name,money) values('A',1000);
insert into account(name,money) values('B',1000);
insert into account(name,money) values('C',1000);

*/

/**
* @ClassName: AccountDao
* @Description: 针对Account对象的CRUD
*
*/ 
public class AccountDao2 {

    public void update(Account account) throws SQLException{
        
        QueryRunner qr = new QueryRunner();
        String sql = "update account set name=?,money=? where id=?";
        Object params[] = {account.getName(),account.getMoney(),account.getId()};
        //JdbcUtils2.getConnection()获取当前线程中的Connection对象
        qr.update(JdbcUtils2.getConnection(),sql, params);
        
    }
    
    public Account find(int id) throws SQLException{
        QueryRunner qr = new QueryRunner();
        String sql = "select * from account where id=?";
        //JdbcUtils2.getConnection()获取当前线程中的Connection对象
        return (Account) qr.query(JdbcUtils2.getConnection(),sql, id, new BeanHandler(Account.class));
    }
}

 

 

对AccountService进行改造,service层不再需要传递数据库连接Connection给Dao层,改造后的AccountService如下:

 

package wkcto.com.service;

import java.sql.SQLException;
import wkcto.com.dao.AccountDao2;
import wkcto.com.domain.Account;
import wkcto.com.util.JdbcUtils2;

public class AccountService2 {
    
    /**
    * @Method: transfer
    * @Description:在业务层处理两个账户之间的转账问题
    * @param sourceid
    * @param tartgetid
    * @param money
    * @throws SQLException
    */ 
    public void transfer(int sourceid,int tartgetid,float money) throws SQLException{
        try{
            //开启事务,在业务层处理事务,保证dao层的多个操作在同一个事务中进行
            JdbcUtils2.startTransaction();
            AccountDao2 dao = new AccountDao2();
            
            Account source = dao.find(sourceid);
            Account target = dao.find(tartgetid);
            source.setMoney(source.getMoney()-money);
            target.setMoney(target.getMoney()+money);
            
            dao.update(source);
            //模拟程序出现异常让事务回滚
            int x = 1/0;
            dao.update(target);
            
            //SQL正常执行之后提交事务
            JdbcUtils2.commit();
        }catch (Exception e) {
            e.printStackTrace();
            //出现异常之后就回滚事务
            JdbcUtils2.rollback();
        }finally{
            //关闭数据库连接
            JdbcUtils2.close();
        }
    }
}

 

 

这样在service层对事务的处理看起来就更加优雅了。ThreadLocal类在开发中使用得是比较多的,程序运行中产生的数据要想在一个线程范围内共享,只需要把数据使用ThreadLocal进行存储即可

 

Apache_DBUtils框架教程学习笔记(五)_Filter处理事务

 

0人推荐
共同学习,写下你的评论
0条评论
HelloWorld
程序员HelloWorld

11篇文章贡献50393字

作者相关文章更多>

推荐相关文章更多>

MySQL开发规范

达芬奇09-04 11:38

必须掌握的30种SQL语句优化

不写代码你养我啊08-23 11:30

ES(Elasticsearch)支持PB级全文搜索引擎入门教程

HelloWorld09-29 17:28

Java面试中的hadoop面试题及答案整理

兔子06-15 17:15

数据库分类之速成篇

呵呵呀06-17 18:54

发评论

举报

0/150

取消