【声明】

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

生命壹号: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()

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

新建工程JDBC02,依旧先导入jar包。然后新建类,完整版代码如下:

 package com.vae.jdbc;

 import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement; public class JdbcQuey { //数据库连接地址
private final static String URL = "jdbc:mysql://localhost:3306/JDBCdb";
//用户名
public final static String USERNAME = "root";
//密码
public final static String PASSWORD = "smyh";
//加载的驱动程序类(这个类就在我们导入的jar包中)
public final static String DRIVER = "com.mysql.jdbc.Driver"; public static void main(String[] args) {
// TODO Auto-generated method stub
query(); } //方法:查询操作
public static void query(){
try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
String sql = "select id,name,age,description from person";
Statement state = conn.createStatement();
//执行查询并返回结果集
ResultSet rs = state.executeQuery(sql);
while(rs.next()){ //通过next来索引:判断是否有下一个记录
//rs.getInt("id"); //方法:int java.sql.ResultSet.getInt(String columnLabel) throws SQLException
int id = rs.getInt(1); //方法:int java.sql.ResultSet.getInt(int columnIndex) throws SQLException String name = rs.getString(2);
int age = rs.getInt(3);
String description = rs.getString(4);
System.out.println("id="+id+",name="+name+",age="+age+",description="+description);
}
rs.close();
state.close();
conn.close(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

关于代码的解释,可以看上一篇博客。上方代码的核心部分是37至45行。

37行:next()函数:通过next来索引,判断是否有下一个记录。一开始就指向内存的首地址,即第一条记录,如果返回值为true,指针会自动指向下一条记录。

38、39行:getInt(String columnLabel)或者getInt(int columnIndex)代表的是列的索引,参数可以是列的名字,也可以用编号来表示,我们一般采用后者。编号的顺序是按照33行sql语句中列的顺序来定的。

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

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

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

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

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

举例:

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

新建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中实现插入操作,完整代码如下:

 package com.vae.jdbc;

 import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException; public class JDBCtest { //数据库连接地址
public final static String URL = "jdbc:mysql://localhost:3306/JDBCdb";
//用户名
public final static String USERNAME = "root";
//密码
public final static String PASSWORD = "smyh";
//驱动类
public final static String DRIVER = "com.mysql.jdbc.Driver"; public static void main(String[] args) {
// TODO Auto-generated method stub
Person p = new Person("smyhvae",22,"我是在Java代码中插入的数据");
insert(p);
} //方法:使用PreparedStatement插入数据
public static void insert(Person p){ try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
String sql = "insert into person(name,age,description)values(?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
//设置占位符对应的值
ps.setString(1, p.getName());
ps.setInt(2, p.getAge());
ps.setString(3, p.getDescription()); ps.executeUpdate(); ps.close();
conn.close(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

我们来看一下上面的代码是怎么实现代码的优化的:

30行:将整个person对象进去,代表的是数据库中的一条记录。

35行:问号可以理解为占位符,有几个问号就代表要插入几个列,这样看来sql代码就比较简洁

38至40行:给35行的问号设值,参数1代表第一个问号的位置,以此类推

然后我们在main主方法中给Person设具体的值(23行),通过insert()方法就插入到数据库中去了。数据库中就多了一条记录:

更新操作:

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

     //方法:使用PreparedStatement更新数据
public static void update(Person p){
try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
String sql = "update person set name=?,age=?,description=? where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
//设置占位符对应的值
ps.setString(1, p.getName());
ps.setInt(2, p.getAge());
ps.setString(3, p.getDescription());
ps.setInt(4, p.getId()); ps.executeUpdate(); ps.close();
conn.close(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}

因为在这里有四个问号的占位符,所以稍后再main方法中记得使用四个参数的Person构造方法,传递四个参数。

删除操作:

代码和上方类似,方法如下:

     //方法:使用PreparedStatement删除数据
public static void delete(int id){
try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
String sql = "delete from person where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
//设置占位符对应的值
ps.setInt(1, id); ps.executeUpdate(); ps.close();
conn.close(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}

这里的方法中,传入的参数是是一个id。

查询操作:

     // 使用PreparedStatement查询数据
public static Person findById(int id){
Person p = null;
try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
String sql = "select name,age,description from person where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
//设置占位符对应的值
ps.setInt(1, id); ResultSet rs = ps.executeQuery();
if(rs.next()){
p = new Person();
p.setId(id);
p.setName(rs.getString(1));
p.setAge(rs.getInt(2));
p.setDescription(rs.getString(3));
//把 java.sql.Date 与 java.util.Date之间的转换
// java.util.Date date = rs.getDate(4);
// ps.setDate(4, new java.sql.Date(date.getTime())); }
rs.close();
ps.close();
conn.close(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return p;
}

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

四、PreparedStatement小结:

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

基于以下的原因:

  • 一、代码的可读性和可维护性
  • 二、PreparedStatement可以尽最大可能提高性能
  • 三、最重要的一点是极大地提高了安全性

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

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

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

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

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

因为1=1永远是成立的,所以这句话永远都成立。所以在Java代码中,可以利用这个漏洞,将上方的蓝框部分内容当做pwd的变量的内容。来举个反例:使用Statement写一个登陆的操作:

     //登 录(Statement:会造成SQL注入的安全性问题)
public static void login(String name,String pwd){
Person p = null;
try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
// String sql = "select id,name,pwd from user where name='' and pwd=''"; StringBuffer sql = new StringBuffer("select id,name,pwd from user where name='");
sql.append(name).append("' and pwd='").append(pwd).append("'");
Statement ps = conn.createStatement(); ResultSet rs = ps.executeQuery(sql.toString());
if(rs.next()){
}
rs.close();
ps.close();
conn.close(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}

上方代码中的第10行就是采用字符串拼接的方式,就会造成SQL注入的安全性问题。

而如果使用PreparedStatement中包含问号的sql语句,程序就会先对这句sql语句进行判断,就不会出现字符串拼接的现象了。

五、完整版代码:

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

 package com.vae.jdbc;

 import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; public class JDBCtest { //数据库连接地址
public final static String URL = "jdbc:mysql://localhost:3306/JDBCdb";
//用户名
public final static String USERNAME = "root";
//密码
public final static String PASSWORD = "smyh";
//驱动类
public final static String DRIVER = "com.mysql.jdbc.Driver"; public static void main(String[] args) {
// TODO Auto-generated method stub
Person p = new Person();
//insert(p);
//update(p);
//delete(3);
p = findById(2);
System.out.println(p);
} //方法:使用PreparedStatement插入数据
public static void insert(Person p){ try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
String sql = "insert into person(name,age,description)values(?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
//设置占位符对应的值
ps.setString(1, p.getName());
ps.setInt(2, p.getAge());
ps.setString(3, p.getDescription()); ps.executeUpdate(); ps.close();
conn.close(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
} //方法:使用PreparedStatement更新数据
public static void update(Person p){
try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
String sql = "update person set name=?,age=?,description=? where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
//设置占位符对应的值
ps.setString(1, p.getName());
ps.setInt(2, p.getAge());
ps.setString(3, p.getDescription());
ps.setInt(4, p.getId()); ps.executeUpdate(); ps.close();
conn.close(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
} //方法:使用PreparedStatement删除数据
public static void delete(int id){
try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
String sql = "delete from person where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
//设置占位符对应的值
ps.setInt(1, id); ps.executeUpdate(); ps.close();
conn.close(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
} // 使用PreparedStatement查询数据
public static Person findById(int id){
Person p = null;
try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
String sql = "select name,age,description from person where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
//设置占位符对应的值
ps.setInt(1, id); ResultSet rs = ps.executeQuery();
if(rs.next()){
p = new Person();
p.setId(id);
p.setName(rs.getString(1));
p.setAge(rs.getInt(2));
p.setDescription(rs.getString(3));
//把 java.sql.Date 与 java.util.Date之间的转换
// java.util.Date date = rs.getDate(4);
// ps.setDate(4, new java.sql.Date(date.getTime())); }
rs.close();
ps.close();
conn.close(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return p;
} }

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

  1. 使用JDBC分别利用Statement和PreparedStatement来对MySQL数据库进行简单的增删改查以及SQL注入的原理

    一.MySQL数据库的下载及安装 https://www.mysql.com/ 点击DOWNLOADS,拉到页面底部,找到MySQL Community(GPL)Downloads,点击 选择下图中的 ...

  2. 【EF学习笔记09】----------使用 EntityState 枚举标记实体状态,实现增删改查

    讲解之前,先来看一下我们的数据库结构:班级表 学生表 如上图,实体状态由EntityState枚举定义:Detached(未跟踪).Unchanged(未改变).Added(已添加).Deleted( ...

  3. MySQL数据库学习笔记(十)----JDBC事务处理、封装JDBC工具类

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  4. MySQL数据库学习笔记(十一)----DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类)

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  5. MySQL数据库学习笔记(十二)----开源工具DbUtils的使用(数据库的增删改查)

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  6. Mysql数据库学习笔记之数据库索引(index)

    什么是索引: SQL索引有两种,聚集索引和非聚集索引,索引主要目的是提高了SQL Server系统的性能,加快数据的查询速度与减少系统的响应时间. 聚集索引:该索引中键值的逻辑顺序决定了表中相应行的物 ...

  7. MYSQL数据库学习笔记1

      MYSQL数据库学习笔记1 数据库概念 关系数据库 常见数据库软件 SQL SQL的概念 SQL语言分类 数据库操作 创建数据库 查看数据库的定义 删除数据库 修改数据库 创建表 数据类型 约束 ...

  8. MySQL数据库学习笔记(三)----基本的SQL语句

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  9. 大数据之路week05--day01(JDBC 初识之实现一个系统 实现用户选择增删改查 未优化版本)

    要求,实现用户选择增删改查. 给出mysql文件,朋友们可以自己运行导入到自己的数据库中: /* Navicat MySQL Data Transfer Source Server : mysql S ...

随机推荐

  1. [ERROR] Plugin 'InnoDB' init function returned error

    今天一大早到公司,计划把开发环境的mysql升级到5.7.15,干净关闭系统后,把目录从5.6指向到5.7,一切正常,重新指向5.6启动时,报下列错误: 2016-10-31 08:13:14 869 ...

  2. DShow实现一个avi视频的播放(含有个人解释和注释)

    此项目为win32下的控制台C++代码(别忘记配置DShow库) // movie_test.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" ...

  3. Android5.0新特性——新增的Widget(Widget)

    新增的Widget RecyclerView RecyclerView是ListView的升级版,它具备了更好的性能,且更容易使用.和ListView一样,RecyclerView是用来显示大量数据的 ...

  4. [Xamarin.Android] 发布NuGet套件

    [Xamarin.Android] 发布NuGet套件 前言 在Xamarin中,可以将自己开发的项目包装成为NuGet套件发布至NuGet Server,来提供其他开发人员使用.本篇介绍如何封装并发 ...

  5. 解决客户 IE 浏览器"兼容性视图"设置带来的问题

    最近在给客户开发一个 ASP.NET web 报表时,发现客户的 IE8 浏览器上,看网页总是怪怪的. 调查后发现,客户的 IE8 浏览器,统一被设置成"对本地网络使用兼容性视图" ...

  6. ASP.NET MVC的请求生命周期

    我希望能理解在浏览器输入URL并敲击回车来请求一个ASP.NET MVC网站的页面之后发生的任何事情. 为什么需要关心这些?有两个原因.首先是因为ASP.NET MVC是一个扩展性非常强的框架.例如, ...

  7. artTemplate 介绍

    artTemplate 是新一代 javascript 模板引擎,它采用预编译方式让性能有了质的飞跃,并且充分利用 javascript 引擎特性,使得其性能无论在前端还是后端都有极其出色的表现. 编 ...

  8. Microsoft Dynamics 2013 --Social Pane

    Microsoft Dynamics 2013 有一个新的东西--Social Pane (图1) 进入窗体设置,发现改选项卡的详细设置如下 (图2) Tab键的选项有3种[活动][公告][注释],若 ...

  9. 安卓开发_浅谈Android动画(一)

    动画效果,针对图片实现 现在学习四种基本的简单动画效果 一.Tween Animation共同属性 1.Duration:动画持续时间(毫秒单位) 2.fillAfter:设置为true,动画转化在动 ...

  10. iOS之Cookie

    iOS之Cookie使用 简介 概念:Cookie中文名称叫做"小型文本文件",指某些网站为了辨别用户身份而存储在用户本地终端上的数据(通常经过加 密). Web服务器可以用过Se ...