resultset mysql_MySQL数据库学习笔记(九)----JDBC的ResultSet接口(查询操作)、PreparedStatement接口重构增删改查(含SQL注入的解释)...
【聲明】
歡迎轉(zhuǎn)載,但請保留文章原始出處→_→
【正文】
一、ResultSet接口的介紹:
對數(shù)據(jù)庫的查詢操作,一般需要返回查詢結(jié)果,在程序中,JDBC為我們提供了ResultSet接口來專門處理查詢結(jié)果集。
Statement通過以下方法執(zhí)行一個查詢操作:
ResultSet executeQuery(String sql) throws SQLException
單詞Query就是查詢的意思。函數(shù)的返回類型是ResultSet,實際上查詢的數(shù)據(jù)并不在ResultSet里面,依然是在數(shù)據(jù)庫里,ResultSet中的next()方法類似于一個指針,指向查詢的結(jié)果,然后不斷遍歷。所以這就要求連接不能斷開。
ResultSet接口常用方法:
boolean next()???? 遍歷時,判斷是否有下一個結(jié)果
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接口實現(xiàn)查詢操作:
步驟如下:(和上一篇博文中的增刪改的步驟類似哦)
1、加載數(shù)據(jù)庫驅(qū)動程序:Class.forName(驅(qū)動程序類)
2、通過用戶名密碼和連接地址獲取數(shù)據(jù)庫連接對象:DriverManager.getConnection(連接地址,用戶名,密碼)
3、構(gòu)造查詢SQL語句
4、創(chuàng)建Statement實例:Statement stmt = conn.createStatement()
5、執(zhí)行查詢SQL語句,并返回結(jié)果:ResultSet rs = stmt.executeQuery(sql)
6、處理結(jié)果
7、關(guān)閉連接:rs.close()、stmt.close()、conn.close()
我們來舉個例子吧,來查詢下面的這個表:
新建工程JDBC02,依舊先導(dǎo)入jar包。然后新建類,完整版代碼如下:
1 packagecom.vae.jdbc;2
3 importjava.sql.Connection;4 importjava.sql.DriverManager;5 importjava.sql.ResultSet;6 importjava.sql.SQLException;7 importjava.sql.Statement;8
9 public classJdbcQuey {10
11
12 //數(shù)據(jù)庫連接地址
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 //加載的驅(qū)動程序類(這個類就在我們導(dǎo)入的jar包中)
19 public final static String DRIVER = "com.mysql.jdbc.Driver";20
21 public static voidmain(String[] args) {22 //TODO Auto-generated method stub
23 query();24
25 }26
27
28 //方法:查詢操作
29 public static voidquery(){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 //執(zhí)行查詢并返回結(jié)果集
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 }
關(guān)于代碼的解釋,可以看上一篇博客。上方代碼的核心部分是37至45行。
37行:next()函數(shù):通過next來索引,判斷是否有下一個記錄。一開始就指向內(nèi)存的首地址,即第一條記錄,如果返回值為true,指針會自動指向下一條記錄。
38、39行:getInt(String columnLabel)或者getInt(int columnIndex)代表的是列的索引,參數(shù)可以是列的名字,也可以用編號來表示,我們一般采用后者。編號的順序是按照33行sql語句中列的順序來定的。
程序運行后,后臺輸出如下:
上一篇博客+以上部分,實現(xiàn)了對數(shù)據(jù)庫的簡單增刪改查的操作。其實這種拼接的方式很不好:既麻煩又不安全。我們接下來進(jìn)行改進(jìn)。
三、使用PreparedStatement重構(gòu)增刪改查(推薦)
概念:表示預(yù)編譯的SQL語句的對象。SQL語句被預(yù)編譯并存儲在PreparedStatement對象中。然后可以使用此對象多次高效地執(zhí)行該語句。PreparedStatement是Statement的一個接口。
作用:靈活處理sql語句中的變量。
舉例:
以下面的這張數(shù)據(jù)庫表為例:
新建Java工程文件JDBC3。新建一個Person類,方便在主方法里進(jìn)行操作。Person類的代碼如下:
packagecom.vae.jdbc;public classPerson {private intid;privateString name;private intage;privateString description;public intgetId() {returnid;
}public void setId(intid) {this.id =id;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}public intgetAge() {returnage;
}public void setAge(intage) {this.age =age;
}publicString getDescription() {returndescription;
}public voidsetDescription(String description) {this.description =description;
}public Person(int id, String name, intage, String description) {super();this.id =id;this.name =name;this.age =age;this.description =description;
}public Person(String name, intage, String description) {super();this.name =name;this.age =age;this.description =description;
}publicPerson() {super();
}
@OverridepublicString toString() {return "Person [id=" + id + ", name=" + name + ", age=" +age+ ", description=" + description + "]";
}
}
上方是一個簡單的Person類,并添加set和get方法以及構(gòu)造方法,無需多解釋。
插入操作:
現(xiàn)在在主類JDBCtest中實現(xiàn)插入操作,完整代碼如下:
1 packagecom.vae.jdbc;2
3 importjava.sql.Connection;4 importjava.sql.DriverManager;5 importjava.sql.PreparedStatement;6 importjava.sql.SQLException;7
8 public classJDBCtest {9
10
11 //數(shù)據(jù)庫連接地址
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 //驅(qū)動類
18 public final static String DRIVER = "com.mysql.jdbc.Driver";19
20
21 public static voidmain(String[] args) {22 //TODO Auto-generated method stub
23 Person p = new Person("smyhvae",22,"我是在Java代碼中插入的數(shù)據(jù)");24 insert(p);25 }26
27
28
29 //方法:使用PreparedStatement插入數(shù)據(jù)
30 public static voidinsert(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 //設(shè)置占位符對應(yīng)的值
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 }
我們來看一下上面的代碼是怎么實現(xiàn)代碼的優(yōu)化的:
30行:將整個person對象進(jìn)去,代表的是數(shù)據(jù)庫中的一條記錄。
35行:問號可以理解為占位符,有幾個問號就代表要插入幾個列,這樣看來sql代碼就比較簡潔。
38至40行:給35行的問號設(shè)值,參數(shù)1代表第一個問號的位置,以此類推。
然后我們在main主方法中給Person設(shè)具體的值(23行),通過insert()方法就插入到數(shù)據(jù)庫中去了。數(shù)據(jù)庫中就多了一條記錄:
更新操作:
代碼和上方類似,修改操作的方法如下:
1 //方法:使用PreparedStatement更新數(shù)據(jù)
2 public static voidupdate(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 //設(shè)置占位符對應(yīng)的值
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方法中記得使用四個參數(shù)的Person構(gòu)造方法,傳遞四個參數(shù)。
刪除操作:
代碼和上方類似,方法如下:
1 //方法:使用PreparedStatement刪除數(shù)據(jù)
2 public static void delete(intid){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 //設(shè)置占位符對應(yīng)的值
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 }
這里的方法中,傳入的參數(shù)是是一個id。
查詢操作:
1 //使用PreparedStatement查詢數(shù)據(jù)
2 public static Person findById(intid){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 //設(shè)置占位符對應(yīng)的值
10 ps.setInt(1, id);11
12 ResultSet rs =ps.executeQuery();13 if(rs.next()){14 p = newPerson();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之間的轉(zhuǎn)換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 returnp;35 }
查詢操作稍微麻煩一點,在方法中傳入的參數(shù)是id,方法的返回值是查詢的結(jié)果,即Person類。
四、PreparedStatement小結(jié):
在JDBC應(yīng)用中,如果你已經(jīng)是稍有水平開發(fā)者,你就應(yīng)該始終以PreparedStatement代替Statement。也就是說,在任何時候都不要使用Statement。
基于以下的原因:
一、代碼的可讀性和可維護(hù)性
二、PreparedStatement可以盡最大可能提高性能
三、最重要的一點是極大地提高了安全性
如果使用Statement而不使用PreparedStatement,則會造成一個安全性問題:SQL注入
來看一下SQL注入是怎么回事。現(xiàn)在有如下的一張用戶名密碼表user:
我們在執(zhí)行如下sql語句進(jìn)行查詢:
select id,name,pwd from user where name='xxx' and pwd = 'x' or '1'='1'
竟能出奇地查到所有的用戶名、密碼信息:
因為1=1永遠(yuǎn)是成立的,所以這句話永遠(yuǎn)都成立。所以在Java代碼中,可以利用這個漏洞,將上方的藍(lán)框部分內(nèi)容當(dāng)做pwd的變量的內(nèi)容。來舉個反例:使用Statement寫一個登陸的操作:
1 //登 錄(Statement:會造成SQL注入的安全性問題)
2 public static voidlogin(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語句進(jìn)行判斷,就不會出現(xiàn)字符串拼接的現(xiàn)象了。
五、完整版代碼:
最后附上本文中,PreparedStatement接口重構(gòu)增刪改查的完整版代碼:
1 packagecom.vae.jdbc;2
3 importjava.sql.Connection;4 importjava.sql.DriverManager;5 importjava.sql.PreparedStatement;6 importjava.sql.ResultSet;7 importjava.sql.SQLException;8
9 public classJDBCtest {10
11
12 //數(shù)據(jù)庫連接地址
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 //驅(qū)動類
19 public final static String DRIVER = "com.mysql.jdbc.Driver";20
21
22 public static voidmain(String[] args) {23 //TODO Auto-generated method stub
24 Person p = newPerson();25 //insert(p);26 //update(p);27 //delete(3);
28 p = findById(2);29 System.out.println(p);30 }31
32
33 //方法:使用PreparedStatement插入數(shù)據(jù)
34 public static voidinsert(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 //設(shè)置占位符對應(yīng)的值
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更新數(shù)據(jù)
61 public static voidupdate(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 //設(shè)置占位符對應(yīng)的值
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刪除數(shù)據(jù)
88 public static void delete(intid){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 //設(shè)置占位符對應(yīng)的值
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查詢數(shù)據(jù)
112 public static Person findById(intid){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 //設(shè)置占位符對應(yīng)的值
120 ps.setInt(1, id);121
122 ResultSet rs =ps.executeQuery();123 if(rs.next()){124 p = newPerson();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之間的轉(zhuǎn)換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 returnp;145 }146
147
148 }
總結(jié)
以上是生活随笔為你收集整理的resultset mysql_MySQL数据库学习笔记(九)----JDBC的ResultSet接口(查询操作)、PreparedStatement接口重构增删改查(含SQL注入的解释)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求W两个世界百度云,谢谢
- 下一篇: jsp 插入mysql乱码_JSP My