• 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进行存储即可

  • 相关阅读:
    angularjs 默认选中ng-repeat的一个
    AngularJs中directive的延迟加载
    AngularJS的angucomplete-alt
    Bootstrap Multiselect中文
    input[file]标签的accept=”image/*”属性响应很慢的解决办法
    全局安装cnpm
    实现鼠标悬浮内容自动撑开的过渡动画
    table表格 css样式
    IntelliJ idea 的破解
    浏览器使用谷歌搜索
  • 原文地址:https://www.cnblogs.com/zydev/p/6122804.html
Copyright © 2020-2023  润新知