热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

【声明】欢迎转载,但请保留文章原始出处→_→生命壹号:http:www.cnblogs.comsmyhvae文章来源:http:www.c

【声明】 

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

生命壹号: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();}@Overridepublic 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 }

 


转载于:https://www.cnblogs.com/qianguyihao/p/4054235.html


推荐阅读
  • 在Java Web服务开发中,Apache CXF 和 Axis2 是两个广泛使用的框架。CXF 由于其与 Spring 框架的无缝集成能力,以及更简便的部署方式,成为了许多开发者的首选。本文将详细介绍如何使用 CXF 框架进行 Web 服务的开发,包括环境搭建、服务发布和客户端调用等关键步骤,为开发者提供一个全面的实践指南。 ... [详细]
  • 本指南详细介绍了如何在CentOS 6.6 64位系统上以root用户身份部署Tomcat 8服务器。系统环境为CentOS 6.6 64位,采用源码安装方式。所需软件为apache-tomcat-8.0.23.tar.gz,建议将软件下载至/root/opt目录。具体下载地址请参见官方资源。本指南涵盖了从环境准备到服务启动的完整步骤,适用于需要在该系统环境下搭建高性能Web应用服务器的技术人员。 ... [详细]
  • 深入解析:Synchronized 关键字在 Java 中对 int 和 Integer 对象的作用与影响
    深入探讨了 `Synchronized` 关键字在 Java 中对 `int` 和 `Integer` 对象的影响。尽管初看此题似乎简单,但其实质在于理解对象的概念。根据《Java编程思想》第二章的观点,一切皆为对象。本文详细分析了 `Synchronized` 关键字在不同数据类型上的作用机制,特别是对基本数据类型 `int` 和包装类 `Integer` 的区别处理,帮助读者深入理解 Java 中的同步机制及其在多线程环境中的应用。 ... [详细]
  • 本文介绍了如何利用Struts1框架构建一个简易的四则运算计算器。通过采用DispatchAction来处理不同类型的计算请求,并使用动态Form来优化开发流程,确保代码的简洁性和可维护性。同时,系统提供了用户友好的错误提示,以增强用户体验。 ... [详细]
  • 在 Axublog 1.1.0 版本的 `c_login.php` 文件中发现了一个严重的 SQL 注入漏洞。该漏洞允许攻击者通过操纵登录请求中的参数,注入恶意 SQL 代码,从而可能获取敏感信息或对数据库进行未授权操作。建议用户尽快更新到最新版本并采取相应的安全措施以防止潜在的风险。 ... [详细]
  • 原文网址:https:www.cnblogs.comysoceanp7476379.html目录1、AOP什么?2、需求3、解决办法1:使用静态代理4 ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • 本文深入探讨了如何利用Maven高效管理项目中的外部依赖库。通过介绍Maven的官方依赖搜索地址(),详细讲解了依赖库的添加、版本管理和冲突解决等关键操作。此外,还提供了实用的配置示例和最佳实践,帮助开发者优化项目构建流程,提高开发效率。 ... [详细]
  • Amoeba 通过优化 MySQL 的读写分离功能显著提升了数据库性能。作为一款基于 MySQL 协议的代理工具,Amoeba 能够高效地处理应用程序的请求,并根据预设的规则将 SQL 请求智能地分配到不同的数据库实例,从而实现负载均衡和高可用性。该方案不仅提高了系统的并发处理能力,还有效减少了主数据库的负担,确保了数据的一致性和可靠性。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 在使用SSH框架进行项目开发时,经常会遇到一些常见的问题。例如,在Spring配置文件中配置AOP事务声明后,进行单元测试时可能会出现“No Hibernate Session bound to thread”的错误。本文将详细探讨这一问题的原因,并提供有效的解决方案,帮助开发者顺利解决此类问题。 ... [详细]
  • Spring框架的核心组件与架构解析 ... [详细]
  • 本文详细介绍了在 Vue.js 前端框架中集成 vue-i18n 插件以实现多语言支持的方法。通过具体的配置步骤和示例代码,帮助开发者快速掌握如何在项目中实现国际化功能,提升用户体验。同时,文章还探讨了常见的多语言切换问题及解决方案,为开发人员提供了实用的参考。 ... [详细]
author-avatar
YYANNILl_242
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有