JDBC介面———PreparedStatement預處理
阿新 • • 發佈:2018-12-11
PreparedStatement * 它是Statement介面的子介面; * 強大之處: - 防SQL攻擊; - 提高程式碼的可讀性、可維護性; - 提高效率! * 學習PreparedStatement的用法: - 給出SQL模板! - 呼叫Connection的PreparedStatement prepareStatement(String sql模板),得到PreparedStatement物件pstmt; - 呼叫pstmt的setXxx()系列方法sql模板中的?賦值!如:pstmt.setString(1,"zhangsan"); - 呼叫pstmt的executeUpdate()或executeQuery(),但它的方法都沒有引數。 * 預處理的原理 - 伺服器的工作: 校驗sql語句的語法! 編譯:一個與函式相似的東西! 執行:呼叫函式, - PreparedStatement: 前提:連線的資料庫必須支援預處理!幾乎沒有不支援的! 每個pstmt都與一個sql模板繫結在一起,先把sql模板給資料庫,資料庫先進行校驗,再進行編譯。 執行時只是把引數傳遞過去而已! 若二次執行時,就不用再次校驗語法,也不用再次編譯!直接執行! 目錄 1、什麼是SQL攻擊 2、演示SQL攻擊 3、防止SQL攻擊 4、PreparedStatement是什麼? 5、PreparedStatement的使用 -------------------------------------------------------------------- 1、什麼是SQL攻擊 在需要使用者輸入的地方,使用者輸入的是SQL語句的片段,終端使用者輸入的SQL片段與我們DAO中寫的SQL語句合成一個完整的SQL語句! 例如使用者在登入時輸入的使用者名稱和密碼都是為SQL語句的片段! 2、演示SQL攻擊 首先我們需要建立一張使用者表,用來儲存使用者的資訊。 CREATE TABLE user( uid CHAR(32) PRIMARY KEY, username VARCHAR(30) UNIQUE KEY NOT NULL, PASSWORD VARCHAR(30) ); INSERT INTO user VALUES('U_1001', 'zs', 'zs'); SELECT * FROM user; 現在使用者表中只有一行記錄,就是zs。 下面我們寫一個login()方法! public boolean login(String username, String password) throws Exception { /* * 一、得到Connection * 二、得到Statement * 三、得到ResultSet * 四、rs.next()返回的是什麼,我們就返回什麼 */ // 準備四大引數 String driverClassName = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://localhost:3306/mydb3"; String mysqlUsername = "root"; String mysqlPassword = "123"; // 載入驅動類 Class.forName(driverClassName); // 得到Connection Connection con = DriverManager.getConnection(url, mysqlUsername, mysqlPassword); // 得到Statement Statement stmt = con.createStatement(); // 給出sql語句,該sql受到攻擊後,查詢結果與事實不符 String sql="select * from t_user where username='" + username + "' and password='" + password + "'"; //得到ResultSet, ResultSet rs = stmt.executeQuery(sql); //返回boolean型別的:結果集是否有第一行資料。只要表裡有資料,就返回true,就能登入,嚴重問題。 return rs.next(); } SQL注入測試 @Test public void fun1() throws Exception { String username = "a' or 'a'='a"; String password = "a' or 'a'='a"; boolean bool = login(username, password); System.out.println(bool); } 這行當前會使我們登入成功!因為是輸入的使用者名稱和密碼是SQL語句片段,最終與我們的login()方法中的SQL語句組合在一起! 我們來看看組合在一起的SQL語句: select * from t_user where username='a' or 'a'='a' and password='a' or 'a'='a'; 很明顯,使用者名稱和密碼都不對,卻可以查詢出所有資料,可以登入。 3、防止SQL攻擊 * 過濾使用者輸入的資料中是否包含非法字元; * 分步交驗!先使用使用者名稱來查詢使用者,如果查詢到了,再比較密碼; * 使用PreparedStatement。 4、PreparedStatement是什麼? PreparedStatement叫預編譯宣告! PreparedStatement是Statement的子介面,你可以使用PreparedStatement來替換Statement。 PreparedStatement的好處: * 防止SQL攻擊; [不只它可以防!] * 提高程式碼的可讀性,以可維護性; * 提高效率。 [很重要!] 5、PreparedStatement的使用 * 使用Connection的prepareStatement(String sql):即建立它時就讓它與一條SQL模板繫結; * 呼叫PreparedStatement的setXXX()系列方法為問號設定值 * 呼叫executeUpdate()或executeQuery()方法,但要注意,呼叫沒有引數的方法; --------------------------------------- // 載入驅動類 Class.forName("com.mysql.jdbc.Driver"); // 得到Connection Connection con = DriverManager.getConnection(url, mysqlUsername, mysqlPassword); /* * 一、得到PreparedStatement * 1. 給出sql模板:所有的引數使用佔位符? 來替代 * 2. 呼叫Connection方法,得到PreparedStatement */ String sql = "select * from t_user where username=? and password=?"; PreparedStatement pstmt = con.prepareStatement(sql); /* * 二、為引數賦值 */ pstmt.setString(1, "zhangsan");//給第1個問號賦值,值為username pstmt.setString(2, "123");//給第2個問號賦值,值為password ResultSet rs = pstmt.executeQuery();//呼叫查詢方法,向資料庫傳送查詢語句,沒有引數。 rs.close(); //立即釋放此 ResultSet 物件的資料庫和 JDBC 資源,而不是等待該物件自動關閉時發生此操作。 pstmt.clearParameters(); // 再次使用時需要把當前引數值清除。 pstmt.setString(1, "liSi"); pstmt.setString(2, "123"); pstmt.executeQuery(); --------------------------------------- 在使用Connection建立PreparedStatement物件時需要給出一個SQL模板,所謂SQL模板就是有"?"的SQL語句,其中"?"就是引數。 在得到PreparedStatement物件後,呼叫它的setXXX()方法為"?"賦值,這樣就可以得到把模板變成一條完整的SQL語句, 然後再呼叫PreparedStatement物件的executeQuery()方法獲取ResultSet物件。 注意: Statement的 ResultSet executeQuery(String sql)、int executeUpdate(String sql)方法是需要引數(SQL語句)的; PreparedStatement物件獨有的 ResultSet executeQuery()、int executeUpdate() 方法是沒有引數的。 因為在建立PreparedStatement物件時已經讓它與一條SQL模板繫結在一起了, 所以在呼叫它的executeQuery()和executeUpdate()方法時就不再需要引數了。 PreparedStatement最大的好處就是在於重複使用同一模板,給予其不同的引數來重複的使用它。這才是真正提高效率的原因。 所以,建議大家在今後的開發中,無論什麼情況,都去需要PreparedStatement,而不是使用Statement。