第28次文章:簡單了解JDBC(續上周)

  • 2019 年 10 月 8 日
  • 筆記

這周的學習緊接上周學習文章哈!


在上次文章的末尾,我們提到了使用Statement介面時,可能發生SQL注入,不建議各位同學使用,為了解決SQL注入問題,我們使用另一種介面PreparedStatement()。(詳細情況請看上一篇文章:第27次文章:簡單了解JDBC)。

下面我們直接給出測試程式碼:

import com.mysql.jdbc.Connection;  /**   * 測試preparedStatement的基本用法   */  public class Demo03 {    public static void main(String[] args) {      Connection conn = null;      PreparedStatement ps = null;      try {        //載入驅動類        Class.forName("com.mysql.jdbc.Driver");        conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc", "root", "123456");          String sql = "insert into t_user1 (username,pwd,regTime) value (?,?,?)";        ps = conn.prepareStatement(sql);  //      ps.setString(1, "peng");//參數索引是從1開始,而不是0  //      ps.setString(2, "123456");  //      ps.setDate(3, new java.sql.Date(System.currentTimeMillis()));//獲取當前時間        System.out.println("插入一條語句");          ps.setObject(1, "peng");        ps.setObject(2, "123456");        ps.setObject(3, new java.sql.Date(System.currentTimeMillis()));//獲取當前時間        } catch (ClassNotFoundException e) {        e.printStackTrace();      } catch (SQLException e) {        e.printStackTrace();      }finally {        try {          if(conn != null) {            conn.close();          }        } catch (SQLException e) {          e.printStackTrace();        }        try {          if(ps != null) {            ps.close();          }        } catch (SQLException e) {          e.printStackTrace();        }      }    }  }

tips:

1.當我們將Statement介面轉換為PreparedStatement之後,我們在傳入參數的時候,使用的不再是拼字元串的方法,而是在SQL命令中的參數位置加入「?」,「?」代表著佔位符。這就屬於PreparedStatement防止SQL注入的關鍵所在。正如我們在注釋掉的上段程式碼中寫的那樣,使用PreparedStatement對象ps的setString,setDate等等方法來向每一個佔位符的位置傳遞參數,此時,我們可以通過對傳遞的參數進行預判斷,判斷傳入的參數是否符合String,int類型等等,這樣就防止了向SQL語句中傳入惡意指令情況的發生。

2.在向SQL語句中輸入參數的時候,我們不但可以使用setXXX的方法,還可以直接使用setObject()的方法傳遞參數,此時就可以不用考慮不同類型參數的問題了,全部當做Object類型進行傳遞。

3.在使用setDate()方法的時候,需要使用資料庫中的時間類型java.sql.Date,需要注意的是,我們傳入的時間類型並不是java中的Date類型。

(5)Result介面

-Statement執行SQL語句返回Result結果集。

-Result提供的檢索不同類型欄位的方法,常用的有:

  • getString():獲得在資料庫里是varchar、char等資料庫類型的對象
  • getFloa():獲得資料庫里是Float類型的對象
  • getDate():獲得資料庫里是Date類型的對象
  • getBoolean():獲得資料庫里是Boolean類型的對象

測試Result介面:

import com.mysql.jdbc.Connection;  /**   * 測試ResultSet結果集的基本用法   */  public class Demo04 {    public static void main(String[] args) {      Connection conn = null;      PreparedStatement ps = null;      ResultSet rs = null;      try {        //載入驅動類        Class.forName("com.mysql.jdbc.Driver");        conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc", "root", "123456");        String sql = "select id,username,pwd from t_user where id>?";        ps = conn.prepareStatement(sql);        ps.setObject(1, 1);//取出所有id大於等於2的記錄        rs = ps.executeQuery();        while(rs.next()) {          System.out.println(rs.getInt(1)+"---"+rs.getString(2)+"---"+rs.getInt(3));//一次取出第1列,第2列,第3列        }      } catch (ClassNotFoundException e) {        e.printStackTrace();      } catch (SQLException e) {        e.printStackTrace();      }finally {        try {          if(conn != null) {            conn.close();          }        } catch (SQLException e) {          e.printStackTrace();        }        try {          if(ps != null) {            ps.close();          }        } catch (SQLException e) {          e.printStackTrace();        }        try {          if(rs != null) {            rs.close();          }        } catch (SQLException e) {          e.printStackTrace();        }      }    }  }

tips:

1.在這段程式碼中,我們依然使用PreparedStatement類型進行傳輸參數,使用「?」佔位符,向佔位符中傳遞我們需要大於的參數值。

2.在我們使用Result介面的時候,我們可以將其類比為一個容器,接納所返回id大於2的結果。再編寫一個while循環將結果集中的內容輸出。其中的rs.next()方法類似於迭代器中的hasNext()方法。

(6)依序關閉使用的對象及連接

Result——>Statement——>Connection

(7)批處理

-Batch

-對於大量的批處理,建議使用Statement,因為PrepareStatement的預編譯空間有限,當數據量特別大時,會發生異常。

測試批處理操作:

import com.mysql.jdbc.Connection;  /**   * 測試批處理的基本用法   */  public class Demo05 {    public static void main(String[] args) {      Connection conn = null;      Statement stmt = null;      ResultSet rs = null;      try {        //載入驅動類        Class.forName("com.mysql.jdbc.Driver");        conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc", "root", "123456");        conn.setAutoCommit(false); //設為手動提交        long start = System.currentTimeMillis();        stmt = conn.createStatement();          for (int i=0;i<2000;i++) {          stmt.addBatch("insert into t_user (username,pwd,regTime) values ('peng"+i+"',666,now())");        }        stmt.executeBatch();        conn.commit(); //提交事務        long end = System.currentTimeMillis();        System.out.println("插入兩萬條語句,耗時(毫秒):"+(end - start));      } catch (ClassNotFoundException e) {        e.printStackTrace();      } catch (SQLException e) {        e.printStackTrace();      }finally {        try {          if(conn != null) {            conn.close();          }        } catch (SQLException e) {          e.printStackTrace();        }        try {          if(stmt != null) {            stmt.close();          }        } catch (SQLException e) {          e.printStackTrace();        }        try {          if(rs != null) {            rs.close();          }        } catch (SQLException e) {          e.printStackTrace();        }      }    }  }

結果圖:

tips:

在批處理需要注意的兩點,第一就是需要將PreparedStatement介面更換為Statement介面,另一個需要注意的點是,需要將鏈接的事務提交設為手動提交。