天天看點

Commons-dbutils架構

文章目錄

  • ​​1. Commons-dbutils簡介​​
  • ​​2.QueryRunner類使用講解​​
  • ​​2.1.QueryRunner類的主要方法​​
  • ​​2.2.使用QueryRunner類實作CRUD​​
  • ​​2.2.1.JdbcUtils​​
  • ​​2.2.2.RunnerCRUDTest​​
  • ​​3.DButis整合Druid資料庫連接配接池​​
  • ​​4.改造QueryRunnerCRUDTest​​
  • ​​5.ResultSetHandler接口使用講解​​
  • ​​5.1.ResultSetHandler接口的實作類​​
  • ​​5.2.測試dbutils各種類型的處理器​​
  • ​​5.5.事務處理​​
  • ​​5.6.自定義處理程式​​

1. Commons-dbutils簡介

​commons-dbutils​

​​ 是 ​

​Apache​

​​ 組織提供的一個開源 ​

​JDBC​

​​工具類庫,它是對​

​JDBC​

​​的簡單封裝,學習成本極低,并且使用dbutils能極大簡化​

​jdbc​

​編碼的工作量,同時也不會影響程式的性能。

API介紹

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

工具類

org.apache.commons.dbutils.DbUtils      

2.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

2.2.1.JdbcUtils

jdbc.properties

# key=value
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/singerdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
jdbc.username=root
jdbc.password=123456      

JdbcUtils

package com.bruce.utils;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {

    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;

    static {
        try {
            //讀取db.properties檔案中的資料庫連接配接資訊
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            Properties prop = new Properties();
            prop.load(in);
            //擷取資料庫連接配接驅動
            driver = prop.getProperty("jdbc.driverClassName");
            //擷取資料庫連接配接URL位址
            url = prop.getProperty("jdbc.url");
            //擷取資料庫連接配接使用者名
            username = prop.getProperty("jdbc.username");
            //擷取資料庫連接配接密碼
            password = prop.getProperty("jdbc.password");
            //加載資料庫驅動
            Class.forName(driver);
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     * @return Connection資料庫連接配接對象
     * @throws SQLException
     * @Method: getConnection
     * @Description: 擷取資料庫連接配接對象
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    /**
     * @param conn
     * @param st
     * @param rs
     * @Method: release
     * @Description: 釋放資源,
     * 要釋放的資源包括Connection資料庫連接配接對象,負責執行SQL指令的Statement對象,存儲查詢結果的ResultSet對象
     */
    public static void release(Connection conn, Statement st, ResultSet rs) {
        try {
            if (rs != null) {
                //關閉存儲查詢結果的ResultSet對象
                rs.close();
            }
            if (st != null) {
                //關閉負責執行SQL指令的Statement對象
                st.close();
            }
            if (conn != null) {
                //關閉Connection資料庫連接配接對象
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}      

2.2.2.RunnerCRUDTest

maven依賴:

<!--導入dbutils包-->
 <dependency>
     <groupId>commons-dbutils</groupId>
     <artifactId>commons-dbutils</artifactId>
     <version>1.6</version>
 </dependency>      

測試表:

-- 測試表
 create table users(
     id int primary key auto_increment,
     name varchar(40),
     password varchar(40),
     email varchar(60),
     birthday date
 );      
package com.bruce.test;

import com.bruce.pojo.Users;
import com.bruce.utils.JdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;

public class QueryRunnerCRUDTest {

    @Test
    public void add() throws SQLException {
        //将資料源傳遞給QueryRunner,QueryRunner内部通過資料源擷取資料庫連接配接
        QueryRunner qr = new QueryRunner();
        String sql = "insert into users(name,password,email,birthday) values(?,?,?,?)";
        Object params[] = {"tom", "123", "[email protected]", new Date()};
        qr.update(JdbcUtils.getConnection(), sql, params);
    }

    @Test
    public void delete() throws SQLException {
        QueryRunner qr = new QueryRunner();
        String sql = "delete from users where id=?";
        qr.update(JdbcUtils.getConnection(), sql, 1);
    }

    @Test
    public void update() throws SQLException {
        QueryRunner qr = new QueryRunner();
        String sql = "update users set name=? where id=?";
        Object params[] = {"ddd", 5};
        qr.update(JdbcUtils.getConnection(), sql, params);
    }

    @Test
    public void find() throws SQLException {
        QueryRunner qr = new QueryRunner();
        String sql = "select * from users where id=?";
        Object params[] = {2};
        Users user = (Users) qr.query(JdbcUtils.getConnection(), sql, params, new BeanHandler(Users.class));
        System.out.println(user);
    }

    @Test
    public void getAll() throws SQLException {
        QueryRunner qr = new QueryRunner();
        String sql = "select * from users";
        List<Users> list = (List) qr.query(JdbcUtils.getConnection(),sql, new BeanListHandler(Users.class));
        System.out.println(list);
    }
}      

3.DButis整合Druid資料庫連接配接池

​Druid​

​(德魯伊)是阿裡巴巴開發的号稱為監控而生的資料庫連接配接池,Druid是目前最好的資料庫連接配接池。在功能、性能、擴充性方面,都超過其他資料庫連接配接池,同時加入了日志監控,可以很好的監控DB池連接配接和SQL的執行情況。

<!--導入druid資料庫連接配接池-->
  <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.10</version>
  </dependency>      

​jdbc.properties​

​配置類

# key=value
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/singerdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
username=root
password=123456
filters=stat
initialSize=2
maxActive=300
maxWait=60000

timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
maxPoolPreparedStatementPerConnectionSize=200      

參數說明:

Commons-dbutils架構
Commons-dbutils架構
package com.bruce.utils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;


public class JdbcUtils {

    private static Properties properties = null;
    private static DataSource dataSource = null;

    private volatile static JdbcUtils instatce = null;
    private Connection connection = null;

    //私有構造函數,防止執行個體化對象
    private JdbcUtils() {

    }

    static {
        try {
            properties = new Properties();
            // 1.加載properties檔案
            InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            // 2.加載輸入流
            properties.load(is);
            // 3.擷取資料源
            dataSource = getDatasource();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 用簡單單例模式確定隻傳回一個連結對象
     *
     * @return
     */
    public static JdbcUtils getInstace() {
        if (instatce == null) {
            synchronized (JdbcUtils.class) {
                if (instatce == null) {
                    instatce = new JdbcUtils();
                }
            }
        }
        return instatce;
    }

    // 傳回一個資料源
    public DataSource getDataSource() {
        return dataSource;
    }

    // 傳回一個連結
    public Connection getConnection() {
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    // 加載資料源
    public static DataSource getDatasource() {
        DataSource source = null;
        try {
            source = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return source;
    }
}      

4.改造QueryRunnerCRUDTest

DruidUtils

package com.bruce.utils;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

public class DruidUtils {

    private static Connection connection = null;

    //擷取中繼資料
    public static DataSource getDatasource() {
        DataSource dataSource = JdbcUtils.getInstace().getDataSource();
        return dataSource;
    }

    //擷取連結
    public static Connection getConnection() {
        connection = JdbcUtils.getInstace().getConnection();
        return connection;
    }

    //歸還資源
    public void release() {
        try {
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}      
package com.bruce.test;

import com.bruce.pojo.Users;
import com.bruce.utils.JdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;

public class QueryRunnerCRUDTest {


    //将資料源傳遞給QueryRunner,QueryRunner内部通過資料源擷取資料庫連接配接
    QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

    @Test
    public void add() throws SQLException {
        String sql = "insert into users(name,password,email,birthday) values(?,?,?,?)";
        Object params[] = {"tom", "123", "[email protected]", new Date()};
        qr.update(sql, params);
    }

    @Test
    public void delete() throws SQLException {
        String sql = "delete from users where id=?";
        qr.update(sql, 1);
    }

    @Test
    public void update() throws SQLException {
        String sql = "update users set name=? where id=?";
        Object params[] = {"ddd", 5};
        qr.update(sql, params);
    }

    @Test
    public void find() throws SQLException {
        String sql = "select * from users where id=?";
        Object params[] = {2};
        Users user = (Users) qr.query(sql, new BeanHandler(Users.class), params);
        System.out.println(user);
    }

    @Test
    public void getAll() throws SQLException {
        String sql = "select * from users";
        List<Users> list = (List) qr.query(sql, new BeanListHandler(Users.class));
        System.out.println(list);
    }
}      

5.ResultSetHandler接口使用講解

該接口用于處理​

​java.sql.ResultSet​

​​,将資料按要求轉換為另一種形式。

​​

​ResultSetHandler​

​​接口提供了一個單獨的方法:​

​Object handle (java.sql.ResultSet .rs)​

5.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​

5.2.測試dbutils各種類型的處理器

package com.bruce.test;

import com.bruce.utils.DruidUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.*;
import org.junit.Test;

import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class ResultSetHandlerTest {

    QueryRunner qr = new QueryRunner(DruidUtils.getDatasource());

    /**
     * 把結果集中的第一行資料轉成對象數組
     *
     * @throws SQLException
     */
    @Test
    public void testArrayHandler() throws SQLException {
        String sql = "select * from users";
        Object result[] = (Object[]) qr.query(sql, new ArrayHandler());
        System.out.println(Arrays.asList(result));  //list  toString()
    }

    /**
     * 把結果集中的每一行資料都轉成一個數組,再存放到List中。
     *
     * @throws SQLException
     */
    @Test
    public void testArrayListHandler() throws SQLException {
        String sql = "select * from users";
        List<Object[]> list = (List) qr.query(sql, new ArrayListHandler());
        for (Object[] o : list) {
            System.out.println(Arrays.asList(o));
        }
    }

    /**
     * 将結果集中某一列的資料存放到List中。
     *
     * @throws SQLException
     */
    @Test
    public void testColumnListHandler() throws SQLException {
        String sql = "select * from users";
        List list = (List) qr.query(sql, new ColumnListHandler("id"));
        System.out.println(list);
    }

    /**
     * KeyedHandler(name):将結果集中的每一行資料都封裝到一個Map裡,再把這些map再存到一個map裡,其key為指定的key。
     *
     * @throws Exception
     */
    @Test
    public void testKeyedHandler() throws Exception {
        String sql = "select * from users";
        Map<Integer, Map> map = (Map) qr.query(sql, new KeyedHandler("id"));
        for (Map.Entry<Integer, Map> me : map.entrySet()) {
            int id = me.getKey();
            Map<String, Object> innermap = me.getValue();
            for (Map.Entry<String, Object> innerme : innermap.entrySet()) {
                String columnName = innerme.getKey();
                Object value = innerme.getValue();
                System.out.println(columnName + "=" + value);
            }
            System.out.println("----------------");
        }
    }

    /**
     * 将結果集中的第一行資料封裝到一個Map裡,key是列名,value就是對應的值。
     * @throws SQLException
     */
    @Test
    public void testMapHandler() throws SQLException {
        String sql = "select * from users";
        Map<String, Object> map = (Map) qr.query(sql, new MapHandler());
        for (Map.Entry<String, Object> me : map.entrySet()) {
            System.out.println(me.getKey() + "=" + me.getValue());
        }
    }


    /**
     * 結果集中的每一行資料都封裝到一個Map裡,然後再存放到List
     * @throws SQLException
     */
    @Test
    public void testMapListHandler() throws SQLException {
        String sql = "select * from users";
        List<Map> list = (List) qr.query(sql, new MapListHandler());
        for (Map<String, Object> map : list) {
            for (Map.Entry<String, Object> me : map.entrySet()) {
                System.out.println(me.getKey() + "=" + me.getValue());
            }
        }
    }

    @Test
    public void testScalarHandler() throws SQLException {
        String sql = "select count(*) from users";  //[13]  list[13]
        int count = ((Long) qr.query(sql, new ScalarHandler(1))).intValue();
        System.out.println(count);
    }

}      

5.5.事務處理

在開發中,對資料庫的多個表或者對一個表中的多條資料執行更新操作時要保證對多個更新操作要麼同時成功,要麼都不成功,這就涉及到對多個更新操作的事務管理問題了。比如銀行業務中的轉賬問題,A使用者向B使用者轉賬100元,假設A使用者和B使用者的錢都存儲在Account表,那麼A使用者向B使用者轉賬時就涉及到同時更新Account表中的A使用者的錢和B使用者的錢,用SQL來表示就是:

update account set money=money-100 where name='A'
update account set money=money+100 where name='B'      

在資料通路層(Dao)中處理事務

對于這樣的同時更新一個表中的多條資料的操作,那麼必須保證要麼同時成功,要麼都不成功,是以需要保證這兩個update操作在同一個事務中進行。在開發中,我們可能會在​

​AccountDao​

​寫一個轉賬處理方法,如下:

/**
    * @Method: transfer
    * @Description:這個方法是用來處理兩個使用者之間的轉賬業務
    * 在開發中,DAO層的職責應該隻涉及到CRUD,
    * 而這個transfer方法是處理兩個使用者之間的轉賬業務的,已經涉及到具體的業務操作,應該在業務層中做,不應該出現在DAO層的
    * 是以在開發中DAO層出現這樣的業務處理方法是完全錯誤的
    * @param sourceName
    * @param targetName
    * @param money
    * @throws SQLException
    */
    public void transfer(String sourceName,String targetName,float money) throws SQLException{
        Connection conn = null;
        try{
            conn = DruidUtils.getConnection();
            //開啟事務
            conn.setAutoCommit(false);
            /**
             * 在建立QueryRunner對象時,不傳遞資料源給它,是為了保證這兩條SQL在同一個事務中進行,
             * 我們手動擷取資料庫連接配接,然後讓這兩條SQL使用同一個資料庫連接配接執行
             */
            QueryRunner runner = new QueryRunner();
            String sql1 = "update account set money=money-100 where name=?";
            String sql2 = "update account set money=money+100 where name=?";
            Object[] paramArr1 = {sourceName};
            Object[] paramArr2 = {targetName};
            runner.update(conn,sql1,paramArr1);
            //模拟程式出現異常讓事務復原
            int x = 1/0;
            runner.update(conn,sql2,paramArr2);
            //sql正常執行之後就送出事務
            conn.commit();
        }catch (Exception e) {
            e.printStackTrace();
            if(conn!=null){
                //出現異常之後就復原事務
                conn.rollback();
            }
        }finally{
            //關閉資料庫連接配接
            conn.close();
        }
    }      

5.6.自定義處理程式

DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `age` int(11) DEFAULT NULL,
  `firstName` varchar(255) DEFAULT NULL,
  `lastName` varchar(255) DEFAULT NULL,
  `uname` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of employee
-- ----------------------------
INSERT INTO `employee` VALUES ('1', '18', '張', '三', '張三');
INSERT INTO `employee` VALUES ('2', '19', '李', '四', '李四');      

實體類

package com.bruce.pojo;

public class Employee {

    private int id;
    private int age;
    private String first;
    private String last;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getFirst() {
        return first;
    }

    public void setFirst(String first) {
        this.first = first;
    }

    public String getLast() {
        return last;
    }

    public void setLast(String last) {
        this.last = last;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Employee() {
    }
}      

自定義​

​EmployeeHandler​

package com.bruce.pojo;

import org.apache.commons.dbutils.handlers.BeanHandler;

import java.sql.ResultSet;
import java.sql.SQLException;

public class EmployeeHandler extends BeanHandler<Employee> {

    public EmployeeHandler(Class<Employee> type) {
        super(type);
    }

    @Override
    public Employee handle(ResultSet rs) throws SQLException {
        Employee employee = super.handle(rs);
        employee.setFirst(rs.getString("firstName"));
        employee.setLast(rs.getString("lastName"));
        employee.setName(rs.getString("uname"));
        return employee;
    }
}      
@Test
 public void testEmp() throws SQLException {
     String sql = "SELECT * FROM employee WHERE firstName=?";
     Employee employee =  qr.query(sql, new EmployeeHandler(Employee.class),"李");
     System.out.println(employee);
 }      
@Test
 public void testEmpList() throws SQLException {
     String sql = "SELECT * FROM employee";
     List<Employee> list = (List) qr.query(sql, new ResultSetHandler<List<Employee>>(){
         public List<Employee> handle(ResultSet resultSet) throws SQLException {
             List<Employee> employeees = new ArrayList<Employee>();
             while (resultSet.next()){
                 int id = resultSet.getInt("id");
                 int age = resultSet.getInt("age");
                 String firstName=resultSet.getString("firstName");
                 String lastName=resultSet.getString("lastName");
                 String uname=resultSet.getString("uname");
                 Employee employee=new Employee(id,age,firstName,lastName,uname);
                 employeees.add(employee);
             }
             return employeees;
         }
     });
     for (Employee employee : list) {
         System.out.println(employee);
     }
 }

 @Test
 public void testEmp1() throws SQLException {
     String sql = "SELECT * FROM employee WHERE firstName=?";
     Employee employee =  qr.query(sql, new ResultSetHandler<Employee>(){

         public Employee handle(ResultSet resultSet) throws SQLException {
             Employee employee=null;
             while (resultSet.next()){
                 int id = resultSet.getInt("id");
                 int age = resultSet.getInt("age");
                 String firstName=resultSet.getString("firstName");
                 String lastName=resultSet.getString("lastName");
                 String uname=resultSet.getString("uname");
                 employee=new Employee(id,age,firstName,lastName,uname);
             }
             return  employee;
         }
     },"李");
     System.out.println(employee);