JDBC补充:DBUtils和ThreadLocal

DBUtils

  1. 导包
    commons-dbutils-xxx.jar包和commons-pool-xxx.jar包,xxx是版本号。
  2. 使用

    package com.jyh.dbutil;
    
    import java.sql.SQLException;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.ArrayHandler;
    import org.apache.commons.dbutils.handlers.ArrayListHandler;
    import org.apache.commons.dbutils.handlers.BeanHandler;
    import org.apache.commons.dbutils.handlers.BeanListHandler;
    import org.apache.commons.dbutils.handlers.ColumnListHandler;
    import org.apache.commons.dbutils.handlers.KeyedHandler;
    import org.apache.commons.dbutils.handlers.MapHandler;
    import org.apache.commons.dbutils.handlers.MapListHandler;
    import org.apache.commons.dbutils.handlers.ScalarHandler;
    import org.junit.Test;
    
    import com.jyh.domain.User;
    import com.jyh.jdbc.DBCPUtil;
    
    /**
     * dbutils框架的使用
     * @author OverrideRe
     *
     */
    public class DBUtilDemo {
    
        /**
         * 传入数据源代表不需要事务,不传代表需要事务
         *传入数据源说明是从数据源随机获取链接,不一定是同一个链接,所以没法进行事务操作
         *如果需要进行事务操作需要同一个链接
         */
        private QueryRunner qr = new QueryRunner(DBCPUtil.getDataSource());
    
        //没有返回值的增删改等
        @Test
        public void test1() throws SQLException{
            qr.update("insert into user(name,password,email,birthday) values(?,?,?,?)", "大狗","123","dagou@qq.com","1994-01-01");
        }
    
        //批处理
        @Test
        public void test2() throws SQLException{
            Object[][] params = new Object [10][];
            for(int i = 0;i < 10;i ++){
                params[i] = new Object[]{"a"+i,"123","a"+i+"@qq.com",new Date()};
            }
            qr.batch("insert into user(name,password,email,birthday) values(?,?,?,?)", params);
        }
    
        //ArrayHandler:有一条返回结果的查询语句,查询结果封装到一个Object[]中去
        @Test
        public void test3() throws SQLException{
            Object[] objs = qr.query("select * from user", new ArrayHandler());
            for(Object obj : objs)
                System.out.println(obj);
        }
    
        //ArrayListHandler:有多条返回结果的查询语句,查询结果先将每条封装到一个Object[]中去,再封装到list中去
        @Test
        public void test4() throws SQLException{
            List<Object[]> list = qr.query("select * from user", new ArrayListHandler());
            for (Object[] objects : list) {
                for (Object object : objects) {
                    System.out.print(object + "---");
                }
                System.out.println();
            }
        }
    
        //ColumnListHandler:封装某列的值,将某一列的值封装进list中去
        @Test
        public void test5() throws SQLException{
            //ColumnListHandler有两种构造方法,一种是传列的序号,一种是传列名
            List<Object> list = qr.query("select * from user", new ColumnListHandler("name"));
            for (Object object : list) {
                System.out.print(object + "---");
            }
            System.out.println();
        }
    
        /**KeyedHandler:以键值对形式封装数据,每条记录封装进键值对集合,value为各属性值,key为各属性值对应的属性名
         *然后再将每条记录封装进键值集合,key值自己定(一般为id),value值就是每条记录,也就是刚刚封装的键值对集合
         *意思就是双重map
         */
        @Test
        public void test6() throws SQLException{
            Map<Object, Map<String, Object>> keyMap = qr.query("select * from user", new KeyedHandler("id"));
            for (Map.Entry<Object, Map<String, Object>> enMap : keyMap.entrySet()) {
                System.out.print("key:" + enMap.getKey());
                for (Map.Entry<String, Object> en: enMap.getValue().entrySet()) {
                    System.out.print("---" + en.getKey() + ":" + en.getValue());
                }
                System.out.println();
            }
            System.out.println();
        }
    
        //MapHandler:取一条数据,将列名和所对应的值以键值对形式封装
        @Test
        public void test7() throws SQLException{
            Map<String, Object> keyMap = qr.query("select * from user", new MapHandler());
            for (Map.Entry<String, Object> en : keyMap.entrySet()) {
                System.out.println(en.getKey() + ":" + en.getValue());
            }
            System.out.println();
        }
    
        //MapListHandler:取多条数据,将列名和所对应的值以键值对形式封装进map集合,然后再将map集合装进list中去
        //与KeyedHandler不同的是,每一条数据是装在List中而不是Map键值对集合中
        @Test
        public void test8() throws SQLException{
            List<Map<String, Object>> keyList = qr.query("select * from user", new MapListHandler());
            for (Map<String, Object> enMap : keyList) {
                for (Map.Entry<String, Object> en: enMap.entrySet()) {
                    System.out.print("---" + en.getKey() + ":" + en.getValue());
                }
                System.out.println();
            }
            System.out.println();
        }
    
        //ScalarHandler:返回某行某列的一个数据,如查询总数coutn(*)
        @Test
        public void test9() throws SQLException{
            Object l= qr.query("select * from user", new ScalarHandler());
            System.out.println(l);
        }
    
        //BeanHandler:将一条数据封装进某个bean类中去
        @Test
        public void test10() throws SQLException{
            User l= qr.query("select * from user", new BeanHandler<User>(User.class));
            System.out.println(l.getName());
        }
    
        //BeanListHandler:获取多条数据,将每条数据封装进bean中然后再添加进集合中去
        @Test
        public void test11() throws SQLException{
            List<User> l= qr.query("select * from user", new BeanListHandler<User>(User.class));
            System.out.println(l.get(0).getName());
        }
    }
    

ThreadLocal

ThreadLocal为线程局部变量,看着名字就知道是保存在线程上的变量
1. 声明实例:ThreadLocal<T> tl = new ThreadLocal<T>();
T为要保存的数据类型。
2. 常用方法:set(T t),get(),remove()。
3. 作用:将数据保存在当前线程中,为线程私有,利用线程之间的互相隔离,保证
变量只能在当前线程中被改变。普通的变量就是保存在外面,任何线程都可以访问,所以每个线程访问的数据都可能是经过别的线程改变之后的数据。
4. 实现原理:查看源码。
为了文章的长度,把源码加上:
set(T value)方法:

 public void set(T value) {
      Thread t = Thread.currentThread();
      ThreadLocalMap map = getMap(t);
      if (map != null)
          map.set(this, value);
      else
          createMap(t, value);
  } 

set方法中调用的getMap(Thread t)方法

ThreadLocalMap getMap(Thread t) {
       return t.threadLocals;
   }

set方法中调用的creatMap(Thread t, T firstValue)方法:

 void createMap(Thread t, T firstValue) {
       t.threadLocals = new ThreadLocalMap(this, firstValue);
   }

Thread类中的threadLocals 参数:

/* ThreadLocal values pertaining to this thread. This map is maintained
    * by the ThreadLocal class. */
   ThreadLocal.ThreadLocalMap threadLocals = null;

查看以上代码可知,set方法是将要保存的数据作为value,当前ThreadLocal实例作为key值保存在当前线程中的一个map中,这个map类是ThreadLocal.ThreadLocalMap,是ThreadLocal的内部类。
做个小实验:

ThreadLocal<Integer> iLocal = new ThreadLocal<Integer>();
iLocal.set(1);
iLocal.set(2);

ThreadLocal<String> sLocal = new ThreadLocal<String>();
sLocal.set("a");
sLocal.set("b");

System.out.println(iLocal.get());
System.out.println(sLocal.get());

输出结果是2,b。也可以看看这篇博客:
http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/

DBUtils与ThreadLocal合并运用

刚刚说了要使用事务就必须保证链接是同一个,所以可以利用ThreadLocal来实现。
一个转账小例子:

开始转账:

package com.jyh.test;

import com.jyh.service.AccountService;
import com.jyh.service.impl.AccountServiceImpl2;
import com.jyh.utils.ServiceFactory;

public class test {

    public static void main(String[] args) {
        AccountService as2 = null;
        try {
            //从工厂里面获取实例,第一个参数为true说明是需要事务的
            as2 = ServiceFactory.getService(true, AccountServiceImpl2.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //ccc转账100给aaa
        as2.transfer("ccc", "aaa", 100);

    }
}

工厂类:

package com.jyh.utils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 工厂模式来决定service如何实现
 * @author OverrideRe
 *
 */

public class ServiceFactory {

    public static <T> T getService(boolean isProxy,Class<T> clazz) throws Exception{
        final T t = clazz.newInstance();
        if(isProxy){//如果是true说明需要事务,那么对每个方法的调用都要用事务包起来
            //代理模式监视每个方法
            @SuppressWarnings("unchecked")
            T tt = (T)Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
                    System.out.println("工厂模式里的代理模式");
                    try {
                        //方法执行前开启事务
                        ThreadLocalUtil.startTransaction();
                        //方法执行
                        method.invoke(t, args);
                        //方法执行之后提交事务
                        ThreadLocalUtil.commit();
                    } catch (Exception e) {
                        //回滚事务
                        ThreadLocalUtil.rollBack();
                        throw new RuntimeException(e);
                    }finally{
                        //释放资源
                        ThreadLocalUtil.release();
                    }
                    return null;
                }
            });
            return tt;
        }
        return t;
    }
}

获取链接的ThreadLocalUtil工具类:

package com.jyh.utils;

import java.sql.Connection;
import java.sql.SQLException;


@SuppressWarnings("rawtypes")
public class ThreadLocalUtil extends ThreadLocal{
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    public static Connection getConnection(){
        Connection conn = tl.get();//获取线程中保存的链接
        if(conn == null){//如果没有则创建一个链接set进去
            conn = DBCPUtil.getConnection();
            tl.set(conn);
        }
        return conn;
    }

    //开启事务
    public static void startTransaction(){
        Connection conn = getConnection();
        try {
            conn.setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //提交事务
    public static void commit(){
        Connection conn = getConnection();
        try {
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //回滚事务
    public static void rollBack(){
        Connection conn = getConnection();
        try {
            conn.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //释放资源
    public static void release(){
        Connection conn = getConnection();
        try {
            conn.close();
            tl.remove();//移除
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

业务逻辑类:

package com.jyh.service.impl;

import com.jyh.dao.AccountDao;
import com.jyh.dao.impl.AccountDaoImpl;
import com.jyh.domain.Account;
import com.jyh.service.AccountService;

public class AccountServiceImpl2 implements AccountService {

    private AccountDao ad = new AccountDaoImpl();

    //实现用户转账
    public void transfer(String sourceName, String targetName, Integer money) {
        Account a = ad.query(sourceName);//获取a用户
        Account b= ad.query(targetName);//获取b用户

        a.setSalary(a.getSalary() - money);//a用户减少这么多money
        b.setSalary(b.getSalary() + money);//b用户增加这么多money

        //更新数据库
        ad.update(a);
        ad.update(b);
    }

}

dao实现类:

package com.jyh.dao.impl;

import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import com.jyh.dao.AccountDao;
import com.jyh.domain.Account;
import com.jyh.utils.ThreadLocalUtil;

public class AccountDaoImpl implements AccountDao {

    //这里没有传递数据源
    private QueryRunner qr = new QueryRunner();

    public AccountDaoImpl(){
    }

    public void update(Account account) {
        try {
            //这里传递了一个链接,为了实现事务
            qr.update(ThreadLocalUtil.getConnection(),"update account set salary = ? where id = ?", account.getSalary(),account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public Account query(String name) {
        try {
            return qr.query(ThreadLocalUtil.getConnection(), "select * from account where name = ?", new BeanHandler<Account>(Account.class),name);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本课程详细讲解了以下内容:    1.jsp环境搭建及入门、虚拟路径和虚拟主机、JSP执行流程    2.使用Eclipse快速开发JSP、编码问题、JSP页面元素以及request对象、使用request对象实现注册示例    3.请求方式的编码问题、response、请求转发和重定向、cookie、session执行机制、session共享问题     4.session与cookie问题及application、cookie补充说明及四种范围对象作用域     5.JDBC原理及使用Statement访问数据库、使用JDBC切换数据库以及PreparedStatement的使用、Statement与PreparedStatement的区别     6.JDBC调用存储过程和存储函数、JDBC处理大文本CLOB及二进制BLOB类型数据     7.JSP访问数据库、JavaBean(封装数据和封装业务逻辑)     8.MVC模式与Servlet执行流程、Servlet25与Servlet30的使用、ServletAPI详解与源码分析     9.MVC案例、三层架构详解、乱码问题以及三层代码流程解析、完善Service和Dao、完善View、优化用户体验、优化三层(加入接口和DBUtil)    1 0.Web调试及bug修复、分页SQL(Oracle、MySQL、SQLSERVER)     11.分页业务逻辑层和数据访问层Service、Dao、分页表示层Jsp、Servlet     12.文件上传及注意问题、控制文件上传类型和大小、下载、各浏览器下载乱码问题     13.EL表达式语法、点操作符和中括号操作符、EL运算、隐式对象、JSTL基础及set、out、remove     14.过滤器、过滤器通配符、过滤器链、监听器     15.session绑定解绑、钝化活化     16.以及Ajax的各种应用     17. Idea环境下的Java Web开发
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值