您的位置:首页 > 产品设计 > UI/UE

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

2016-09-24 17:58 399 查看
1、DBUtils也是Apache开发的。它的作用是操作数据库的,相比之前的那些有什么优势呢?

它的读操作可以把结果直接转化成Array、List和Set等集合。

它的写操作非常简单,只需要写sql语句即可。

它当然可以与数据源的操作结合起来,使用连接池等技术。

所以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处理事务的案例
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  DBUtils