java資料庫程式設計(5) 使用preparedStatement
阿新 • • 發佈:2018-12-02
- preparedStatement是一種帶有佔位符(?)的sql語句,它可以將預編譯的sql語句儲存起來,然後可以使用這個預編譯好的sql語句多次高效地執行傳入具體引數的sql語句。
- 例如執行 insert into table xxx values(1); insert into table xxx values(2); ... 這樣結構相同,只是具體的資料不通過的語句,就可以使用PreparedStatement物件將一個insert into table xxx valuse(?);預編譯好,然後再在具體執行的時候呼叫PreparedMent物件的setInt(1,x);方法傳入具體的值。 這裡的1是指第一個?。
- 具體的講解穿插在程式碼中。
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
-
使用PreparedStatement不僅可以提高效率,而且可以防止sql注入。我理解的sql注入是使用一般的sql語句處理過程中如果使用者輸入的內容不是合法資訊,但是可以和系統預先設定的sql語句部分合成一條語法和邏輯都沒有問題的sql語句。並讓這條語句執行某種非法操作。
-
如下面這個例子,在不使用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的。
-
當使用了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(); } }
-
總的看來PreparedStetement比一般的sql語句處理有兩個有點
-
它是採用預編譯的原理,效能更好。
-
可以防止sql注入,安全性更高。
-