1. 程式人生 > >java資料庫程式設計(5) 使用preparedStatement

java資料庫程式設計(5) 使用preparedStatement

  1. preparedStatement是一種帶有佔位符(?)的sql語句,它可以將預編譯的sql語句儲存起來,然後可以使用這個預編譯好的sql語句多次高效地執行傳入具體引數的sql語句。
  2. 例如執行 insert into table xxx values(1);  insert into table xxx values(2); ... 這樣結構相同,只是具體的資料不通過的語句,就可以使用PreparedStatement物件將一個insert into table xxx valuse(?);預編譯好,然後再在具體執行的時候呼叫PreparedMent物件的setInt(1,x);方法傳入具體的值。   這裡的1是指第一個?。
  3. 具體的講解穿插在程式碼中。
    import java.io.FileInputStream;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.Statement;
    import java.util.Properties;
    
    public class PreparedStatementTest {
        private String driver;
        private String  url;
        private String user;
        private String pass;
    
    //    通過讀取屬性問價來獲得屬性
        public void iniParam(String paramFile) throws Exception{
            Properties properties = new Properties();
    
    //       以輸入流的方式開啟屬性檔案
            properties.load(new FileInputStream(paramFile));
    
    //        依次讀取屬性檔案裡的各種屬性
            driver = properties.getProperty("driver");
            url= properties.getProperty("url");
            user = properties.getProperty("user");
            pass = properties.getProperty("pass");
            Class.forName(driver);
        }
    
    //    這是通過常規的方式將一百條記錄新增到表中。
        public void insertUserStatement() throws Exception{
            long start = System.currentTimeMillis();
            try(
    //                先獲得資料庫連結Connection物件,並且通過這個資料庫連結建立Statement物件,在由statement物件來執行sql語句
                    Connection connection  = DriverManager.getConnection(url,user,pass);
                    Statement statement  = connection.createStatement()
                    ){
                for(int i=0; i<100; i++){
    //                執行一百次新增資料。
                    statement.executeUpdate("insert into insert100 values(i)");
                }
                System.out.println("使用Statement費時:" + (System.currentTimeMillis()-start));
            }
        }
    
    
    //    通過PreparedStatement來新增一百條記錄
        public void inserUserPrepare() throws Exception{
            long start = System.currentTimeMillis();
            try(
    //                PreparedStatement同樣是通過Connection物件來或獲得的。
    //                具體的構造方法是將要執行的sql語句傳進其構造方法裡。但是要將具體的資料用?代替
    //                然後再在具體執行sql語句的時候將?代替成具體的值
                    Connection connection = DriverManager.getConnection(url,user,pass);
    
                    PreparedStatement preparedStatement =
                            connection.prepareStatement("insert into insert100 values(?)"))
                    {
                        for(int i=0; i<100; i++){
                            preparedStatement.setInt(1,i);
                            preparedStatement.execute();
                        }
                        System.out.println("使用PreparedStatement費時" + (System.currentTimeMillis() - start));
            }
    
        }
    
        public static void main(String args[]) throws Exception{
            PreparedStatementTest preparedStatementTest  = new PreparedStatementTest();
            preparedStatementTest.iniParam("mysql.ini");
            preparedStatementTest.insertUserStatement();a
            preparedStatementTest.inserUserPrepare();
        }
    }
    //執行上面程式,看到以下輸出:
    //        使用Statement費時:5000
    //        使用PreparedStatement費時4094

     

  4. 使用PreparedStatement不僅可以提高效率,而且可以防止sql注入。我理解的sql注入是使用一般的sql語句處理過程中如果使用者輸入的內容不是合法資訊,但是可以和系統預先設定的sql語句部分合成一條語法和邏輯都沒有問題的sql語句。並讓這條語句執行某種非法操作。

  5. 如下面這個例子,在不使用PreparedStatement的情況下,如果使用者名稱輸入‘or ture or'這樣的內容的話,會顯示登陸成功。

    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.io.FileInputStream;
    import java.io.FileReader;
    import java.sql.*;
    import java.util.Properties;
    
    public class LoginFrame {
        private final String PROP_FILE = "mysql.ini";
        private String driver;
        private String url;
        private String user;
        private String pass;
    
        private JFrame jf = new JFrame("登陸");
        private JTextField userField = new JTextField(20);
        private JTextField passField = new JTextField(20);
        private JButton loginButton = new JButton("登入");
        public void init() throws  Exception{
            Properties connProp = new Properties();
            connProp.load(new FileInputStream(PROP_FILE));
            driver = connProp.getProperty("driver");
            url = connProp.getProperty("url");
            user = connProp.getProperty("user");
            pass = connProp.getProperty("pass");
            Class.forName(driver);
            loginButton.addActionListener(new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if(validate(userField.getText(), passField.getText())){
                        JOptionPane.showMessageDialog(jf, "登陸成功");
                    }else{
                        JOptionPane.showMessageDialog(jf, "登陸失敗");
                    }
                }
            });
            jf.add(userField, BorderLayout.NORTH);
            jf.add(passField);
            jf.add(loginButton, BorderLayout.SOUTH);
            jf.pack();
            jf.setVisible(true);
        }
        private boolean validate(String userName, String userPass){
            String sql = "select * from students where Sname = '" + userName + "' and Sno= '" + userPass + "';";
            System.out.println(sql);
            try(
                    Connection connection = DriverManager.getConnection(url, user, pass);
                    Statement statement = connection.createStatement();
                    ResultSet rs = statement.executeQuery(sql))
            {
                if(rs.next()){
                    return true;
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return false;
        }
    
    
        private boolean valicate1(String userName, String userPass){
            try(
                    Connection conn = DriverManager.getConnection(url, user, pass);
                    PreparedStatement pstmt = conn.prepareStatement(
                            "select * from students where Sname = ? and Sno = ?")){
                pstmt.setString(1, userName);
                pstmt.setString(2,userPass);
                try(
                        ResultSet rs = pstmt.executeQuery())
                {
                    if(rs.next()){
                        return true;
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return false;
        }
    
    
    
        public static void main(String args[]) throws Exception{
            new LoginFrame().init();
        }
    
    
    
    
    
    }
    

    這是因為當輸入 'or ture or'之後,“拼接而成”的sql語句為 

    select * from students where Sname = '‘or true or’' and Sno= '';
    

    這樣的語句是返回true的。

  6. 當使用了PaparedStatememt之後,使用者輸入的內容只能是代替了‘’裡的內容 ,所以也就不存在sql注入了。

    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.io.FileInputStream;
    import java.io.FileReader;
    import java.sql.*;
    import java.util.Properties;
    
    public class LoginFrame {
        private final String PROP_FILE = "mysql.ini";
        private String driver;
        private String url;
        private String user;
        private String pass;
    
        private JFrame jf = new JFrame("登陸");
        private JTextField userField = new JTextField(20);
        private JTextField passField = new JTextField(20);
        private JButton loginButton = new JButton("登入");
        public void init() throws  Exception{
            Properties connProp = new Properties();
            connProp.load(new FileInputStream(PROP_FILE));
            driver = connProp.getProperty("driver");
            url = connProp.getProperty("url");
            user = connProp.getProperty("user");
            pass = connProp.getProperty("pass");
            Class.forName(driver);
            loginButton.addActionListener(new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if(valicate1(userField.getText(), passField.getText())){
                        JOptionPane.showMessageDialog(jf, "登陸成功");
                    }else{
                        JOptionPane.showMessageDialog(jf, "登陸失敗");
                    }
                }
            });
            jf.add(userField, BorderLayout.NORTH);
            jf.add(passField);
            jf.add(loginButton, BorderLayout.SOUTH);
            jf.pack();
            jf.setVisible(true);
        }
        private boolean validate(String userName, String userPass){
            String sql = "select * from students where Sname = '" + userName + "' and Sno= '" + userPass + "';";
            System.out.println(sql);
            try(
                    Connection connection = DriverManager.getConnection(url, user, pass);
                    Statement statement = connection.createStatement();
                    ResultSet rs = statement.executeQuery(sql))
            {
                if(rs.next()){
                    return true;
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return false;
        }
    
    
        private boolean valicate1(String userName, String userPass){
            try(
                    Connection conn = DriverManager.getConnection(url, user, pass);
                    PreparedStatement pstmt = conn.prepareStatement(
                            "select * from students where Sname = ? and Sno = ?")){
                pstmt.setString(1, userName);
                pstmt.setString(2,userPass);
                try(
                        ResultSet rs = pstmt.executeQuery())
                {
                    if(rs.next()){
                        return true;
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return false;
        }
    
    
    
        public static void main(String args[]) throws Exception{
            new LoginFrame().init();
        }
    
    
    
    
    
    }
    

  7. 總的看來PreparedStetement比一般的sql語句處理有兩個有點

    1. 它是採用預編譯的原理,效能更好。

    2. 可以防止sql注入,安全性更高。