玩轉JDBC打造資料庫操作萬能工具類JDBCUtil,加入了高效的資料庫連線池,利用了引數繫結有效防止SQL隱碼攻擊

DylanAndroid發表於2016-12-20

轉載請註明出處:http://blog.csdn.net/linglongxin24/article/details/53750584
本文出自【DylanAndroid的部落格】


玩轉JDBC打造資料庫操作萬能工具類JDBCUtil,加入了高效的資料庫連線池,利用了引數繫結有效防止SQL隱碼攻擊

在之前學習了MySQL和Oracle之後,那麼,如和在Java種去連線這兩種資料庫。在這個輕量級的工具類當中,使用了資料庫連線池
去提高資料庫連線的高效性,並且使用了PreparedStatement來執行對SQL的預編譯,能夠有效防止SQL隱碼攻擊問題。

一.準備在配置檔案配置:配置資料庫連線屬性檔案:在專案新建config包下建立jdbc-mysql.properties並加入以下配置

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8
jdbc.username=root
jdbc.password=root複製程式碼

二.準備資料庫連線池物件:單例

package util;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;

import java.beans.PropertyVetoException;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * 資料庫連線物件
 * Created by yuandl on 2016-12-16.
 */
public class DBConnectionPool {
    private static volatile DBConnectionPool dbConnection;
    private ComboPooledDataSource cpds;

    /**
     * 在建構函式初始化的時候獲取資料庫連線
     */
    private DBConnectionPool() {
        try {
            /**通過屬性檔案獲取資料庫連線的引數值**/
            Properties properties = new Properties();
            FileInputStream fileInputStream = new FileInputStream("src/config/jdbc-mysql.properties");
            properties.load(fileInputStream);
            /**獲取屬性檔案中的值**/
            String driverClassName = properties.getProperty("jdbc.driverClassName");
            String url = properties.getProperty("jdbc.url");
            String username = properties.getProperty("jdbc.username");
            String password = properties.getProperty("jdbc.password");

            /**資料庫連線池物件**/
            cpds = new ComboPooledDataSource();

            /**設定資料庫連線驅動**/
            cpds.setDriverClass(driverClassName);
            /**設定資料庫連線地址**/
            cpds.setJdbcUrl(url);
            /**設定資料庫連線使用者名稱**/
            cpds.setUser(username);
            /**設定資料庫連線密碼**/
            cpds.setPassword(password);

            /**初始化時建立的連線數,應在minPoolSize與maxPoolSize之間取值.預設為3**/
            cpds.setInitialPoolSize(3);
            /**連線池中保留的最大連線資料.預設為15**/
            cpds.setMaxPoolSize(10);
            /**當連線池中的連線用完時,C3PO一次性建立新的連線數目;**/
            cpds.setAcquireIncrement(1);
            /**隔多少秒檢查所有連線池中的空閒連線,預設為0表示不檢查;**/
            cpds.setIdleConnectionTestPeriod(60);
            /**最大空閒時間,超過空閒時間的連線將被丟棄.為0或負資料則永不丟棄.預設為0;**/
            cpds.setMaxIdleTime(3000);

            /**因效能消耗大請只在需要的時候使用它。如果設為true那麼在每個connection提交的
             時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
             等方法來提升連線測試的效能。Default: false**/
            cpds.setTestConnectionOnCheckout(true);

            /**如果設為true那麼在取得連線的同時將校驗連線的有效性。Default: false **/
            cpds.setTestConnectionOnCheckin(true);
            /**定義在從資料庫獲取新的連線失敗後重復嘗試獲取的次數,預設為30;**/
            cpds.setAcquireRetryAttempts(30);
            /**兩次連線中間隔時間預設為1000毫秒**/
            cpds.setAcquireRetryDelay(1000);
            /** 獲取連線失敗將會引起所有等待獲取連線的執行緒異常,
             但是資料來源仍有效的保留,並在下次呼叫getConnection()的時候繼續嘗試獲取連線.如果設為true,
             那麼嘗試獲取連線失敗後該資料來源將申明已經斷開並永久關閉.預設為false**/
            cpds.setBreakAfterAcquireFailure(true);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }

    }

    /**
     * 獲取資料庫連線物件,單例
     *
     * @return
     */
    public static DBConnectionPool getInstance() {
        if (dbConnection == null) {
            synchronized (DBConnectionPool.class) {
                if (dbConnection == null) {
                    dbConnection = new DBConnectionPool();
                }
            }
        }
        return dbConnection;
    }

    /**
     * 獲取資料庫連線
     *
     * @return 資料庫連線
     */
    public final synchronized Connection getConnection() throws SQLException {
        return cpds.getConnection();
    }

    /**
     * finalize()方法是在垃圾收集器刪除物件之前對這個物件呼叫的。
     *
     * @throws Throwable
     */
    protected void finalize() throws Throwable {
        DataSources.destroy(cpds);
        super.finalize();
    }
}複製程式碼

三.實現新增、修改、刪除、查詢操作的兩個核心方法:可以實現任何複雜的SQL,而且通過資料繫結的方式不會有SQL隱碼攻擊問題

    /**
     * 可以執行新增,修改,刪除
     *
     * @param sql      sql語句
     * @param bindArgs 繫結引數
     * @return 影響的行數
     * @throws SQLException SQL異常
     */
    public static int executeUpdate(String sql, Object[] bindArgs) throws SQLException {
        /**影響的行數**/
        int affectRowCount = -1;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            /**從資料庫連線池中獲取資料庫連線**/
            connection = DBConnectionPool.getInstance().getConnection();
            /**執行SQL預編譯**/
            preparedStatement = connection.prepareStatement(sql.toString());
            /**設定不自動提交,以便於在出現異常的時候資料庫回滾**/
            connection.setAutoCommit(false);
            System.out.println(getExecSQL(sql, bindArgs));
            if (bindArgs != null) {
                /**繫結引數設定sql佔位符中的值**/
                for (int i = 0; i < bindArgs.length; i++) {
                    preparedStatement.setObject(i + 1, bindArgs[i]);
                }
            }
            /**執行sql**/
            affectRowCount = preparedStatement.executeUpdate();
            connection.commit();
            String operate;
            if (sql.toUpperCase().indexOf("DELETE FROM") != -1) {
                operate = "刪除";
            } else if (sql.toUpperCase().indexOf("INSERT INTO") != -1) {
                operate = "新增";
            } else {
                operate = "修改";
            }
            System.out.println("成功" + operate + "了" + affectRowCount + "行");
            System.out.println();
        } catch (Exception e) {
            if (connection != null) {
                connection.rollback();
            }
            e.printStackTrace();
            throw e;
        } finally {
            if (preparedStatement != null) {
                preparedStatement.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
        return affectRowCount;
    }


   /**
     * 執行查詢
     *
     * @param sql      要執行的sql語句
     * @param bindArgs 繫結的引數
     * @return List<Map<String, Object>>結果集物件
     * @throws SQLException SQL執行異常
     */
    public static List<Map<String, Object>> executeQuery(String sql, Object[] bindArgs) throws SQLException {
        List<Map<String, Object>> datas = new ArrayList<>();
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            /**獲取資料庫連線池中的連線**/
            connection = DBConnectionPool.getInstance().getConnection();
            preparedStatement = connection.prepareStatement(sql);
            if (bindArgs != null) {
                /**設定sql佔位符中的值**/
                for (int i = 0; i < bindArgs.length; i++) {
                    preparedStatement.setObject(i + 1, bindArgs[i]);
                }
            }
            System.out.println(getExecSQL(sql, bindArgs));
            /**執行sql語句,獲取結果集**/
            resultSet = preparedStatement.executeQuery();
            getDatas(resultSet);
            System.out.println();
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (resultSet != null) {
                resultSet.close();
            }
            if (preparedStatement != null) {
                preparedStatement.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
        return datas;
    }複製程式碼

四.執行新增的簡化操作

 /**
     * 執行資料庫插入操作
     *
     * @param valueMap  插入資料表中key為列名和value為列對應的值的Map物件
     * @param tableName 要插入的資料庫的表名
     * @return 影響的行數
     * @throws SQLException SQL異常
     */
    public static int insert(String tableName, Map<String, Object> valueMap) throws SQLException {

        /**獲取資料庫插入的Map的鍵值對的值**/
        Set<String> keySet = valueMap.keySet();
        Iterator<String> iterator = keySet.iterator();
        /**要插入的欄位sql,其實就是用key拼起來的**/
        StringBuilder columnSql = new StringBuilder();
        /**要插入的欄位值,其實就是?**/
        StringBuilder unknownMarkSql = new StringBuilder();
        Object[] bindArgs = new Object[valueMap.size()];
        int i = 0;
        while (iterator.hasNext()) {
            String key = iterator.next();
            columnSql.append(i == 0 ? "" : ",");
            columnSql.append(key);

            unknownMarkSql.append(i == 0 ? "" : ",");
            unknownMarkSql.append("?");
            bindArgs[i] = valueMap.get(key);
            i++;
        }
        /**開始拼插入的sql語句**/
        StringBuilder sql = new StringBuilder();
        sql.append("INSERT INTO ");
        sql.append(tableName);
        sql.append(" (");
        sql.append(columnSql);
        sql.append(" )  VALUES (");
        sql.append(unknownMarkSql);
        sql.append(" )");
        return executeUpdate(sql.toString(), bindArgs);
    }複製程式碼

五.執行更新的簡化操作

  /**
     * 執行更新操作
     *
     * @param tableName 表名
     * @param valueMap  要更改的值
     * @param whereMap  條件
     * @return 影響的行數
     * @throws SQLException SQL異常
     */
    public static int update(String tableName, Map<String, Object> valueMap, Map<String, Object> whereMap) throws SQLException {
        /**獲取資料庫插入的Map的鍵值對的值**/
        Set<String> keySet = valueMap.keySet();
        Iterator<String> iterator = keySet.iterator();
        /**開始拼插入的sql語句**/
        StringBuilder sql = new StringBuilder();
        sql.append("UPDATE ");
        sql.append(tableName);
        sql.append(" SET ");

        /**要更改的的欄位sql,其實就是用key拼起來的**/
        StringBuilder columnSql = new StringBuilder();
        int i = 0;
        List<Object> objects = new ArrayList<>();
        while (iterator.hasNext()) {
            String key = iterator.next();
            columnSql.append(i == 0 ? "" : ",");
            columnSql.append(key + " = ? ");
            objects.add(valueMap.get(key));
            i++;
        }
        sql.append(columnSql);

        /**更新的條件:要更改的的欄位sql,其實就是用key拼起來的**/
        StringBuilder whereSql = new StringBuilder();
        int j = 0;
        if (whereMap != null && whereMap.size() > 0) {
            whereSql.append(" WHERE ");
            iterator = whereMap.keySet().iterator();
            while (iterator.hasNext()) {
                String key = iterator.next();
                whereSql.append(j == 0 ? "" : " AND ");
                whereSql.append(key + " = ? ");
                objects.add(whereMap.get(key));
                j++;
            }
            sql.append(whereSql);
        }
        return executeUpdate(sql.toString(), objects.toArray());
    }複製程式碼

六.執行刪除的簡化操作

  /**
     * 執行刪除操作
     *
     * @param tableName 要刪除的表名
     * @param whereMap  刪除的條件
     * @return 影響的行數
     * @throws SQLException SQL執行異常
     */
    public static int delete(String tableName, Map<String, Object> whereMap) throws SQLException {
        /**準備刪除的sql語句**/
        StringBuilder sql = new StringBuilder();
        sql.append("DELETE FROM ");
        sql.append(tableName);

        /**更新的條件:要更改的的欄位sql,其實就是用key拼起來的**/
        StringBuilder whereSql = new StringBuilder();
        Object[] bindArgs = null;
        if (whereMap != null && whereMap.size() > 0) {
            bindArgs = new Object[whereMap.size()];
            whereSql.append(" WHERE ");
            /**獲取資料庫插入的Map的鍵值對的值**/
            Set<String> keySet = whereMap.keySet();
            Iterator<String> iterator = keySet.iterator();
            int i = 0;
            while (iterator.hasNext()) {
                String key = iterator.next();
                whereSql.append(i == 0 ? "" : " AND ");
                whereSql.append(key + " = ? ");
                bindArgs[i] = whereMap.get(key);
                i++;
            }
            sql.append(whereSql);
        }
        return executeUpdate(sql.toString(), bindArgs);
    }複製程式碼

七.查詢的4種玩法

  • 1.執行sql通過 Map限定查詢條件查詢

    /**
       * 1..執行sql通過 Map<String, Object>限定查詢條件查詢
       *
       * @param tableName 表名
       * @param whereMap  where條件
       * @return List<Map<String, Object>>
       * @throws SQLException
       */
      public static List<Map<String, Object>> query(String tableName,
                                                    Map<String, Object> whereMap) throws SQLException {
          String whereClause = "";
          Object[] whereArgs = null;
          if (whereMap != null && whereMap.size() > 0) {
              Iterator<String> iterator = whereMap.keySet().iterator();
              whereArgs = new Object[whereMap.size()];
              int i = 0;
              while (iterator.hasNext()) {
                  String key = iterator.next();
                  whereClause += (i == 0 ? "" : " AND ");
                  whereClause += (key + " = ? ");
                  whereArgs[i] = whereMap.get(key);
                  i++;
              }
          }
          return query(tableName, false, null, whereClause, whereArgs, null, null, null, null);
      }複製程式碼
  • 2.執行sql條件引數繫結形式的查詢

      /**
       * 2.執行sql條件引數繫結形式的查詢
       *
       * @param tableName   表名
       * @param whereClause where條件的sql
       * @param whereArgs   where條件中佔位符中的值
       * @return List<Map<String, Object>>
       * @throws SQLException
       */
      public static List<Map<String, Object>> query(String tableName,
                                                    String whereClause,
                                                    String[] whereArgs) throws SQLException {
          return query(tableName, false, null, whereClause, whereArgs, null, null, null, null);
      }複製程式碼
  • 3.包含所有的查詢條件的查詢方法

       /**
        * 執行全部結構的sql查詢
        *
        * @param tableName     表名
        * @param distinct      去重
        * @param columns       要查詢的列名
        * @param selection     where條件
        * @param selectionArgs where條件中佔位符中的值
        * @param groupBy       分組
        * @param having        篩選
        * @param orderBy       排序
        * @param limit         分頁
        * @return List<Map<String, Object>>
        * @throws SQLException
        */
       public static List<Map<String, Object>> query(String tableName,
                                                     boolean distinct,
                                                     String[] columns,
                                                     String selection,
                                                     Object[] selectionArgs,
                                                     String groupBy,
                                                     String having,
                                                     String orderBy,
                                                     String limit) throws SQLException {
           String sql = buildQueryString(distinct, tableName, columns, selection, groupBy, having, orderBy, limit);
           return executeQuery(sql, selectionArgs);
         }複製程式碼
    • 4.通過單純的sql查詢資料,慎用,會有sql注入問題,只是為了方便查詢,實際開發中不會去使用這個方法
         * 4.通過sql查詢資料,
         * 慎用,會有sql注入問題只是為了方便查詢,實際開發中不會去使用這個方法
         *
         * @param sql
         * @return 查詢的資料集合
         * @throws SQLException
         */
        public static List<Map<String, Object>> query(String sql) throws SQLException {
            return executeQuery(sql, null);
        }複製程式碼

    八.DBUtil的完整程式碼

package util;

import com.sun.istack.internal.Nullable;

import java.sql.*;
import java.util.*;
import java.util.regex.Pattern;

/**
* 資料庫JDBC連線工具類
* Created by yuandl on 2016-12-16.
*/
public class DBUtil {

 /**
  * 執行資料庫插入操作
  *
  * @param valueMap  插入資料表中key為列名和value為列對應的值的Map物件
  * @param tableName 要插入的資料庫的表名
  * @return 影響的行數
  * @throws SQLException SQL異常
  */
 public static int insert(String tableName, Map<String, Object> valueMap) throws SQLException {

     /**獲取資料庫插入的Map的鍵值對的值**/
     Set<String> keySet = valueMap.keySet();
     Iterator<String> iterator = keySet.iterator();
     /**要插入的欄位sql,其實就是用key拼起來的**/
     StringBuilder columnSql = new StringBuilder();
     /**要插入的欄位值,其實就是?**/
     StringBuilder unknownMarkSql = new StringBuilder();
     Object[] bindArgs = new Object[valueMap.size()];
     int i = 0;
     while (iterator.hasNext()) {
         String key = iterator.next();
         columnSql.append(i == 0 ? "" : ",");
         columnSql.append(key);

         unknownMarkSql.append(i == 0 ? "" : ",");
         unknownMarkSql.append("?");
         bindArgs[i] = valueMap.get(key);
         i++;
     }
     /**開始拼插入的sql語句**/
     StringBuilder sql = new StringBuilder();
     sql.append("INSERT INTO ");
     sql.append(tableName);
     sql.append(" (");
     sql.append(columnSql);
     sql.append(" )  VALUES (");
     sql.append(unknownMarkSql);
     sql.append(" )");
     return executeUpdate(sql.toString(), bindArgs);
 }

 /**
  * 執行更新操作
  *
  * @param tableName 表名
  * @param valueMap  要更改的值
  * @param whereMap  條件
  * @return 影響的行數
  * @throws SQLException SQL異常
  */
 public static int update(String tableName, Map<String, Object> valueMap, Map<String, Object> whereMap) throws SQLException {
     /**獲取資料庫插入的Map的鍵值對的值**/
     Set<String> keySet = valueMap.keySet();
     Iterator<String> iterator = keySet.iterator();
     /**開始拼插入的sql語句**/
     StringBuilder sql = new StringBuilder();
     sql.append("UPDATE ");
     sql.append(tableName);
     sql.append(" SET ");

     /**要更改的的欄位sql,其實就是用key拼起來的**/
     StringBuilder columnSql = new StringBuilder();
     int i = 0;
     List<Object> objects = new ArrayList<>();
     while (iterator.hasNext()) {
         String key = iterator.next();
         columnSql.append(i == 0 ? "" : ",");
         columnSql.append(key + " = ? ");
         objects.add(valueMap.get(key));
         i++;
     }
     sql.append(columnSql);

     /**更新的條件:要更改的的欄位sql,其實就是用key拼起來的**/
     StringBuilder whereSql = new StringBuilder();
     int j = 0;
     if (whereMap != null && whereMap.size() > 0) {
         whereSql.append(" WHERE ");
         iterator = whereMap.keySet().iterator();
         while (iterator.hasNext()) {
             String key = iterator.next();
             whereSql.append(j == 0 ? "" : " AND ");
             whereSql.append(key + " = ? ");
             objects.add(whereMap.get(key));
             j++;
         }
         sql.append(whereSql);
     }
     return executeUpdate(sql.toString(), objects.toArray());
 }

 /**
  * 執行刪除操作
  *
  * @param tableName 要刪除的表名
  * @param whereMap  刪除的條件
  * @return 影響的行數
  * @throws SQLException SQL執行異常
  */
 public static int delete(String tableName, Map<String, Object> whereMap) throws SQLException {
     /**準備刪除的sql語句**/
     StringBuilder sql = new StringBuilder();
     sql.append("DELETE FROM ");
     sql.append(tableName);

     /**更新的條件:要更改的的欄位sql,其實就是用key拼起來的**/
     StringBuilder whereSql = new StringBuilder();
     Object[] bindArgs = null;
     if (whereMap != null && whereMap.size() > 0) {
         bindArgs = new Object[whereMap.size()];
         whereSql.append(" WHERE ");
         /**獲取資料庫插入的Map的鍵值對的值**/
         Set<String> keySet = whereMap.keySet();
         Iterator<String> iterator = keySet.iterator();
         int i = 0;
         while (iterator.hasNext()) {
             String key = iterator.next();
             whereSql.append(i == 0 ? "" : " AND ");
             whereSql.append(key + " = ? ");
             bindArgs[i] = whereMap.get(key);
             i++;
         }
         sql.append(whereSql);
     }
     return executeUpdate(sql.toString(), bindArgs);
 }

 /**
  * 可以執行新增,修改,刪除
  *
  * @param sql      sql語句
  * @param bindArgs 繫結引數
  * @return 影響的行數
  * @throws SQLException SQL異常
  */
 public static int executeUpdate(String sql, Object[] bindArgs) throws SQLException {
     /**影響的行數**/
     int affectRowCount = -1;
     Connection connection = null;
     PreparedStatement preparedStatement = null;
     try {
         /**從資料庫連線池中獲取資料庫連線**/
         connection = DBConnectionPool.getInstance().getConnection();
         /**執行SQL預編譯**/
         preparedStatement = connection.prepareStatement(sql.toString());
         /**設定不自動提交,以便於在出現異常的時候資料庫回滾**/
         connection.setAutoCommit(false);
         System.out.println(getExecSQL(sql, bindArgs));
         if (bindArgs != null) {
             /**繫結引數設定sql佔位符中的值**/
             for (int i = 0; i < bindArgs.length; i++) {
                 preparedStatement.setObject(i + 1, bindArgs[i]);
             }
         }
         /**執行sql**/
         affectRowCount = preparedStatement.executeUpdate();
         connection.commit();
         String operate;
         if (sql.toUpperCase().indexOf("DELETE FROM") != -1) {
             operate = "刪除";
         } else if (sql.toUpperCase().indexOf("INSERT INTO") != -1) {
             operate = "新增";
         } else {
             operate = "修改";
         }
         System.out.println("成功" + operate + "了" + affectRowCount + "行");
         System.out.println();
     } catch (Exception e) {
         if (connection != null) {
             connection.rollback();
         }
         e.printStackTrace();
         throw e;
     } finally {
         if (preparedStatement != null) {
             preparedStatement.close();
         }
         if (connection != null) {
             connection.close();
         }
     }
     return affectRowCount;
 }

 /**
  * 通過sql查詢資料,
  * 慎用,會有sql注入問題
  *
  * @param sql
  * @return 查詢的資料集合
  * @throws SQLException
  */
 public static List<Map<String, Object>> query(String sql) throws SQLException {
     return executeQuery(sql, null);
 }

 /**
  * 執行sql通過 Map<String, Object>限定查詢條件查詢
  *
  * @param tableName 表名
  * @param whereMap  where條件
  * @return List<Map<String, Object>>
  * @throws SQLException
  */
 public static List<Map<String, Object>> query(String tableName,
                                               Map<String, Object> whereMap) throws SQLException {
     String whereClause = "";
     Object[] whereArgs = null;
     if (whereMap != null & whereMap.size() > 0) {
         Iterator<String> iterator = whereMap.keySet().iterator();
         whereArgs = new Object[whereMap.size()];
         int i = 0;
         while (iterator.hasNext()) {
             String key = iterator.next();
             whereClause += (i == 0 ? "" : " AND ");
             whereClause += (key + " = ? ");
             whereArgs[i] = whereMap.get(key);
             i++;
         }
     }
     return query(tableName, false, null, whereClause, whereArgs, null, null, null, null);
 }

 /**
  * 執行sql條件引數繫結形式的查詢
  *
  * @param tableName   表名
  * @param whereClause where條件的sql
  * @param whereArgs   where條件中佔位符中的值
  * @return List<Map<String, Object>>
  * @throws SQLException
  */
 public static List<Map<String, Object>> query(String tableName,
                                               String whereClause,
                                               String[] whereArgs) throws SQLException {
     return query(tableName, false, null, whereClause, whereArgs, null, null, null, null);
 }

 /**
  * 執行全部結構的sql查詢
  *
  * @param tableName     表名
  * @param distinct      去重
  * @param columns       要查詢的列名
  * @param selection     where條件
  * @param selectionArgs where條件中佔位符中的值
  * @param groupBy       分組
  * @param having        篩選
  * @param orderBy       排序
  * @param limit         分頁
  * @return List<Map<String, Object>>
  * @throws SQLException
  */
 public static List<Map<String, Object>> query(String tableName,
                                               boolean distinct,
                                               String[] columns,
                                               String selection,
                                               Object[] selectionArgs,
                                               String groupBy,
                                               String having,
                                               String orderBy,
                                               String limit) throws SQLException {
     String sql = buildQueryString(distinct, tableName, columns, selection, groupBy, having, orderBy, limit);
     return executeQuery(sql, selectionArgs);

 }

 /**
  * 執行查詢
  *
  * @param sql      要執行的sql語句
  * @param bindArgs 繫結的引數
  * @return List<Map<String, Object>>結果集物件
  * @throws SQLException SQL執行異常
  */
 public static List<Map<String, Object>> executeQuery(String sql, Object[] bindArgs) throws SQLException {
     List<Map<String, Object>> datas = new ArrayList<>();
     Connection connection = null;
     PreparedStatement preparedStatement = null;
     ResultSet resultSet = null;

     try {
         /**獲取資料庫連線池中的連線**/
         connection = DBConnectionPool.getInstance().getConnection();
         preparedStatement = connection.prepareStatement(sql);
         if (bindArgs != null) {
             /**設定sql佔位符中的值**/
             for (int i = 0; i < bindArgs.length; i++) {
                 preparedStatement.setObject(i + 1, bindArgs[i]);
             }
         }
         System.out.println(getExecSQL(sql, bindArgs));
         /**執行sql語句,獲取結果集**/
         resultSet = preparedStatement.executeQuery();
         getDatas(resultSet);
         System.out.println();
     } catch (Exception e) {
         e.printStackTrace();
         throw e;
     } finally {
         if (resultSet != null) {
             resultSet.close();
         }
         if (preparedStatement != null) {
             preparedStatement.close();
         }
         if (connection != null) {
             connection.close();
         }
     }
     return datas;
 }


 /**
  * 將結果集物件封裝成List<Map<String, Object>> 物件
  *
  * @param resultSet 結果多想
  * @return 結果的封裝
  * @throws SQLException
  */
 private static List<Map<String, Object>> getDatas(ResultSet resultSet) throws SQLException {
     List<Map<String, Object>> datas = new ArrayList<>();
     /**獲取結果集的資料結構物件**/
     ResultSetMetaData metaData = resultSet.getMetaData();
     while (resultSet.next()) {
         Map<String, Object> rowMap = new HashMap<>();
         for (int i = 1; i <= metaData.getColumnCount(); i++) {
             rowMap.put(metaData.getColumnName(i), resultSet.getObject(i));
         }
         datas.add(rowMap);
     }
     System.out.println("成功查詢到了" + datas.size() + "行資料");
     for (int i = 0; i < datas.size(); i++) {
         Map<String, Object> map = datas.get(i);
         System.out.println("第" + (i + 1) + "行:" + map);
     }
     return datas;
 }


 /**
  * Build an SQL query string from the given clauses.
  *
  * @param distinct true if you want each row to be unique, false otherwise.
  * @param tables   The table names to compile the query against.
  * @param columns  A list of which columns to return. Passing null will
  *                 return all columns, which is discouraged to prevent reading
  *                 data from storage that isn't going to be used.
  * @param where    A filter declaring which rows to return, formatted as an SQL
  *                 WHERE clause (excluding the WHERE itself). Passing null will
  *                 return all rows for the given URL.
  * @param groupBy  A filter declaring how to group rows, formatted as an SQL
  *                 GROUP BY clause (excluding the GROUP BY itself). Passing null
  *                 will cause the rows to not be grouped.
  * @param having   A filter declare which row groups to include in the cursor,
  *                 if row grouping is being used, formatted as an SQL HAVING
  *                 clause (excluding the HAVING itself). Passing null will cause
  *                 all row groups to be included, and is required when row
  *                 grouping is not being used.
  * @param orderBy  How to order the rows, formatted as an SQL ORDER BY clause
  *                 (excluding the ORDER BY itself). Passing null will use the
  *                 default sort order, which may be unordered.
  * @param limit    Limits the number of rows returned by the query,
  *                 formatted as LIMIT clause. Passing null denotes no LIMIT clause.
  * @return the SQL query string
  */
 private static String buildQueryString(
         boolean distinct, String tables, String[] columns, String where,
         String groupBy, String having, String orderBy, String limit) {
     if (isEmpty(groupBy) && !isEmpty(having)) {
         throw new IllegalArgumentException(
                 "HAVING clauses are only permitted when using a groupBy clause");
     }
     if (!isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
         throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
     }

     StringBuilder query = new StringBuilder(120);

     query.append("SELECT ");
     if (distinct) {
         query.append("DISTINCT ");
     }
     if (columns != null && columns.length != 0) {
         appendColumns(query, columns);
     } else {
         query.append(" * ");
     }
     query.append("FROM ");
     query.append(tables);
     appendClause(query, " WHERE ", where);
     appendClause(query, " GROUP BY ", groupBy);
     appendClause(query, " HAVING ", having);
     appendClause(query, " ORDER BY ", orderBy);
     appendClause(query, " LIMIT ", limit);
     return query.toString();
 }

 /**
  * Add the names that are non-null in columns to s, separating
  * them with commas.
  */
 private static void appendColumns(StringBuilder s, String[] columns) {
     int n = columns.length;

     for (int i = 0; i < n; i++) {
         String column = columns[i];

         if (column != null) {
             if (i > 0) {
                 s.append(", ");
             }
             s.append(column);
         }
     }
     s.append(' ');
 }

 /**
  * addClause
  *
  * @param s      the add StringBuilder
  * @param name   clauseName
  * @param clause clauseSelection
  */
 private static void appendClause(StringBuilder s, String name, String clause) {
     if (!isEmpty(clause)) {
         s.append(name);
         s.append(clause);
     }
 }

 /**
  * Returns true if the string is null or 0-length.
  *
  * @param str the string to be examined
  * @return true if str is null or zero length
  */
 private static boolean isEmpty(@Nullable CharSequence str) {
     if (str == null || str.length() == 0)
         return true;
     else
         return false;
 }

 /**
  * the pattern of limit
  */
 private static final Pattern sLimitPattern =
         Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");

 /**
  * After the execution of the complete SQL statement, not necessarily the actual implementation of the SQL statement
  *
  * @param sql      SQL statement
  * @param bindArgs Binding parameters
  * @return Replace? SQL statement executed after the
  */
 private static String getExecSQL(String sql, Object[] bindArgs) {
     StringBuilder sb = new StringBuilder(sql);
     if (bindArgs != null && bindArgs.length > 0) {
         int index = 0;
         for (int i = 0; i < bindArgs.length; i++) {
             index = sb.indexOf("?", index);
             sb.replace(index, index + 1, String.valueOf(bindArgs[i]));
         }
     }
     return sb.toString();
 }
}複製程式碼

九.用法

  • 測試程式碼
import util.DBUtil;

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

/**
* Created by yuandl on 2016-12-16.
*/
public class DBTest {

public static void main(String[] args) {
    System.out.println("資料庫的原資料");
    testQuery3();
    testInsert();
    System.out.println("執行插入後的資料");
    testQuery3();
    testUpdate();
    System.out.println("執行修改後的資料");
    testQuery3();
    testDelete();
    System.out.println("執行刪除後的資料");
    testQuery3();
    System.out.println("帶條件的查詢1");
    testQuery2();
    System.out.println("帶條件的查詢2");
    testQuery1();
}



/**
 * 測試插入
 */
private static void testInsert() {
    Map<String, Object> map = new HashMap<>();
    map.put("emp_id", 1013);
    map.put("name", "JDBCUtil測試");
    map.put("job", "developer");
    map.put("salary", 10000);
    map.put("hire_date", new java.sql.Date(System.currentTimeMillis()));
    try {
        int count = DBUtil.insert("emp_test", map);
              } catch (SQLException e) {
        e.printStackTrace();
    }
}



/**
 * 測試更新
 */
private static void testUpdate() {
    Map<String, Object> map = new HashMap<>();
    map.put("name", "測試更新");

    Map<String, Object> whereMap = new HashMap<>();
    whereMap.put("emp_id", "1013");
    try {
        int count = DBUtil.update("emp_test", map, whereMap);
    } catch (SQLException e) {
        e.printStackTrace();
    }
}



/**
 * 測試刪除
 */
private static void testDelete() {
    Map<String, Object> whereMap = new HashMap<>();
    whereMap.put("emp_id", 1013);
    whereMap.put("job", "developer");
    try {
        int count = DBUtil.delete("emp_test", whereMap);
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
/**
 * 查詢方式一
 */
public static void testQuery1() {
    Map<String,Object> whereMap=new HashMap<>();
    whereMap.put("salary","10000");
    try {
        DBUtil.query("emp_test",whereMap);
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
/**
 * 查詢方式二
 */
public static void testQuery2() {
    String where = "job = ?  AND salary = ? ";
    String[] whereArgs = new String[]{"clerk", "3000"};

    try {
        List<Map<String, Object>> list = DBUtil.query("emp_test", where, whereArgs);

    } catch (SQLException e) {
        e.printStackTrace();
    }
}

/**
 * 查詢方式三
 */
public static void testQuery3() {
    try {
        List<Map<String, Object>> list = DBUtil.query("emp_test", false, null, null, null, null, null, null, null);

    } catch (SQLException e) {
        e.printStackTrace();
    }
}
}複製程式碼
  • 列印結果
資料庫的原資料
SELECT  * FROM emp_test
成功查詢到了14行資料
第1行:{DEPT_TEST_ID=10, EMP_ID=1001, SALARY=10000, HIRE_DATE=2010-01-12, BONUS=2000, MANAGER=1005, JOB=Manager, NAME=張無忌}
第2行:{DEPT_TEST_ID=10, EMP_ID=1002, SALARY=8000, HIRE_DATE=2011-01-12, BONUS=1000, MANAGER=1001, JOB=Analyst, NAME=劉蒼松}
第3行:{DEPT_TEST_ID=10, EMP_ID=1003, SALARY=9000, HIRE_DATE=2010-02-11, BONUS=1000, MANAGER=1001, JOB=Analyst, NAME=李翊}
第4行:{DEPT_TEST_ID=10, EMP_ID=1004, SALARY=5000, HIRE_DATE=2010-02-11, BONUS=null, MANAGER=1001, JOB=Programmer, NAME=郭芙蓉}
第5行:{DEPT_TEST_ID=20, EMP_ID=1005, SALARY=15000, HIRE_DATE=2008-02-15, BONUS=null, MANAGER=null, JOB=President, NAME=張三丰}
第6行:{DEPT_TEST_ID=20, EMP_ID=1006, SALARY=5000, HIRE_DATE=2009-02-01, BONUS=400, MANAGER=1005, JOB=Manager, NAME=燕小六}
第7行:{DEPT_TEST_ID=20, EMP_ID=1007, SALARY=3000, HIRE_DATE=2009-02-01, BONUS=500, MANAGER=1006, JOB=clerk, NAME=陸無雙}
第8行:{DEPT_TEST_ID=30, EMP_ID=1008, SALARY=5000, HIRE_DATE=2009-05-01, BONUS=500, MANAGER=1005, JOB=Manager, NAME=黃蓉}
第9行:{DEPT_TEST_ID=30, EMP_ID=1009, SALARY=4000, HIRE_DATE=2009-02-20, BONUS=null, MANAGER=1008, JOB=salesman, NAME=韋小寶}
第10行:{DEPT_TEST_ID=30, EMP_ID=1010, SALARY=4500, HIRE_DATE=2009-05-10, BONUS=500, MANAGER=1008, JOB=salesman, NAME=郭靖}
第11行:{DEPT_TEST_ID=null, EMP_ID=1011, SALARY=null, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=於澤成}
第12行:{DEPT_TEST_ID=null, EMP_ID=1012, SALARY=null, HIRE_DATE=2011-08-10, BONUS=null, MANAGER=null, JOB=null, NAME=amy}
第13行:{DEPT_TEST_ID=null, EMP_ID=1014, SALARY=8000, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=張無忌}
第14行:{DEPT_TEST_ID=20, EMP_ID=1015, SALARY=null, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=劉蒼松}

INSERT INTO emp_test (name,hire_date,job,salary,emp_id )  VALUES (JDBCUtil測試,2016-12-17,developer,10000,1013 )
成功新增了1行

執行插入後的資料
SELECT  * FROM emp_test
成功查詢到了15行資料
第1行:{DEPT_TEST_ID=10, EMP_ID=1001, SALARY=10000, HIRE_DATE=2010-01-12, BONUS=2000, MANAGER=1005, JOB=Manager, NAME=張無忌}
第2行:{DEPT_TEST_ID=10, EMP_ID=1002, SALARY=8000, HIRE_DATE=2011-01-12, BONUS=1000, MANAGER=1001, JOB=Analyst, NAME=劉蒼松}
第3行:{DEPT_TEST_ID=10, EMP_ID=1003, SALARY=9000, HIRE_DATE=2010-02-11, BONUS=1000, MANAGER=1001, JOB=Analyst, NAME=李翊}
第4行:{DEPT_TEST_ID=10, EMP_ID=1004, SALARY=5000, HIRE_DATE=2010-02-11, BONUS=null, MANAGER=1001, JOB=Programmer, NAME=郭芙蓉}
第5行:{DEPT_TEST_ID=20, EMP_ID=1005, SALARY=15000, HIRE_DATE=2008-02-15, BONUS=null, MANAGER=null, JOB=President, NAME=張三丰}
第6行:{DEPT_TEST_ID=20, EMP_ID=1006, SALARY=5000, HIRE_DATE=2009-02-01, BONUS=400, MANAGER=1005, JOB=Manager, NAME=燕小六}
第7行:{DEPT_TEST_ID=20, EMP_ID=1007, SALARY=3000, HIRE_DATE=2009-02-01, BONUS=500, MANAGER=1006, JOB=clerk, NAME=陸無雙}
第8行:{DEPT_TEST_ID=30, EMP_ID=1008, SALARY=5000, HIRE_DATE=2009-05-01, BONUS=500, MANAGER=1005, JOB=Manager, NAME=黃蓉}
第9行:{DEPT_TEST_ID=30, EMP_ID=1009, SALARY=4000, HIRE_DATE=2009-02-20, BONUS=null, MANAGER=1008, JOB=salesman, NAME=韋小寶}
第10行:{DEPT_TEST_ID=30, EMP_ID=1010, SALARY=4500, HIRE_DATE=2009-05-10, BONUS=500, MANAGER=1008, JOB=salesman, NAME=郭靖}
第11行:{DEPT_TEST_ID=null, EMP_ID=1011, SALARY=null, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=於澤成}
第12行:{DEPT_TEST_ID=null, EMP_ID=1012, SALARY=null, HIRE_DATE=2011-08-10, BONUS=null, MANAGER=null, JOB=null, NAME=amy}
第13行:{DEPT_TEST_ID=null, EMP_ID=1014, SALARY=8000, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=張無忌}
第14行:{DEPT_TEST_ID=20, EMP_ID=1015, SALARY=null, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=劉蒼松}
第15行:{DEPT_TEST_ID=null, EMP_ID=1013, SALARY=10000, HIRE_DATE=2016-12-17, BONUS=null, MANAGER=null, JOB=developer, NAME=JDBCUtil測試}

UPDATE emp_test SET name = 測試更新  WHERE emp_id = 1013 
成功修改了1行

執行修改後的資料
SELECT  * FROM emp_test
成功查詢到了15行資料
第1行:{DEPT_TEST_ID=10, EMP_ID=1001, SALARY=10000, HIRE_DATE=2010-01-12, BONUS=2000, MANAGER=1005, JOB=Manager, NAME=張無忌}
第2行:{DEPT_TEST_ID=10, EMP_ID=1002, SALARY=8000, HIRE_DATE=2011-01-12, BONUS=1000, MANAGER=1001, JOB=Analyst, NAME=劉蒼松}
第3行:{DEPT_TEST_ID=10, EMP_ID=1003, SALARY=9000, HIRE_DATE=2010-02-11, BONUS=1000, MANAGER=1001, JOB=Analyst, NAME=李翊}
第4行:{DEPT_TEST_ID=10, EMP_ID=1004, SALARY=5000, HIRE_DATE=2010-02-11, BONUS=null, MANAGER=1001, JOB=Programmer, NAME=郭芙蓉}
第5行:{DEPT_TEST_ID=20, EMP_ID=1005, SALARY=15000, HIRE_DATE=2008-02-15, BONUS=null, MANAGER=null, JOB=President, NAME=張三丰}
第6行:{DEPT_TEST_ID=20, EMP_ID=1006, SALARY=5000, HIRE_DATE=2009-02-01, BONUS=400, MANAGER=1005, JOB=Manager, NAME=燕小六}
第7行:{DEPT_TEST_ID=20, EMP_ID=1007, SALARY=3000, HIRE_DATE=2009-02-01, BONUS=500, MANAGER=1006, JOB=clerk, NAME=陸無雙}
第8行:{DEPT_TEST_ID=30, EMP_ID=1008, SALARY=5000, HIRE_DATE=2009-05-01, BONUS=500, MANAGER=1005, JOB=Manager, NAME=黃蓉}
第9行:{DEPT_TEST_ID=30, EMP_ID=1009, SALARY=4000, HIRE_DATE=2009-02-20, BONUS=null, MANAGER=1008, JOB=salesman, NAME=韋小寶}
第10行:{DEPT_TEST_ID=30, EMP_ID=1010, SALARY=4500, HIRE_DATE=2009-05-10, BONUS=500, MANAGER=1008, JOB=salesman, NAME=郭靖}
第11行:{DEPT_TEST_ID=null, EMP_ID=1011, SALARY=null, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=於澤成}
第12行:{DEPT_TEST_ID=null, EMP_ID=1012, SALARY=null, HIRE_DATE=2011-08-10, BONUS=null, MANAGER=null, JOB=null, NAME=amy}
第13行:{DEPT_TEST_ID=null, EMP_ID=1014, SALARY=8000, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=張無忌}
第14行:{DEPT_TEST_ID=20, EMP_ID=1015, SALARY=null, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=劉蒼松}
第15行:{DEPT_TEST_ID=null, EMP_ID=1013, SALARY=10000, HIRE_DATE=2016-12-17, BONUS=null, MANAGER=null, JOB=developer, NAME=測試更新}

DELETE FROM emp_test WHERE job = developer  AND emp_id = 1013 
成功刪除了1行

執行刪除後的資料
SELECT  * FROM emp_test
成功查詢到了14行資料
第1行:{DEPT_TEST_ID=10, EMP_ID=1001, SALARY=10000, HIRE_DATE=2010-01-12, BONUS=2000, MANAGER=1005, JOB=Manager, NAME=張無忌}
第2行:{DEPT_TEST_ID=10, EMP_ID=1002, SALARY=8000, HIRE_DATE=2011-01-12, BONUS=1000, MANAGER=1001, JOB=Analyst, NAME=劉蒼松}
第3行:{DEPT_TEST_ID=10, EMP_ID=1003, SALARY=9000, HIRE_DATE=2010-02-11, BONUS=1000, MANAGER=1001, JOB=Analyst, NAME=李翊}
第4行:{DEPT_TEST_ID=10, EMP_ID=1004, SALARY=5000, HIRE_DATE=2010-02-11, BONUS=null, MANAGER=1001, JOB=Programmer, NAME=郭芙蓉}
第5行:{DEPT_TEST_ID=20, EMP_ID=1005, SALARY=15000, HIRE_DATE=2008-02-15, BONUS=null, MANAGER=null, JOB=President, NAME=張三丰}
第6行:{DEPT_TEST_ID=20, EMP_ID=1006, SALARY=5000, HIRE_DATE=2009-02-01, BONUS=400, MANAGER=1005, JOB=Manager, NAME=燕小六}
第7行:{DEPT_TEST_ID=20, EMP_ID=1007, SALARY=3000, HIRE_DATE=2009-02-01, BONUS=500, MANAGER=1006, JOB=clerk, NAME=陸無雙}
第8行:{DEPT_TEST_ID=30, EMP_ID=1008, SALARY=5000, HIRE_DATE=2009-05-01, BONUS=500, MANAGER=1005, JOB=Manager, NAME=黃蓉}
第9行:{DEPT_TEST_ID=30, EMP_ID=1009, SALARY=4000, HIRE_DATE=2009-02-20, BONUS=null, MANAGER=1008, JOB=salesman, NAME=韋小寶}
第10行:{DEPT_TEST_ID=30, EMP_ID=1010, SALARY=4500, HIRE_DATE=2009-05-10, BONUS=500, MANAGER=1008, JOB=salesman, NAME=郭靖}
第11行:{DEPT_TEST_ID=null, EMP_ID=1011, SALARY=null, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=於澤成}
第12行:{DEPT_TEST_ID=null, EMP_ID=1012, SALARY=null, HIRE_DATE=2011-08-10, BONUS=null, MANAGER=null, JOB=null, NAME=amy}
第13行:{DEPT_TEST_ID=null, EMP_ID=1014, SALARY=8000, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=張無忌}
第14行:{DEPT_TEST_ID=20, EMP_ID=1015, SALARY=null, HIRE_DATE=null, BONUS=null, MANAGER=null, JOB=null, NAME=劉蒼松}

帶條件的查詢1
SELECT  * FROM emp_test WHERE job = clerk  AND salary = 3000 
成功查詢到了1行資料
第1行:{DEPT_TEST_ID=20, EMP_ID=1007, SALARY=3000, HIRE_DATE=2009-02-01, BONUS=500, MANAGER=1006, JOB=clerk, NAME=陸無雙}

帶條件的查詢2
SELECT  * FROM emp_test WHERE salary = 10000 
成功查詢到了1行資料
第1行:{DEPT_TEST_ID=10, EMP_ID=1001, SALARY=10000, HIRE_DATE=2010-01-12, BONUS=2000, MANAGER=1005, JOB=Manager, NAME=張無忌}複製程式碼

十.GitHub

相關文章