JavaWeb学习总结(十四)--Apache的DBUtils

一、commons-dbutils简介

commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。因此dbutils成为很多不喜欢hibernate的公司的首选。

  commons-dbutilsAPI介绍:

  • org.apache.commons.dbutils.QueryRunner
  • org.apache.commons.dbutils.ResultSetHandler

  工具类

  • org.apache.commons.dbutils.DbUtils

二、QueryRunner类使用讲解

  该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
  QueryRunner类提供了两个构造方法:

  • 默认的构造方法
  • 需要一个 javax.sql.DataSource 来作参数的构造方法。

2.1、QueryRunner类的主要方法

  public Object query(Connection conn, String sql, Object[] params, ResultSetHandler rsh) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
  public Object query(String sql, Object[] params, ResultSetHandler rsh) throws SQLException: 几乎与第一种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造方法的数据源(DataSource) 或使用的setDataSource 方法中重新获得 Connection。
  public Object query(Connection conn, String sql, ResultSetHandler rsh) throws SQLException : 执行一个不需要置换参数的查询操作。
  public int update(Connection conn, String sql, Object[] params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
  public int update(Connection conn, String sql) throws SQLException:用来执行一个不需要置换参数的更新操作。

2.2、使用QueryRunner类实现CRUD

1.建立测试表:

create table users(
       id int primary key auto_increment, 
       name varchar(40),
       password varchar(40), 
       email varchar(60), 
       birthday date 
      );

2.建立JavaBean

 1 package cn.zy.dbutils;
 2 
 3 import java.util.Date;
 4 
 5 public class User {
 6     private int id;
 7     private String name;
 8     private String password;
 9     private String email;
10     private Date birthday;
11     public String getName() {
12         return name;
13     }
14     public void setName(String name) {
15         this.name = name;
16     }
17     public String getPassword() {
18         return password;
19     }
20     public void setPassword(String password) {
21         this.password = password;
22     }
23     @Override
24     public String toString() {
25         return "User [birthday=" + birthday + ", email=" + email + ", id=" + id
26                 + ", name=" + name + ", password=" + password + "]";
27     }
28     public String getEmail() {
29         return email;
30     }
31     public void setEmail(String email) {
32         this.email = email;
33     }
34     public Date getBirthday() {
35         return birthday;
36     }
37     public void setBirthday(Date birthday) {
38         this.birthday = birthday;
39     }
40     public int getId() {
41         return id;
42     }
43     public void setId(int id) {
44         this.id = id;
45     }
46 }

3.建立测试类

 1 package cn.zy.test;
 2 
 3 import java.sql.SQLException;
 4 import java.util.Date;
 5 import java.util.List;
 6 
 7 import org.apache.commons.dbutils.QueryRunner;
 8 import org.apache.commons.dbutils.handlers.BeanHandler;
 9 import org.apache.commons.dbutils.handlers.BeanListHandler;
10 import org.junit.Test;
11 
12 import cn.zy.dbutils.User;
13 import cn.zy.utils.JdbcUtils_C3P0;
14 
15 public class QueryRunnerCRUDTest {
16     @Test
17     public void add() throws SQLException{
18         //将数据源传递给QueryRunner,QueryRunner内部通过数据源获取数据库连接
19         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
20         String sql = "insert into users(name,password,email,birthday) values(?,?,?,?)";
21         Object param[] = {"张三","123","zhansan@qq.com",new Date(0)};
22         qr.update(sql, param);    
23     }
24     
25     @Test
26     public void delete() throws SQLException{
27         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
28         String  sql = "delete from users where id=?";
29         qr.update(sql,1);
30     }
31     
32     @Test
33     public void update() throws SQLException{
34         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
35         String sql = "update users set name=? where id=?";
36         Object param[] = {"王五",3};
37         qr.update(sql, param);
38     }
39     
40     @Test
41     public void find() throws SQLException{
42         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
43         String sql = "select * from users where id=?";
44         Object params[] = {2};
45         User user = (User) qr.query(sql, params, new BeanHandler(User.class));
46         System.out.println(user);
47     }
48     
49     @Test
50     public void getAll() throws SQLException{
51         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
52         String sql = "select * from users";
53         List list = (List) qr.query(sql, new BeanListHandler(User.class));
54         System.out.println(list.size());
55     }
56     
57     @Test
58     public void testBatch() throws SQLException{
59         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
60         String sql = "insert into users(name,password,email,birthday) values(?,?,?,?)";
61         Object params[][] = new Object[10][];
62         for (int i=0;i<10;i++){
63             params[i] = new Object[] { "aa" + i, "123", "aa@sina.com",
64                     new Date() };
65             }
66         qr.batch(sql, params);
67     }
68 }

三、ResultSetHandler接口使用讲解

  该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式。
  ResultSetHandler接口提供了一个单独的方法:Object handle (java.sql.ResultSet .rs)

3.1、ResultSetHandler接口的实现类

  • ArrayHandler:把结果集中的第一行数据转成对象数组。
  • ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
  • BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
  • BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
  • ColumnListHandler:将结果集中某一列的数据存放到List中。
  • KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
  • MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
  • MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List

Scalar处理器

3.2、测试dbutils各种类型的处理器

 1 package cn.zy.test;
 2 
 3 import java.sql.SQLException;
 4 import java.util.Arrays;
 5 import java.util.List;
 6 import java.util.Map;
 7 
 8 import org.apache.commons.dbutils.QueryRunner;
 9 import org.apache.commons.dbutils.handlers.ArrayHandler;
10 import org.apache.commons.dbutils.handlers.ArrayListHandler;
11 import org.apache.commons.dbutils.handlers.ColumnListHandler;
12 import org.apache.commons.dbutils.handlers.KeyedHandler;
13 import org.apache.commons.dbutils.handlers.MapHandler;
14 import org.apache.commons.dbutils.handlers.MapListHandler;
15 import org.apache.commons.dbutils.handlers.ScalarHandler;
16 import org.junit.Test;
17 
18 import cn.zy.utils.JdbcUtils_C3P0;
19 
20 public class ResultSetHandlerTest {
21     @Test
22     public void testArrayHandler() throws SQLException{
23         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
24         String sql = "select *from users";
25         Object result[] = (Object[]) qr.query(sql, new ArrayHandler());
26          System.out.println(Arrays.asList(result));
27     }
28     
29     @Test
30      public void testArrayListHandler() throws SQLException{
31          QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
32          String sql = "select * from users";
33          List<Object[]> list = (List) qr.query(sql, new ArrayListHandler());
34          for(Object[] o : list){
35              System.out.println(Arrays.asList(o));
36          }
37      }
38     @Test
39     public void testColumnListHandler() throws SQLException{
40         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
41         String sql = "select * from users";
42         List list = (List) qr.query(sql, new ColumnListHandler("id"));
43          System.out.println(list);
44     }
45     
46     @Test
47     public void testKeyedHandler() throws Exception{
48         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
49         String sql = "select * from users";
50         Map<Integer,Map> map = (Map) qr.query(sql, new KeyedHandler("id"));
51          for(Map.Entry<Integer, Map> me : map.entrySet()){
52                          int  id = me.getKey();
53                          Map<String,Object> innermap = me.getValue();
54                          for(Map.Entry<String, Object> innerme : innermap.entrySet()){
55                              String columnName = innerme.getKey();
56                              Object value = innerme.getValue();
57                               System.out.println(columnName + "=" + value);
58                          }
59                          System.out.println("----------------");
60                      }
61     }
62     
63     @Test
64      public void testMapHandler() throws SQLException{
65          QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
66          String sql = "select * from users";
67          Map<String,Object> map = (Map) qr.query(sql, new MapHandler());
68          for(Map.Entry<String, Object> me : map.entrySet()){
69              System.out.println(me.getKey() + "=" + me.getValue());
70          }         
71     }
72     
73     @Test
74     public void testMapListHandler() throws SQLException{
75         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
76         String sql = "select * from users";
77         List<Map> list = (List) qr.query(sql, new MapListHandler());
78         for(Map<String,Object> map :list){
79             for(Map.Entry<String, Object> me : map.entrySet())
80                 System.out.println(me.getKey() + "=" + me.getValue());
81         }
82     }
83     
84     @Test
85     public void testScalarHandler() throws SQLException{
86         QueryRunner qr = new QueryRunner(JdbcUtils_C3P0.getDataSource());
87          String sql = "select count(*) from users"; 
88          int count = ((Long)qr.query(sql, new ScalarHandler(1))).intValue();
89          System.out.println(count);    
90     }
91 
92 }

 依次返回:

====================================

====================================

====================================

+++++++++++++++++++++++++++++++++++

===================================

++++++++++++++++++++++++++++++++++

三、DbUtils类使用讲解

  DbUtils :提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。主要方法如下:
  public static void close(…) throws java.sql.SQLException: DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
  public static void closeQuietly(…): 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLEeception。
  public static void commitAndCloseQuietly(Connection conn): 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。 
  public static boolean loadDriver(java.lang.String driverClassName):这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。

四、JDBC开发中的事务处理

4.1、在业务层(BusinessService)处理事务

建立表:

CREATE TABLE account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(40),
    money FLOAT
);

INSERT INTO account(NAME,money) VALUES('A',1000);
INSERT INTO account(NAME,money) VALUES('B',1000);
INSERT INTO account(NAME,money) VALUES('C',1000);

domain层

 1 package cn.zy.domain;
 2 
 3 import java.util.Date;
 4 
 5 public class Account {
 6     private int id;
 7     private String name;
 8     private Float money;
 9     public int getId() {
10         return id;
11     }
12     public void setId(int id) {
13         this.id = id;
14     }
15     public String getName() {
16         return name;
17     }
18     @Override
19     public String toString() {
20         return "Account [id=" + id + ", money=" + money + ", name=" + name
21                 + "]";
22     }
23     public void setName(String name) {
24         this.name = name;
25     }
26     public Float getMoney() {
27         return money;
28     }
29     public void setMoney(Float money) {
30         this.money = money;
31     }
32     public Account(int id, String name, Float money) {
33         super();
34         this.id = id;
35         this.name = name;
36         this.money = money;
37     }
38     public Account() {
39         super();
40     }
41 
42 }

写一个Dao类进行进行CURD操作

 1 package cn.zy.dao;
 2 
 3 import java.sql.Connection;
 4 import java.sql.SQLException;
 5 import org.apache.commons.dbutils.QueryRunner;
 6 import org.apache.commons.dbutils.handlers.BeanHandler;
 7 import cn.zy.domain.Account;
 8 
 9 public class AccountDao {
10     
11     //接受service层传递过来的Connection对象
12     private Connection conn = null;
13     
14     public AccountDao(Connection conn){
15         this.conn = conn;
16     }
17     public AccountDao(){
18         
19     }
20     
21     public void update(Account account) throws SQLException{
22          QueryRunner qr = new QueryRunner();
23          String sql = "update account set name=?,money=? where id=?";
24          Object params[] = {account.getName(),account.getMoney(),account.getId()};
25 
26         //使用service层传递过来的Connection对象操作数据库
27          qr.update(conn, sql, params);
28          System.out.println("更新成功");
29     }
30     
31     public Account find(int id) throws SQLException{
32         QueryRunner qr = new QueryRunner();
33         String sql = "select * from account where id=?";
34 //使用service层传递过来的Connection对象操作数据库
35 return (Account) qr.query(conn,sql, id, new BeanHandler(Account.class));
36 
37 
38     }
39 
40 }

在业务层实现转账

package cn.zy.service;

import java.sql.Connection;
import java.sql.SQLException;
import cn.zy.dao.AccountDao;
import zn.zy.domain.Account;
import zn.zy.util.JdbcUtils;

/**
* @ClassName: AccountService
* @Description: 业务逻辑处理层
*/ 
public class AccountService {
    
    /**
    * @Method: transfer
    * @Description:这个方法是用来处理两个用户之间的转账业务
    * @param sourceid
    * @param tartgetid
    * @param money
    * @throws SQLException
    */ 
    public void transfer(int sourceid,int tartgetid,float money) throws SQLException{
        Connection conn = null;
        try{
            //获取数据库连接
            conn = JdbcUtils.getConnection();
            //开启事务
            conn.setAutoCommit(false);
            //将获取到的Connection传递给AccountDao,保证dao层使用的是同一个Connection对象操作数据库
            AccountDao dao = new AccountDao(conn);
            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);
            //提交事务
            conn.commit();
        }catch (Exception e) {
            e.printStackTrace();
            //出现异常之后就回滚事务
            conn.rollback();
        }finally{
            conn.close();
        }
    }
}

测试:

 1 package cn.zy.test;
 2 
 3 import java.sql.SQLException;
 4 import org.junit.Test;
 5 import cn.zy.service.AccountService;
 6 
 7 public class AccountServiceTest {
 8     @Test
 9     public void fun() throws SQLException{
10         AccountService service = new AccountService();
11         service.transfer(1, 2, 100f);
12     }
13 }

4.2、使用ThreadLocal进行更加优雅的事务处理

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

ThreadLocal类的使用范例如下:

 1 package me.gacl.test;
 2 
 3 public class ThreadLocalTest {
 4 
 5     public static void main(String[] args) {
 6         //得到程序运行时的当前线程
 7         Thread currentThread = Thread.currentThread();
 8         System.out.println(currentThread);
 9         //ThreadLocal一个容器,向这个容器存储的对象,在当前线程范围内都可以取得出来
10         ThreadLocal<String> t = new ThreadLocal<String>();
11         //把某个对象绑定到当前线程上 对象以键值对的形式存储到一个Map集合中,对象的的key是当前的线程,如: map(currentThread,"aaa")
12         t.set("aaa");
13         //获取绑定到当前线程中的对象
14         String value = t.get();
15         //输出value的值是aaa
16         System.out.println(value);
17     }
18 }

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

  1 package cn.zy.utils;
  2 
  3 import java.sql.Connection;
  4 import java.sql.SQLException;
  5 import javax.sql.DataSource;
  6 import com.mchange.v2.c3p0.ComboPooledDataSource;
  7 
  8 public class JdbcUtils_C3P0_v2 {
  9      private static ComboPooledDataSource ds = null;
 10      //使用ThreadLoacal存储当前线程中的Connection对象
 11      private static ThreadLocal<Connection> threadLocal =new ThreadLocal<Connection>();
 12      
 13      //在静态块中创建连接池
 14      static{
 15          try {
 16             //使用C3P0的命名配置来创建数据源
 17              ds = new ComboPooledDataSource("MySQL");
 18         } catch (Exception e) {
 19             throw new ExceptionInInitializerError(e);
 20         }
 21      }
 22      
 23      /*
 24       * 从数据源中获取数据库连接
 25       */
 26      public static Connection getConnection() throws SQLException{
 27          //从当前线程中获取数据库连接
 28          Connection conn = threadLocal.get();
 29          if(conn==null){
 30              //从数据源中获取连接
 31              conn = ds.getConnection();
 32              //将conn绑定到当前线程
 33              threadLocal.set(conn);
 34          }
 35          return conn;
 36      }
 37      
 38      /*
 39       * 开启事务
 40       */
 41      public static void startTransaction(){
 42          try {
 43             Connection conn = threadLocal.get();
 44             if(conn==null){
 45                 conn = getConnection();
 46                 //把conn绑定到当前线程上
 47                 threadLocal.set(conn);
 48             }
 49             //开启事务
 50             conn.setAutoCommit(false);
 51         } catch (Exception e) {
 52             throw new RuntimeException(e);
 53         }
 54      }
 55      
 56      /*
 57       * 回滚事务
 58       */
 59      public static void rollback(){
 60          try {
 61                 Connection conn = threadLocal.get();
 62                 if(conn!=null){
 63                     //回滚事务
 64                     conn.rollback();
 65                 }
 66             } catch (Exception e) {
 67                 throw new RuntimeException(e);
 68             }
 69      }
 70      
 71      /*
 72       * 提交事务
 73       */
 74      public static void commit(){
 75          try {
 76             Connection conn = threadLocal.get();
 77             if(conn!=null){
 78                 //提交事务
 79                 conn.commit();
 80             }
 81         } catch (Exception e) {
 82             throw new RuntimeException(e);
 83         }
 84      }
 85      
 86      /*
 87       * 关闭数据库连接
 88       */
 89      public static void close(){
 90          try {
 91             //从当前线程获取连接
 92              Connection conn = threadLocal.get();
 93              if(conn!=null){
 94                  conn.close();
 95                  //从当前线程接解除定
 96                  threadLocal.remove();
 97              }
 98         } catch (Exception e) {
 99             throw new RuntimeException(e);
100         }
101      }
102      
103      /*
104       * 获取数据源
105       */
106      public static DataSource getDataSource(){
107          return ds;
108      }
109 }

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

 1 package cn.zy.dao;
 2 
 3 import java.sql.Connection;
 4 import java.sql.SQLException;
 5 import org.apache.commons.dbutils.QueryRunner;
 6 import org.apache.commons.dbutils.handlers.BeanHandler;
 7 import cn.zy.domain.Account;
 8 import cn.zy.utils.JdbcUtils_C3P0_v2;
 9 
10 public class AccountDao {
11     
12     public void update(Account account) throws SQLException{
13          QueryRunner qr = new QueryRunner();
14          String sql = "update account set name=?,money=? where id=?";
15          Object params[] = {account.getName(),account.getMoney(),account.getId()};
16         //JdbcUtils2.getConnection()获取当前线程中的Connection对象
17          qr.update(JdbcUtils_C3P0_v2.getConnection(), sql, params);
18     }
19     
20     public Account find(int id) throws SQLException{
21         QueryRunner qr = new QueryRunner();
22         String sql = "select * from account where id=?";
23         //JdbcUtils2.getConnection()获取当前线程中的Connection对象
24         return (Account) qr.query(JdbcUtils_C3P0_v2.getConnection(),sql, id, new BeanHandler(Account.class));
25     }
26 
27 }

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

 1 package cn.zy.service;
 2 
 3 import java.sql.Connection;
 4 import java.sql.SQLException;
 5 import cn.zy.dao.AccountDao;
 6 import cn.zy.domain.Account;
 7 import cn.zy.utils.JdbcUtils_C3P0;
 8 import cn.zy.utils.JdbcUtils_C3P0_v2;
 9 
10 public class AccountService {
11     public void transfer(int sourceid,int targetid,float money) throws SQLException{
12         Connection conn = null;
13         try {
14             //开启事务
15             JdbcUtils_C3P0_v2.startTransaction();
16             AccountDao dao = new AccountDao();
17             
18             Account source = dao.find(sourceid);
19             Account target = dao.find(targetid);
20             
21             //转账
22             source.setMoney(source.getMoney()-money);
23             target.setMoney(target.getMoney()+money);
24             dao.update(source);
25             dao.update(target);
26             //提交事务
27             JdbcUtils_C3P0_v2.commit();
28         } catch (Exception e) {
29             e.printStackTrace();
30             //出现异常之后就回滚事务
31             JdbcUtils_C3P0_v2.rollback();
32         }finally{
33             //关闭数据库连接
34             JdbcUtils_C3P0_v2.close();
35         }
36     }
37     
38 }

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

原文地址:https://www.cnblogs.com/zydev/p/6122804.html