MySQL數據庫學習筆記(九)----JDBC的ResultSet接口(查詢操作)、PreparedStatement接口重構增刪改查(含SQL注入的解釋)


【聲明】 

歡迎轉載,但請保留文章原始出處→_→ 

生命壹號:http://www.cnblogs.com/smyhvae/

文章來源:http://www.cnblogs.com/smyhvae/p/4050825.html

 

【正文】

首先需要回顧一下上一篇文章中的內容:MySQL數據庫學習筆記(八)----JDBC入門及簡單增刪改數據庫的操作

一、ResultSet接口的介紹:

對數據庫的查詢操作,一般需要返回查詢結果,在程序中,JDBC為我們提供了ResultSet接口來專門處理查詢結果集

Statement通過以下方法執行一個查詢操作:

ResultSet executeQuery(String sql) throws SQLException 

單詞Query就是查詢的意思。函數的返回類型是ResultSet,實際上查詢的數據並不在ResultSet里面,依然是在數據庫里,ResultSet中的next()方法類似於一個指針,指向查詢的結果,然后不斷遍歷。所以這就要求連接不能斷開。

ResultSet接口常用方法:

  • boolean next()     遍歷時,判斷是否有下一個結果
  • int getInt(String columnLabel)
  • int getInt(int columnIndex)
  • Date getDate(String columnLabel)
  • Date getDate(int columnIndex)
  • String getString(String columnLabel)
  • String getString(int columnIndex)

 

二、ResultSet接口實現查詢操作:

步驟如下:(和上一篇博文中的增刪改的步驟類似哦)

  • 1、加載數據庫驅動程序:Class.forName(驅動程序類)
  • 2、通過用戶名密碼和連接地址獲取數據庫連接對象:DriverManager.getConnection(連接地址,用戶名,密碼)
  • 3、構造查詢SQL語句
  • 4、創建Statement實例:Statement stmt = conn.createStatement()
  • 5、執行查詢SQL語句,並返回結果:ResultSet rs = stmt.executeQuery(sql)
  • 6、處理結果
  • 7、關閉連接:rs.close()、stmt.close()、conn.close()

我們來舉個例子吧,來查詢下面的這個表:

55ceaaf2-b1f8-420a-89a6-37f502b48192

新建工程JDBC02,依舊先導入jar包。然后新建類,完整版代碼如下:

 1 package com.vae.jdbc;
 2 
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 import java.sql.ResultSet;
 6 import java.sql.SQLException;
 7 import java.sql.Statement;
 8 
 9 public class JdbcQuey {
10 
11 
12     //數據庫連接地址
13     private final static String URL = "jdbc:mysql://localhost:3306/JDBCdb";
14     //用戶名
15     public final static String USERNAME = "root";
16     //密碼
17     public final static String PASSWORD = "smyh";
18     //加載的驅動程序類(這個類就在我們導入的jar包中)
19     public final static String DRIVER = "com.mysql.jdbc.Driver";
20     
21     public static void main(String[] args) {
22         // TODO Auto-generated method stub
23         query();
24 
25     }
26     
27     
28     //方法:查詢操作
29     public static void query(){
30         try {
31             Class.forName(DRIVER);
32             Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
33             String sql = "select id,name,age,description from person";
34             Statement state = conn.createStatement();
35             //執行查詢並返回結果集
36             ResultSet rs = state.executeQuery(sql);
37             while(rs.next()){  //通過next來索引:判斷是否有下一個記錄
38                 //rs.getInt("id"); //方法:int java.sql.ResultSet.getInt(String columnLabel) throws SQLException
39                 int id = rs.getInt(1);  //方法:int java.sql.ResultSet.getInt(int columnIndex) throws SQLException
40 
41                 String name = rs.getString(2);
42                 int age = rs.getInt(3);
43                 String description = rs.getString(4);
44                 System.out.println("id="+id+",name="+name+",age="+age+",description="+description);
45             }
46             rs.close();
47             state.close();
48             conn.close();            
49             
50         } catch (ClassNotFoundException e) {
51             e.printStackTrace();
52         } catch (SQLException e) {
53             e.printStackTrace();
54         }
55     }
56 }

關於代碼的解釋,可以看上一篇博客。上方代碼的核心部分是37至45行。

37行:next()函數:通過next來索引,判斷是否有下一個記錄。一開始就指向內存的首地址,即第一條記錄,如果返回值為true,指針會自動指向下一條記錄。

38、39行:getInt(String columnLabel)或者getInt(int columnIndex)代表的是列的索引,參數可以是列的名字,也可以用編號來表示,我們一般采用后者。編號的順序是按照33行sql語句中列的順序來定的。

程序運行后,后台輸出如下:

a9422041-b446-4dd1-9972-25c10304a4d6

上一篇博客+以上部分,實現了對數據庫的簡單增刪改查的操作。其實這種拼接的方式很不好:既麻煩又不安全。我們接下來進行改進。

 

三、使用PreparedStatement重構增刪改查(推薦)

概念:表示預編譯的SQL語句的對象。SQL語句被預編譯並存儲在PreparedStatement對象中。然后可以使用此對象多次高效地執行該語句。PreparedStatement是Statement的一個接口。

作用:靈活處理sql語句中的變量。

舉例:

以下面的這張數據庫表為例:

d0d81c8d-285b-45a7-8c4b-3bb2beb00b69

新建Java工程文件JDBC3。新建一個Person類,方便在主方法里進行操作。Person類的代碼如下:

package com.vae.jdbc;

public class Person {

    private int id;
    private String name;
    private int age;
    private String description;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    
    public Person(int id, String name, int age, String description) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
        this.description = description;
    }    
    
    public Person(String name, int age, String description) {
        super();
        this.name = name;
        this.age = age;
        this.description = description;
    }
    public Person() {
        super();
    }
    
    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + ", age=" + age
                + ", description=" + description + "]";
    }
    
    
}

上方是一個簡單的Person類,並添加set和get方法以及構造方法,無需多解釋。

插入操作:

現在在主類JDBCtest中實現插入操作,完整代碼如下:

 1 package com.vae.jdbc;
 2 
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 import java.sql.PreparedStatement;
 6 import java.sql.SQLException;
 7 
 8 public class JDBCtest {
 9 
10 
11     //數據庫連接地址
12     public final static String URL = "jdbc:mysql://localhost:3306/JDBCdb";
13     //用戶名
14     public final static String USERNAME = "root";
15     //密碼
16     public final static String PASSWORD = "smyh";
17     //驅動類
18     public final static String DRIVER = "com.mysql.jdbc.Driver";
19     
20     
21     public static void main(String[] args) {
22         // TODO Auto-generated method stub
23         Person p = new Person("smyhvae",22,"我是在Java代碼中插入的數據");
24         insert(p);
25     }
26     
27 
28 
29     //方法:使用PreparedStatement插入數據
30     public static void insert(Person p){
31         
32         try {
33             Class.forName(DRIVER);
34             Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
35             String sql = "insert into person(name,age,description)values(?,?,?)";
36             PreparedStatement ps = conn.prepareStatement(sql);
37             //設置占位符對應的值
38             ps.setString(1, p.getName());
39             ps.setInt(2, p.getAge());
40             ps.setString(3, p.getDescription());
41             
42             ps.executeUpdate();
43             
44             ps.close();
45             conn.close();
46             
47             
48         } catch (ClassNotFoundException e) {
49             e.printStackTrace();
50         } catch (SQLException e) {
51             e.printStackTrace();
52         }        
53     }
54 }

我們來看一下上面的代碼是怎么實現代碼的優化的:

30行:將整個person對象進去,代表的是數據庫中的一條記錄。

35行:問號可以理解為占位符,有幾個問號就代表要插入幾個列,這樣看來sql代碼就比較簡潔

38至40行:給35行的問號設值,參數1代表第一個問號的位置,以此類推

然后我們在main主方法中給Person設具體的值(23行),通過insert()方法就插入到數據庫中去了。數據庫中就多了一條記錄:

868b266f-4a7c-4018-998d-85befb15bbc0

更新操作:

代碼和上方類似,修改操作的方法如下:

 1     //方法:使用PreparedStatement更新數據
 2     public static void update(Person p){
 3         try {
 4             Class.forName(DRIVER);
 5             Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
 6             String sql = "update person set name=?,age=?,description=? where id=?";
 7             PreparedStatement ps = conn.prepareStatement(sql);
 8             //設置占位符對應的值
 9             ps.setString(1, p.getName());
10             ps.setInt(2, p.getAge());
11             ps.setString(3, p.getDescription());
12             ps.setInt(4, p.getId());
13             
14             ps.executeUpdate();
15             
16             ps.close();
17             conn.close();
18             
19             
20         } catch (ClassNotFoundException e) {
21             e.printStackTrace();
22         } catch (SQLException e) {
23             e.printStackTrace();
24         }
25     }

因為在這里有四個問號的占位符,所以稍后再main方法中記得使用四個參數的Person構造方法,傳遞四個參數。

刪除操作:

代碼和上方類似,方法如下:

 1     //方法:使用PreparedStatement刪除數據
 2     public static void delete(int id){
 3         try {
 4             Class.forName(DRIVER);
 5             Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
 6             String sql = "delete from person where id=?";
 7             PreparedStatement ps = conn.prepareStatement(sql);
 8             //設置占位符對應的值
 9             ps.setInt(1, id);
10             
11             ps.executeUpdate();
12             
13             ps.close();
14             conn.close();
15             
16             
17         } catch (ClassNotFoundException e) {
18             e.printStackTrace();
19         } catch (SQLException e) {
20             e.printStackTrace();
21         }
22     }

這里的方法中,傳入的參數是是一個id。

查詢操作:

 1     // 使用PreparedStatement查詢數據
 2     public static Person findById(int id){
 3         Person p = null;
 4         try {
 5             Class.forName(DRIVER);
 6             Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
 7             String sql = "select name,age,description from person where id=?";
 8             PreparedStatement ps = conn.prepareStatement(sql);
 9             //設置占位符對應的值
10             ps.setInt(1, id);
11             
12             ResultSet rs = ps.executeQuery();
13             if(rs.next()){
14                 p = new Person();
15                 p.setId(id);
16                 p.setName(rs.getString(1));
17                 p.setAge(rs.getInt(2));
18                 p.setDescription(rs.getString(3));
19                 //把 java.sql.Date 與 java.util.Date之間的轉換
20 //                java.util.Date date = rs.getDate(4);
21 //                ps.setDate(4, new java.sql.Date(date.getTime()));
22                 
23             }
24             rs.close();
25             ps.close();
26             conn.close();
27             
28             
29         } catch (ClassNotFoundException e) {
30             e.printStackTrace();
31         } catch (SQLException e) {
32             e.printStackTrace();
33         }
34         return p;
35     }

查詢操作稍微麻煩一點,在方法中傳入的參數是id,方法的返回值是查詢的結果,即Person類。

 

四、PreparedStatement小結:

在JDBC應用中,如果你已經是稍有水平開發者,你就應該始終以PreparedStatement代替Statement。也就是說,在任何時候都不要使用Statement。

基於以下的原因:

  • 一、代碼的可讀性和可維護性
  • 二、PreparedStatement可以盡最大可能提高性能
  • 三、最重要的一點是極大地提高了安全性

如果使用Statement而不使用PreparedStatement,則會造成一個安全性問題:SQL注入

來看一下SQL注入是怎么回事。現在有如下的一張用戶名密碼表user:

1cbad809-eef2-43f7-9044-631c7b94838d

我們在執行如下sql語句進行查詢:

select id,name,pwd from user where name='xxx' and pwd = 'x' or '1'='1'

 

竟能出奇地查到所有的用戶名、密碼信息:

9bde5b94-960e-4894-bc99-eec64b1f3ee8

因為1=1永遠是成立的,所以這句話永遠都成立。所以在Java代碼中,可以利用這個漏洞,將上方的藍框部分內容當做pwd的變量的內容。來舉個反例:使用Statement寫一個登陸的操作:

 1     //登 錄(Statement:會造成SQL注入的安全性問題)
 2     public static void login(String name,String pwd){
 3         Person p = null;
 4         try {
 5             Class.forName(DRIVER);
 6             Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
 7 //            String sql = "select id,name,pwd from user where name='' and pwd=''";
 8             
 9             StringBuffer sql = new StringBuffer("select id,name,pwd from user where name='");
10             sql.append(name).append("' and pwd='").append(pwd).append("'");
11             Statement ps = conn.createStatement();
12             
13             ResultSet rs = ps.executeQuery(sql.toString());
14             if(rs.next()){
15             }
16             rs.close();
17             ps.close();
18             conn.close();
19             
20             
21         } catch (ClassNotFoundException e) {
22             e.printStackTrace();
23         } catch (SQLException e) {
24             e.printStackTrace();
25         }
26     }

上方代碼中的第10行就是采用字符串拼接的方式,就會造成SQL注入的安全性問題。

而如果使用PreparedStatement中包含問號的sql語句,程序就會先對這句sql語句進行判斷,就不會出現字符串拼接的現象了。

 

五、完整版代碼:

最后附上本文中,PreparedStatement接口重構增刪改查的完整版代碼:

  1 package com.vae.jdbc;
  2 
  3 import java.sql.Connection;
  4 import java.sql.DriverManager;
  5 import java.sql.PreparedStatement;
  6 import java.sql.ResultSet;
  7 import java.sql.SQLException;
  8 
  9 public class JDBCtest {
 10 
 11 
 12     //數據庫連接地址
 13     public final static String URL = "jdbc:mysql://localhost:3306/JDBCdb";
 14     //用戶名
 15     public final static String USERNAME = "root";
 16     //密碼
 17     public final static String PASSWORD = "smyh";
 18     //驅動類
 19     public final static String DRIVER = "com.mysql.jdbc.Driver";
 20     
 21     
 22     public static void main(String[] args) {
 23         // TODO Auto-generated method stub
 24         Person p = new Person();
 25         //insert(p);
 26         //update(p);
 27         //delete(3);
 28         p = findById(2);
 29         System.out.println(p);
 30     }
 31     
 32     
 33     //方法:使用PreparedStatement插入數據
 34     public static void insert(Person p){
 35         
 36         try {
 37             Class.forName(DRIVER);
 38             Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
 39             String sql = "insert into person(name,age,description)values(?,?,?)";
 40             PreparedStatement ps = conn.prepareStatement(sql);
 41             //設置占位符對應的值
 42             ps.setString(1, p.getName());
 43             ps.setInt(2, p.getAge());
 44             ps.setString(3, p.getDescription());
 45             
 46             ps.executeUpdate();
 47             
 48             ps.close();
 49             conn.close();
 50             
 51             
 52         } catch (ClassNotFoundException e) {
 53             e.printStackTrace();
 54         } catch (SQLException e) {
 55             e.printStackTrace();
 56         }        
 57     }
 58     
 59     
 60     //方法:使用PreparedStatement更新數據
 61     public static void update(Person p){
 62         try {
 63             Class.forName(DRIVER);
 64             Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
 65             String sql = "update person set name=?,age=?,description=? where id=?";
 66             PreparedStatement ps = conn.prepareStatement(sql);
 67             //設置占位符對應的值
 68             ps.setString(1, p.getName());
 69             ps.setInt(2, p.getAge());
 70             ps.setString(3, p.getDescription());
 71             ps.setInt(4, p.getId());
 72             
 73             ps.executeUpdate();
 74             
 75             ps.close();
 76             conn.close();
 77             
 78             
 79         } catch (ClassNotFoundException e) {
 80             e.printStackTrace();
 81         } catch (SQLException e) {
 82             e.printStackTrace();
 83         }
 84     }
 85     
 86     
 87     //方法:使用PreparedStatement刪除數據
 88     public static void delete(int id){
 89         try {
 90             Class.forName(DRIVER);
 91             Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
 92             String sql = "delete from person where id=?";
 93             PreparedStatement ps = conn.prepareStatement(sql);
 94             //設置占位符對應的值
 95             ps.setInt(1, id);
 96             
 97             ps.executeUpdate();
 98             
 99             ps.close();
100             conn.close();
101             
102             
103         } catch (ClassNotFoundException e) {
104             e.printStackTrace();
105         } catch (SQLException e) {
106             e.printStackTrace();
107         }
108     }
109     
110     
111     // 使用PreparedStatement查詢數據
112     public static Person findById(int id){
113         Person p = null;
114         try {
115             Class.forName(DRIVER);
116             Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
117             String sql = "select name,age,description from person where id=?";
118             PreparedStatement ps = conn.prepareStatement(sql);
119             //設置占位符對應的值
120             ps.setInt(1, id);
121             
122             ResultSet rs = ps.executeQuery();
123             if(rs.next()){
124                 p = new Person();
125                 p.setId(id);
126                 p.setName(rs.getString(1));
127                 p.setAge(rs.getInt(2));
128                 p.setDescription(rs.getString(3));
129                 //把 java.sql.Date 與 java.util.Date之間的轉換
130 //                java.util.Date date = rs.getDate(4);
131 //                ps.setDate(4, new java.sql.Date(date.getTime()));
132                 
133             }
134             rs.close();
135             ps.close();
136             conn.close();
137             
138             
139         } catch (ClassNotFoundException e) {
140             e.printStackTrace();
141         } catch (SQLException e) {
142             e.printStackTrace();
143         }
144         return p;
145     }
146     
147 
148 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM