【JavaWeb-11】DBUtils、QueryRunner的query/update/batch、ResultSetHandler的9个处理器、ThreadLocal管理conn进行事务处理的案例

来源:互联网 发布:怪物猎人捏脸数据女 编辑:程序博客网 时间:2024/06/05 05:11

1、DBUtils也是Apache开发的。它的作用是操作数据库的,相比之前的那些有什么优势呢?

  1. 它的读操作可以把结果直接转化成Array、List和Set等集合。
  2. 它的写操作非常简单,只需要写sql语句即可。
  3. 它当然可以与数据源的操作结合起来,使用连接池等技术。

所以DBUtils的核心还是简化操作数据库的代码。

2、DBUtils的3个核心对象。QueryRunner类(里面有query查询、update增删改和batch批处理)、ResultSetHandler接口(用于select后如何封装数据的)、DBUtils类(定义了关闭资源和事务处理的方法)。

3、准备工作的第一步肯定是导入jar包。我们还需要mysql连接的jar包,还需要c3p0的jar包。
这里写图片描述

4、我们一个个说说上面的文件和内容。我们先成最高层的应用说起,看DBUtilsServlet这个类,主要内容就下面的一段代码。这里我们先实例化了一个QueryRunner类,这个类需要的是一个数据源。然后我们用这个实例化出来的qr来调用个查询方法,并且把查询结果封装到BeanListHandler里面并复制给一个变量list,最后把这个list遍历出来。

    public void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        QueryRunner qr=new QueryRunner(C3P0Util.getDs());        try {            List<User> list=qr.query("select * from fuser", new BeanListHandler<User>(User.class));            for(User u : list){                System.out.println(u);            }        } catch (SQLException e) {            e.printStackTrace();        }    }

——这个数据源就是我们利用我们上一个课程里面的C3P0方法得来的。

——在这里我们注意到,有两个地方需要扩展的。一个就是我们例子里面使用的是QueryRunner的query方法,还有另外两个需要学习。还有一个扩展的地方就是返回的结果封装,我们只用了一个BeanListHandler,还有好几个封装类需要我们去学习。

5、我们先对上面的查询做一个扩展,就是加不确定的参数,加一个问号,就在后面加1个参数。

List<User> list=qr.query("select * from fuser where username=?", new BeanListHandler<User>(User.class),"eric");

——同理update函数也是,只是update函数有一个conn用于事务处理。

qr.update("insert into fuser(username,pwd,email) values(?,?,?)", "wang","1234","wang@163.com");
qr.update("update fuser set username=?,pwd=? where email=?", "li","123","wang@163.com");
qr.update("delete from fuser where email=?", "wang@163.com");

——batch是批处理。所以需要的参数是一个数组,这个数组第一个参数代表执行的次数,比如下面的5次,第二个参数代表需要的参数个数,下面是3个。所以就需要创建一个二维数组,把这个二维数组赋值过去。

Object[][] params=new Object[5][];for(int i=0;i<params.length;i++){    params[i]=new Object[]{"tom"+i,"000"+i,i+"@163.com"};}qr.batch("insert into fuser(username,pwd,email) values(?,?,?)", params);

6、我们接下来要扩展的是几个结果处理器。

——ArrayHandler,适合取1条记录,把这条记录的每列都封装到一维数组中。如果我们select语句是查询多条语句的话,它也只取第1条记录放到这个数组中。

Object[] ob=qr.query("select * from fuser where id=?", new ArrayHandler(),1);for(Object o : ob){    System.out.println(o);}

——ArrayListHandler。就是把上面的数组封装到了List中。

List<Object[]> list=qr.query("select * from fuser", new ArrayListHandler());for(Object[] os : list){    for(Object o : os){        System.out.println(o);    }}

——ColumnListHandler,取某一列的值,每一列是一个Object并且把这些Object封装到List里。我好奇的是为什么不用Array,毕竟同一列的值类型至少都是一样的?反正不管了,先记住再说,这里相当于取1条记录然后封装到Array里面的ArrayHandler。里面参数2表示取查询结果里的第二列值。

List<Object> list=qr.query("select username,pwd from fuser", new ColumnListHandler(2));        for(Object o : list){            System.out.println(o);        }

——KeyedHandler适合查询多条记录,最终是一个Map,但是里面还有一个Map。它是先把每条记录的字段名和值封装成小Map,然后根据你规定的大Map的key值,再把每一个小Map当做value值封装到大Map里面去。下面我们给的数字是1,就是制定第一列id未大Map的key。

Map<Object,Map<String,Object>> map=qr.query("select * from fuser", new KeyedHandler(1));for(Map.Entry<Object, Map<String,Object>> mm : map.entrySet()){    System.out.println("大Map的key是:"+mm.getKey());    for(Map.Entry<String, Object> m : mm.getValue().entrySet()){        System.out.println(m.getKey()+":"+m.getValue());    }    System.out.println("--------------------");}

——MapListHandler。就是把上面那个结果集封装到List里面。

List<Map<String,Object>> list=qr.query("select * from fuser", new MapListHandler());for(Map<String,Object> m : list){    for(Map.Entry<String, Object> mm : m.entrySet()){        System.out.println(mm.getKey()+":"+mm.getValue());    }    System.out.println("----------");}

——ScalarHandler去某列某一个值。一般我们用来和聚合函数进行配合,后面的参数表示取第几列。如果我们只给了第几列,但是前面查询的不是聚合函数结果,那么它只取该列的第1个值。需要注意的是如果与count(*)配合的话Object的类型是long,其他的是String类型。

Object o=qr.query("select count(*) from fuser", new ScalarHandler(1));Object o=qr.query("select * from fuser", new ScalarHandler(2));System.out.println(o);

——BeanHandler取1条记录封装成类。如果查询了多条记录,那么只取第1条。

User u=qr.query("select * from fuser where id=?", new BeanHandler<User>(User.class),2);System.out.println(u);

——BeanListHandler上面说过,就是把类再封装到List里面。

7、我们现在再回顾一下如何结合事务来使用我们的DBUtils。结合事务的原理就是给一个Connection,但是需要保证是同一个Connection,这个时候就需要用ThreadLocal线程的知识。

——我们先来看一下目录结构和最重要的线程管理类。就是运用了ThreadLocal来管理我们的Connection。以后处理事务的时候需要的Connection都是从ThreadLocal里面拿,这样能保证是同一个Connection。这个ThreadLocalManager类相当于在C3P0Util上再封装了一遍。
这里写图片描述

——从最高层的应用层开始,也就是只需要一个服务类,这个服务类提供了一个转账的方法,方法提供3个参数:

TransferServiceImpl tsi=new TransferServiceImpl();try {    tsi.transfer("andy", "eric", 100);} catch (SQLException e) {    e.printStackTrace();}

——看看这个服务类内容是什么?这个服务类的主要目的是更新账户,但是更新的是做过修改的账户。我们直接传递的是账户类。所以需要先获取到涉及双方的账户,然后修改账户里面的金额,最后在更新这个账户。(这个类遵循惯例是继承自一个接口)。

public class TransferServiceImpl implements TransferService {    public void transfer(String fromName, String toName, int money) throws SQLException {        AccountDaoImpl accountDao=new AccountDaoImpl();        try {            ThreadLocalManager.startTransaction();            // 获取账户            Account fromAccount=accountDao.findAccountByName(fromName);            Account toAccount=accountDao.findAccountByName(toName);            // 计算金额            fromAccount.setMoney(fromAccount.getMoney()-money);            toAccount.setMoney(toAccount.getMoney()+money);            // 更新账户            accountDao.update(fromAccount);            accountDao.update(toAccount);            ThreadLocalManager.commit();        } catch (Exception e) {            ThreadLocalManager.rollback();            e.printStackTrace();        }finally{            ThreadLocalManager.close();        }    }}

——上面用到的AccountDao接口和它的实现类AccountDaoImpl,我们看实现类。实现类里面我们在使用QueryRunner的query和update方法时增加了一个Connection参数,这个Connection参数就是从ThreadLocalManager里面获取的,而不是直接从C3P0Util获取的。

    public void update(Account account) throws SQLException {        QueryRunner qr=new QueryRunner(C3P0Util.getDs());        qr.update(ThreadLocalManager.getConnection(),"update account set money=? where name=?",account.getMoney(),account.getName());    }    public Account findAccountByName(String name) throws SQLException {        QueryRunner qr=new QueryRunner(C3P0Util.getDs());        return qr.query(ThreadLocalManager.getConnection(),"select * from account where name=?", new BeanHandler<Account>(Account.class),name);    }

源代码:JavaEE DBUtil结合ThreadLocal处理事务的案例

1 0