Statement和preparedStatement之間的區別?
阿新 • • 發佈:2020-09-11
根據JDBC模擬使用者功能的實現:
loginUser.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/mydatabase user=root password=333
package com.java.JDBC; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import java.util.Scanner; /* 實現功能: 1 需求:模擬使用者登入功能的實現。 2 業務描述: 程式執行的時候,提供一個輸入的入口,可以讓使用者輸入使用者名稱和密碼。 使用者輸入使用者名稱和密碼之後,提交資訊,java程式收集使用者資訊 Java程式連線資料庫驗證使用者名稱和密碼是否合法。 合法:顯示登陸成功。 不合法:顯示登陸失敗。 3 資料的準備 在實際開發中,表的設計會使用專業的建模工具,我們這裡安裝一個建模工具:PowerDesigner 使用PD工具來進行資料庫表的設計。(參見user-login.sql指令碼) 4 當前程式存在的問題: 使用者名稱:fdsa 密碼:fdsa' or '1'='1 登入成功 這種現象被稱為SQL注入(安全隱患)。(黑客經常使用) 5 導致SQL注入的根本原因是什麼? select * from t_user where loginName = 'fdsa' and loginpwd = 'fdsa' or '1'='1'; 使用者輸入的資訊中含有sql語句的關鍵字,並且這些關鍵字參與sql語句的編譯過程,導致sql語句的原意被扭曲,從而達到sql注入。*/ public class JDBCTest06 { public static void main(String[] args) { // 初始化一個介面 Map<String,String> userLoginInfo = initUI(); // 驗證使用者名稱和密碼 boolean loginSuccess = login(userLoginInfo); System.out.println(loginSuccess == true ? "登陸成功" : "登陸失敗"); } /*** 使用者登入 * @param userLoginInfo 使用者登入資訊 * @return true表示成功 false表示失敗 */ private static boolean login(Map<String, String> userLoginInfo) { // JDBC程式碼 Connection conn = null; Statement stmt = null; ResultSet rs = null; boolean result = false; ResourceBundle rb = ResourceBundle.getBundle("loginUser"); String driver = rb.getString("driver"); String url = rb.getString("url"); String user = rb.getString("user"); String password = rb.getString("password"); String loginName = userLoginInfo.get("loginName"); String loginpwd = userLoginInfo.get("password"); try { // 1 註冊驅動 // Class.forName("com.mysql.jdbc.Driver"); Class.forName(driver); // 2 獲取連線 // conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","333"); conn = DriverManager.getConnection(url,user,password); // 3 獲取資料庫操作物件 stmt = conn.createStatement(); // 4 執行sql語句 String sql = "select * from t_user where loginName = '" + loginName + "' and loginpwd = '" + loginpwd +"'"; // 以上正好完成了sql語句的拼接,以下程式碼的含義是,傳送sql語句給DBMS進行sql編譯。 // 正好將使用者提供的“非法資訊”編譯進去。導致了原sql語句的含義被扭曲了。 System.out.println(sql); rs = stmt.executeQuery(sql); // 5 處理返回結果集 if (rs.next()){ result = true; } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { // 6 釋放資源 if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } return result; } /** * 初始化使用者介面 * @return 使用者輸入的使用者名稱和密碼等登陸資訊 */ private static Map<String, String> initUI() { Scanner scanner = new Scanner(System.in); System.out.print("使用者名稱: "); String username = scanner.nextLine(); System.out.print("密碼: "); String password = scanner.nextLine(); Map<String,String> userLoginInfo = new HashMap<>(); userLoginInfo.put("loginName",username); userLoginInfo.put("password",password); return userLoginInfo; } }
案例7:如何解決JDBC注入問題及Statement和PreparedStatement之間的區別?
package com.java.JDBC; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import java.util.Scanner; /* 1 解決sql注入問題 只要使用者提供的資訊不參與sql語句的編譯過程,問題就解決了。 即使使用者提供的資訊中心含有sql語句的關鍵字,但是沒有參與編譯,不起作用。 要想使用者資訊不參與sql語句的編譯,必須使用java.sql.PreparedStatement PreparedStatement介面繼承了java.sql.Statement PreparedStatement是屬於預編譯的資料庫作業系統 PreparedStatement原理是:預先對sql語句的框架進行編譯,然後再給sql語句傳“值” 2 解決sql注入的關鍵是什麼? 使用者提供的資訊中即使含有sql語句的關鍵字,但是這些關鍵字並沒有參與編譯,不起作用。 3 對比一下Statement和PreparedStatement? - Statement存在sql注入問題,PreparedStatement解決了sql注入問題。 - Statement是編譯一次執行一次,PreparedStatement是編譯一次,可執行n次。PreparedStatement效率較高一些。 - PreparedStatement會在編譯階段做型別的安全檢查。 綜上所述:PreparedStatement使用較多,只有極少數的情況下需要使用Statement 4 什麼情況下必須使用Statement呢? 業務方面要求必須支援SQL注入的時候。 Statement支援sql注入,凡是業務方面要求是需要進行sql語句拼接的,必須使用Statement。 */ public class JDBCTest07 { public static void main(String[] args) { // 初始化一個介面 Map<String,String> userLoginInfo = initUI(); // 驗證使用者名稱和密碼 boolean loginSuccess = login(userLoginInfo); System.out.println(loginSuccess == true ? "登陸成功" : "登陸失敗"); } /** * 使用者登入 * @param userLoginInfo 使用者登入資訊 * @return true表示成功 false表示失敗 */ private static boolean login(Map<String, String> userLoginInfo) { // JDBC程式碼 Connection conn = null; PreparedStatement ps = null; // 這裡使用PreparedStatement ResultSet rs = null; boolean result = false; ResourceBundle rb = ResourceBundle.getBundle("loginUser"); String driver = rb.getString("driver"); String url = rb.getString("url"); String user = rb.getString("user"); String password = rb.getString("password"); String loginName = userLoginInfo.get("loginName"); String loginpwd = userLoginInfo.get("password"); try { // 1 註冊驅動 // Class.forName("com.mysql.jdbc.Driver"); Class.forName(driver); // 2 獲取連線 // conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","333"); conn = DriverManager.getConnection(url,user,password); // 3 獲取預編譯的資料庫操作物件 // sql語句的框子,其中一個?,表示一個佔位符,一個?將來接收一個“值”,注意:佔位符不能使用單引號括起來 String sql = "select * from t_user where loginName = ? and loginpwd = ?"; // 程式執行到此處,會發送slq語句框子給DBMS,然後DBMS進行sql語句的預先編譯。 ps = conn.prepareStatement(sql); // 給佔位符?傳值(第1個問號下標是1,第2個問號下標是2,JDBC中所有下標從1開始) ps.setString(1,loginName); ps.setString(2,loginpwd); // 4 執行sql語句 rs = ps.executeQuery(); // 5 處理返回結果集 if (rs.next()){ result = true; } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { // 6 釋放資源 if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } return result; } /** * 初始化使用者介面 * @return 使用者輸入的使用者名稱和密碼等登陸資訊 */ private static Map<String, String> initUI() { Scanner scanner = new Scanner(System.in); System.out.print("使用者名稱: "); String username = scanner.nextLine(); System.out.print("密碼: "); String password = scanner.nextLine(); Map<String,String> userLoginInfo = new HashMap<>(); userLoginInfo.put("loginName",username); userLoginInfo.put("password",password); return userLoginInfo; } }案例8:使用Statement可以對查詢結果集進行升序和降序,但是用PreparedStatement不行,因為他會預編譯。
package com.java.JDBC; import java.sql.*; import java.util.Scanner; public class JDBCTest08 { public static void main(String[] args) { /*// 使用者在控制檯輸入desc就是降序,輸入asc就是升序 Scanner s = new Scanner(System.in); System.out.println("請輸入desc或asc,desc表示降序,asc表示升序"); System.out.print("請輸入: "); String keyWords = s.nextLine(); // 執行sql Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { // 註冊驅動 Class.forName("com.mysql.jdbc.Driver"); // 獲取資料庫連線 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","333"); // 獲取資料庫物件 String sql = "select ename from emp order by ename ?"; ps = conn.prepareStatement(sql); // 執行sql語句 ps.setString(1,keyWords); // 處理查詢結果集 rs = ps.executeQuery(); // 遍歷結果集 while (rs.next()){ String ename = rs.getString("ename"); System.out.println(ename); } } catch (Exception e) { e.printStackTrace(); } finally { // 釋放資源 if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }*/ // 使用者在控制檯輸入desc就是降序,輸入asc就是升序 Scanner s = new Scanner(System.in); System.out.println("請輸入desc或asc,desc表示降序,asc表示升序"); System.out.print("請輸入: "); String keyWords = s.nextLine(); // 執行sql Connection conn = null; Statement stmt = null; ResultSet rs = null; try { // 註冊驅動 Class.forName("com.mysql.jdbc.Driver"); // 獲取資料庫連線 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","333"); // 獲取資料庫物件 stmt = conn.createStatement(); // 執行sql語句 String sql = "select ename from emp order by ename " + keyWords; // 處理查詢結果集 rs = stmt.executeQuery(sql); // 遍歷結果集 while (rs.next()){ String ename = rs.getString("ename"); System.out.println(ename); } } catch (Exception e) { e.printStackTrace(); } finally { // 釋放資源 if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }