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()
我们来举个例子吧,来查询下面的这个表:
新建工程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注入的解释)的更多相关文章
- 使用JDBC分别利用Statement和PreparedStatement来对MySQL数据库进行简单的增删改查以及SQL注入的原理
一.MySQL数据库的下载及安装 https://www.mysql.com/ 点击DOWNLOADS,拉到页面底部,找到MySQL Community(GPL)Downloads,点击 选择下图中的 ...
- 【EF学习笔记09】----------使用 EntityState 枚举标记实体状态,实现增删改查
讲解之前,先来看一下我们的数据库结构:班级表 学生表 如上图,实体状态由EntityState枚举定义:Detached(未跟踪).Unchanged(未改变).Added(已添加).Deleted( ...
- MySQL数据库学习笔记(十)----JDBC事务处理、封装JDBC工具类
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- MySQL数据库学习笔记(十一)----DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类)
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- MySQL数据库学习笔记(十二)----开源工具DbUtils的使用(数据库的增删改查)
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- Mysql数据库学习笔记之数据库索引(index)
什么是索引: SQL索引有两种,聚集索引和非聚集索引,索引主要目的是提高了SQL Server系统的性能,加快数据的查询速度与减少系统的响应时间. 聚集索引:该索引中键值的逻辑顺序决定了表中相应行的物 ...
- MYSQL数据库学习笔记1
MYSQL数据库学习笔记1 数据库概念 关系数据库 常见数据库软件 SQL SQL的概念 SQL语言分类 数据库操作 创建数据库 查看数据库的定义 删除数据库 修改数据库 创建表 数据类型 约束 ...
- MySQL数据库学习笔记(三)----基本的SQL语句
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...
- 大数据之路week05--day01(JDBC 初识之实现一个系统 实现用户选择增删改查 未优化版本)
要求,实现用户选择增删改查. 给出mysql文件,朋友们可以自己运行导入到自己的数据库中: /* Navicat MySQL Data Transfer Source Server : mysql S ...
随机推荐
- Hibernate之映射文件中索引及约束的使用
1.添加索引: 在一对多的关系中,在多的一方会产生一个外键,这个外键没有自动 添加索引,当存在从一的一端产生对多的一端的查询时,有可能会在多的一端造成全表查询问题,数据量巨大时会产生严重的性能问题.可 ...
- [WP8] ListBox的Item宽度自动填满
[WP8] ListBox的Item宽度自动填满 范例下载 范例程序代码:点此下载 问题情景 开发WP8应用程序的时候,常常会需要使用ListBox作为容器来呈现各种数据集合.但是在ListBox呈现 ...
- CSS3边框温故
1.简介:border属性在CSS1中就已经定义了,用来设置元素边框风格,设置不同的边框.颜色.粗细 2.基本属性,包括三个类型值:(1)border-width:设置元素边框的粗细,默认3~4px( ...
- Javascript中的Label语句
在javascript中,我们可能很少会去用到 Label 语句,但是熟练的应用 Label 语句,尤其是在嵌套循环中熟练应用 break, continue 与 Label 可以精确的返回到你想要的 ...
- javascript宿主对象之window.location
location属性是一个用来存储当前页面URL信息的对象. 下面我们通过循环来列出location对象的完整属性列表: for(var i in location){ if(typeof locat ...
- Android 多语言
Android 多语言 在res文件上右击创建新的values文件 在strings文件中设置多语言 3.在layout文件中使用 @strings/key 引用相应资源
- 高清SDI编码器|上海视涛科技
SDI编码器(E500)简介 SDI编码器(E500)是上海视涛科技出品的高性能SDI编码产品.该SDI编码器是上海视涛电子完全自主研发,并适用于各种SDI信号的编码采集及网络传输的专用硬件设备.可兼 ...
- 解决Sharepoint 2010 custom display form 不显示附件的问题
sharepoint 2010用designer添加自定义的 display form默认是不会显示附件的. 需要添加如下代码才会显示附件: <tr> <td width=" ...
- ShareSDK短信验证码集成详细步骤
1.这里使用的是ShareSDK网的短信验证码SDK 官网 http://www.mob.com 先去http://www.mob.com/#/reg 注册成为开发者 填写相应的信息,邮箱账号,然后 ...
- win7共享文件夹给局域网
1.设置共享 2.关闭"需要密码访问"