JDBC--使用PrepareStatement

1、PrepareStatement

PrepareStatement是 Statement的子接口,能夠傳入帶佔位符的 SQL語句,提供了補充佔位符變量的方法。

使用 prepareStatement:java

1. 建立 PrepareStatement對象sql

  • String sql = "INSERT INTO Student VALUES(?,?,?,?,?,?)";
  • PrepareStatement ps = conn.prepareStatement(sql);

2. 調用 PrepareStatement的 setXxx(int index, Object val) 設置佔位符的值,索引值從1開始。
3. 執行 SQL語句: executeQuery() 或 executeUpdate().。執行時不須要再傳入 SQL 語句。數據庫

2、使用帶佔位符的SQL語句

/**
     * PrepareStatement是 Statement的子接口,能夠傳入帶佔位符的 SQL語句
     * 提供了補充佔位符變量的方法。
     *
     * 使用 prepareStatement:
     * 1. 建立 PrepareStatement對象
     *    String sql = "INSERT INTO Student VALUES(?,?,?,?,?,?)";
     *    PrepareStatement ps = conn.prepareStatement(sql);
     * 2. 調用 PrepareStatement的 setXxx(int index, Object val) 設置佔位符的值
     *    索引值從1開始。
     * 3. 執行 SQL語句: executeQuery() 或 executeUpdate().
     *    執行時不須要再傳入 SQL 語句。
     */
    @Test
    public void test8() {
        Connection conn = null;
        PreparedStatement preparedStatement = null;
         try {
             //獲取數據庫鏈接
             conn = getConnection2();
             //設置 SQL語句
             String sql = "INSERT INTO Student(Sno, Sname, Ssex, Sage, Sdept, S_entrance) " +
                          "VALUES(?,?,?,?,?,?)";
             //爲佔位符賦值
             preparedStatement = conn.prepareStatement(sql);
             preparedStatement.setString(1, "201415411");
             preparedStatement.setString(2, "李明");
             preparedStatement.setString(3, "男");
             preparedStatement.setInt(4, 20);
             preparedStatement.setString(5, "CS");
             preparedStatement.setDate(6, Date.valueOf("2014-09-09"));
             //執行 SQL語句
             preparedStatement.executeUpdate();
         } catch(Exception e) {
             e.printStackTrace();
         } finally {
             if(preparedStatement != null) {
                 try {
                     preparedStatement.close();
                 } catch(Exception e) {
                     e.printStackTrace();
                 }
             }
             if(conn != null) {
                 try {
                     conn.close();
                 } catch(Exception e) {
                     e.printStackTrace();
                 }
             }
         }
    }


    //getConnection2()方法
    public Connection getConnection2() throws Exception {
        //1.準備鏈接數據庫的4個字符串
        //1.1 建立Properties對象
        Properties properties = new Properties();
        //1.2 獲取jdbc.properties對應的輸入流
        java.io.InputStream in = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
        //1.3 加載文件
        properties.load(in);
        //1.4 給字符串賦值
        String driver = properties.getProperty("driver");
        String jdbcUrl = properties.getProperty("jdbcUrl");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        //2.加載數據庫驅動程序(對應的Driver實現類中有註冊驅動的靜態代碼塊)
        Class.forName(driver);
        //3.經過DriverManager的getConnection方法獲取數據庫鏈接
        return DriverManager.getConnection(jdbcUrl, user, password);
    }

3、SQL 注入攻擊

SQL 注入是利用某些系統沒有對用戶輸入的數據進行充分的檢查,而在用戶輸入數據中注入非法的 SQL 語句段或命令,從而利用系統的 SQL 引擎完成惡意行爲的作法。緩存

對於 Java 而言,要防範 SQL 注入,只要用 PreparedStatement 取代 Statement 就能夠了。性能優化

以下程序,使用Statement,有SQL注入風險函數

/**
     * 用戶表中有兩列:用戶username和密碼password
     * 只有當用戶和密碼都正確時,登錄成功,不然不成功
     * 
     */
    @Test
    public void test9() {
        //當用戶名和密碼設置以下,表中不存在這個用戶名和密碼,可是任然能夠登陸成功
        String username = "a' OR password = ";
        String userpassword = " OR '1'='1";

        String sql = "SELECT * FROM Users WHERE username = '" +
                     username +"' AND " +
                     "password= '" + userpassword + "'";
        Connection conn = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            conn = getConnection2();
            statement = conn.createStatement();
            resultSet = statement.executeQuery(sql);
            if(resultSet.next()) {
                System.out.println("登錄成功!");
            } else {
                System.out.println("用戶名和密碼不匹配或用戶不存在");
            }
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            if(resultSet != null) {
                try {
                    resultSet.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
            if(statement != null) {
                try {
                    statement.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
            if(conn != null) {
                try {
                    conn.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

當用戶名和密碼這樣設置時:性能

String username = "a' OR password = ";
String userpassword = " OR '1'='1";學習

儘管表中沒有這個用戶名和密碼,但依然能夠登陸,此時SQL語句爲:優化

SELECT * FROM Student WHERE username = 'a' OR password = ' AND Sno= ' OR '1'='1'this

如上,username的值爲 a,password的值爲 AND Sno=。整個WHERE條件是或條件判斷,而 '1'='1' 是恆成立的,因此這條語句會執行。

使用PreparedStatement,將有效的解決注入問題

/**
     * 使用 PreparedStatement將有效的解決注入問題
     *
     */
    @Test
    public void test10() {
        String username = "a' OR Sno = ";
        String userpassword = " OR '1'='1";

        String sql = "SELECT * FROM Users WHERE username = ?" +
                " AND password= ?";

        Connection conn = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            conn = getConnection2();
            preparedStatement = conn.prepareStatement(sql);

            preparedStatement.setString(1, username);
            preparedStatement.setString(2, userpassword);
            resultSet = preparedStatement.executeQuery();
            if(resultSet.next()) {
                System.out.println("登錄成功!");
            } else {
                System.out.println("用戶名和密碼不匹配或用戶不存在");
            }
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            if(resultSet != null) {
                try {
                    resultSet.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
            if(preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
            if(conn != null) {
                try {
                    conn.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

4、PreparedStatement 能最大可能提升性能

  • DBServer會對預編譯語句提供性能優化。由於預編譯語句有可能被重複調用,因此語句在被DBServer的編譯器編譯後的執行代碼被緩存下來,那麼下次調用時只要是相同的預編譯語句就不須要編譯,只要將參數直接傳入編譯過的語句執行代碼中就會獲得執行。
  • 在statement語句中,即便是相同操做但由於數據內容不同,因此整個語句自己不能匹配,沒有緩存語句的意義.事實是沒有數據庫會對普通語句編譯後的執行代碼緩存.這樣每執行一次都要對傳入的語句編譯一次. 
  • (語法檢查,語義檢查,翻譯成二進制命令,緩存)

 

 

JDBC學習筆記:

1. 獲取數據庫鏈接    http://my.oschina.net/daowuming/blog/704243

2. 經過Statement執行更新、查詢操做    http://my.oschina.net/daowuming/blog/704384

3. 使用PrepareStatement    ----當前----

4. 使用ResultSetMetaData 對象處理結果集元數據    http://my.oschina.net/daowuming/blog/704487

5. 使用DatabaseMetaData獲取數據庫信息    http://my.oschina.net/daowuming/blog/704553

6. BLOB    http://my.oschina.net/daowuming/blog/704593

7. 處理事務與隔離級別    http://my.oschina.net/daowuming/blog/704611

8. 批量處理    http://my.oschina.net/daowuming/blog/704641

9. 數據庫鏈接池    http://my.oschina.net/daowuming/blog/704700

10. 調用函數與存儲過程    http://my.oschina.net/daowuming/blog/704813

相關文章
相關標籤/搜索