JDBC补充:DBUtils和ThreadLocal

来源:互联网 发布:新手开淘宝店教程 编辑:程序博客网 时间:2024/06/11 04:13

      • DBUtils
      • ThreadLocal
      • 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 0
原创粉丝点击