resultset mysql_MySQL数据库学习笔记(九)----JDBC的ResultSet接口(查询操作)、PreparedStatement接口重构增删改查(含SQL注入的解释)...

本文介绍了JDBC中用于处理查询结果的ResultSet接口,包括其常用方法如next()、getInt()等。通过示例展示了如何使用ResultSet遍历查询结果。此外,还探讨了PreparedStatement接口的优势,如提高性能、增强安全性,并给出了使用PreparedStatement重构增删改查操作的代码示例,强调了其在防止SQL注入方面的关键作用。
摘要由CSDN通过智能技术生成

【声明】

欢迎转载,但请保留文章原始出处→_→

【正文】

一、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()

我们来举个例子吧,来查询下面的这个表:

49cbc059ba89d0adbb5f7f3f1202179a.png

新建工程JDBC02,依旧先导入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 //数据库连接地址

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 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 //执行查询并返回结果集

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语句中列的顺序来定的。

程序运行后,后台输出如下:

1ef6a105393d45ae1bc6b098649c7261.png

上一篇博客+以上部分,实现了对数据库的简单增删改查的操作。其实这种拼接的方式很不好:既麻烦又不安全。我们接下来进行改进。

三、使用PreparedStatement重构增删改查(推荐)

概念:表示预编译的SQL语句的对象。SQL语句被预编译并存储在PreparedStatement对象中。然后可以使用此对象多次高效地执行该语句。PreparedStatement是Statement的一个接口。

作用:灵活处理sql语句中的变量。

举例:

以下面的这张数据库表为例:

27e5752a1f6d6271812db47d7cb08282.png

新建Java工程文件JDBC3。新建一个Person类,方便在主方法里进行操作。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方法以及构造方法,无需多解释。

插入操作:

现在在主类JDBCtest中实现插入操作,完整代码如下:

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 //数据库连接地址

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 voidmain(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 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 //设置占位符对应的值

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()方法就插入到数据库中去了。数据库中就多了一条记录:

cafb08ce7f34442b097b9848f2530f6a.png

更新操作:

代码和上方类似,修改操作的方法如下:

1 //方法:使用PreparedStatement更新数据

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 //设置占位符对应的值

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(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 //设置占位符对应的值

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(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 //设置占位符对应的值

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之间的转换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 }

查询操作稍微麻烦一点,在方法中传入的参数是id,方法的返回值是查询的结果,即Person类。

四、PreparedStatement小结:

在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement。也就是说,在任何时候都不要使用Statement。

基于以下的原因:

一、代码的可读性和可维护性

二、PreparedStatement可以尽最大可能提高性能

三、最重要的一点是极大地提高了安全性

如果使用Statement而不使用PreparedStatement,则会造成一个安全性问题:SQL注入

来看一下SQL注入是怎么回事。现在有如下的一张用户名密码表user:

da278f795d09c0e16e5f8884f6c45e2d.png

我们在执行如下sql语句进行查询:

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

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

f5d486749c7d8a5664f5bd4250c8b94c.png

因为1=1永远是成立的,所以这句话永远都成立。所以在Java代码中,可以利用这个漏洞,将上方的蓝框部分内容当做pwd的变量的内容。来举个反例:使用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语句进行判断,就不会出现字符串拼接的现象了。

五、完整版代码:

最后附上本文中,PreparedStatement接口重构增删改查的完整版代码:

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 //数据库连接地址

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 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插入数据

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 //设置占位符对应的值

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 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 //设置占位符对应的值

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(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 //设置占位符对应的值

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(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 //设置占位符对应的值

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之间的转换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 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值