【声明】

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

生命壹号:http://www.cnblogs.com/smyhvae/

文章来源:http://www.cnblogs.com/smyhvae/p/4050825.html

【正文】

首先需要回顾一下上一篇文章中的内容:MySQL数据库学习笔记(八)----JDBC入门及简单增删改数据库的操作

一、ResultSet接口的介绍:

对数据库的查询操作,一般需要返回查询结果,在程序中,JDBC为我们提供了ResultSet接口来专门处理查询结果集。

Statement通过以下方法执行一个查询操作:

  1. 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包。然后新建类,完整版代码如下:

  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. private 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. //加载的驱动程序类(这个类就在我们导入的jar包中)
  18. public final static String DRIVER = "com.mysql.jdbc.Driver";
  19.  
  20. public static void main(String[] args) {
  21. // TODO Auto-generated method stub
  22. query();
  23.  
  24. }
  25.  
  26. //方法:查询操作
  27. public static void query(){
  28. try {
  29. Class.forName(DRIVER);
  30. Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
  31. String sql = "select id,name,age,description from person";
  32. Statement state = conn.createStatement();
  33. //执行查询并返回结果集
  34. ResultSet rs = state.executeQuery(sql);
  35. while(rs.next()){ //通过next来索引:判断是否有下一个记录
  36. //rs.getInt("id"); //方法:int java.sql.ResultSet.getInt(String columnLabel) throws SQLException
  37. int id = rs.getInt(1); //方法:int java.sql.ResultSet.getInt(int columnIndex) throws SQLException
  38.  
  39. String name = rs.getString(2);
  40. int age = rs.getInt(3);
  41. String description = rs.getString(4);
  42. System.out.println("id="+id+",name="+name+",age="+age+",description="+description);
  43. }
  44. rs.close();
  45. state.close();
  46. conn.close();
  47.  
  48. } catch (ClassNotFoundException e) {
  49. e.printStackTrace();
  50. } catch (SQLException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54. }

关于代码的解释,可以看上一篇博客。上方代码的核心部分是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类的代码如下:

  1. package com.vae.jdbc;
  2.  
  3. public class Person {
  4.  
  5. private int id;
  6. private String name;
  7. private int age;
  8. private String description;
  9.  
  10. public int getId() {
  11. return id;
  12. }
  13. public void setId(int id) {
  14. this.id = id;
  15. }
  16. public String getName() {
  17. return name;
  18. }
  19. public void setName(String name) {
  20. this.name = name;
  21. }
  22. public int getAge() {
  23. return age;
  24. }
  25. public void setAge(int age) {
  26. this.age = age;
  27. }
  28. public String getDescription() {
  29. return description;
  30. }
  31. public void setDescription(String description) {
  32. this.description = description;
  33. }
  34.  
  35. public Person(int id, String name, int age, String description) {
  36. super();
  37. this.id = id;
  38. this.name = name;
  39. this.age = age;
  40. this.description = description;
  41. }
  42.  
  43. public Person(String name, int age, String description) {
  44. super();
  45. this.name = name;
  46. this.age = age;
  47. this.description = description;
  48. }
  49. public Person() {
  50. super();
  51. }
  52.  
  53. @Override
  54. public String toString() {
  55. return "Person [id=" + id + ", name=" + name + ", age=" + age
  56. + ", description=" + description + "]";
  57. }
  58.  
  59. }

上方是一个简单的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. public final static String URL = "jdbc:mysql://localhost:3306/JDBCdb";
  12. //用户名
  13. public final static String USERNAME = "root";
  14. //密码
  15. public final static String PASSWORD = "smyh";
  16. //驱动类
  17. public final static String DRIVER = "com.mysql.jdbc.Driver";
  18.  
  19. public static void main(String[] args) {
  20. // TODO Auto-generated method stub
  21. Person p = new Person("smyhvae",22,"我是在Java代码中插入的数据");
  22. insert(p);
  23. }
  24.  
  25. //方法:使用PreparedStatement插入数据
  26. public static void insert(Person p){
  27.  
  28. try {
  29. Class.forName(DRIVER);
  30. Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
  31. String sql = "insert into person(name,age,description)values(?,?,?)";
  32. PreparedStatement ps = conn.prepareStatement(sql);
  33. //设置占位符对应的值
  34. ps.setString(1, p.getName());
  35. ps.setInt(2, p.getAge());
  36. ps.setString(3, p.getDescription());
  37.  
  38. ps.executeUpdate();
  39.  
  40. ps.close();
  41. conn.close();
  42.  
  43. } catch (ClassNotFoundException e) {
  44. e.printStackTrace();
  45. } catch (SQLException e) {
  46. e.printStackTrace();
  47. }
  48. }
  49. }

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

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

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

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

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

更新操作:

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

  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. } catch (ClassNotFoundException e) {
  20. e.printStackTrace();
  21. } catch (SQLException e) {
  22. e.printStackTrace();
  23. }
  24. }

因为在这里有四个问号的占位符,所以稍后再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. } catch (ClassNotFoundException e) {
  17. e.printStackTrace();
  18. } catch (SQLException e) {
  19. e.printStackTrace();
  20. }
  21. }

这里的方法中,传入的参数是是一个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. } catch (ClassNotFoundException e) {
  29. e.printStackTrace();
  30. } catch (SQLException e) {
  31. e.printStackTrace();
  32. }
  33. return p;
  34. }

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

四、PreparedStatement小结:

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

基于以下的原因:

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

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

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

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

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

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

因为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. } catch (ClassNotFoundException e) {
  21. e.printStackTrace();
  22. } catch (SQLException e) {
  23. e.printStackTrace();
  24. }
  25. }

上方代码中的第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. 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. public static void main(String[] args) {
  21. // TODO Auto-generated method stub
  22. Person p = new Person();
  23. //insert(p);
  24. //update(p);
  25. //delete(3);
  26. p = findById(2);
  27. System.out.println(p);
  28. }
  29.  
  30. //方法:使用PreparedStatement插入数据
  31. public static void insert(Person p){
  32.  
  33. try {
  34. Class.forName(DRIVER);
  35. Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
  36. String sql = "insert into person(name,age,description)values(?,?,?)";
  37. PreparedStatement ps = conn.prepareStatement(sql);
  38. //设置占位符对应的值
  39. ps.setString(1, p.getName());
  40. ps.setInt(2, p.getAge());
  41. ps.setString(3, p.getDescription());
  42.  
  43. ps.executeUpdate();
  44.  
  45. ps.close();
  46. conn.close();
  47.  
  48. } catch (ClassNotFoundException e) {
  49. e.printStackTrace();
  50. } catch (SQLException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54.  
  55. //方法:使用PreparedStatement更新数据
  56. public static void update(Person p){
  57. try {
  58. Class.forName(DRIVER);
  59. Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
  60. String sql = "update person set name=?,age=?,description=? where id=?";
  61. PreparedStatement ps = conn.prepareStatement(sql);
  62. //设置占位符对应的值
  63. ps.setString(1, p.getName());
  64. ps.setInt(2, p.getAge());
  65. ps.setString(3, p.getDescription());
  66. ps.setInt(4, p.getId());
  67.  
  68. ps.executeUpdate();
  69.  
  70. ps.close();
  71. conn.close();
  72.  
  73. } catch (ClassNotFoundException e) {
  74. e.printStackTrace();
  75. } catch (SQLException e) {
  76. e.printStackTrace();
  77. }
  78. }
  79.  
  80. //方法:使用PreparedStatement删除数据
  81. public static void delete(int id){
  82. try {
  83. Class.forName(DRIVER);
  84. Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
  85. String sql = "delete from person where id=?";
  86. PreparedStatement ps = conn.prepareStatement(sql);
  87. //设置占位符对应的值
  88. ps.setInt(1, id);
  89.  
  90. ps.executeUpdate();
  91.  
  92. ps.close();
  93. conn.close();
  94.  
  95. } catch (ClassNotFoundException e) {
  96. e.printStackTrace();
  97. } catch (SQLException e) {
  98. e.printStackTrace();
  99. }
  100. }
  101.  
  102. // 使用PreparedStatement查询数据
  103. public static Person findById(int id){
  104. Person p = null;
  105. try {
  106. Class.forName(DRIVER);
  107. Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
  108. String sql = "select name,age,description from person where id=?";
  109. PreparedStatement ps = conn.prepareStatement(sql);
  110. //设置占位符对应的值
  111. ps.setInt(1, id);
  112.  
  113. ResultSet rs = ps.executeQuery();
  114. if(rs.next()){
  115. p = new Person();
  116. p.setId(id);
  117. p.setName(rs.getString(1));
  118. p.setAge(rs.getInt(2));
  119. p.setDescription(rs.getString(3));
  120. //把 java.sql.Date 与 java.util.Date之间的转换
  121. // java.util.Date date = rs.getDate(4);
  122. // ps.setDate(4, new java.sql.Date(date.getTime()));
  123.  
  124. }
  125. rs.close();
  126. ps.close();
  127. conn.close();
  128.  
  129. } catch (ClassNotFoundException e) {
  130. e.printStackTrace();
  131. } catch (SQLException e) {
  132. e.printStackTrace();
  133. }
  134. return p;
  135. }
  136.  
  137. }

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. Hibernate之映射文件中索引及约束的使用

    1.添加索引: 在一对多的关系中,在多的一方会产生一个外键,这个外键没有自动 添加索引,当存在从一的一端产生对多的一端的查询时,有可能会在多的一端造成全表查询问题,数据量巨大时会产生严重的性能问题.可 ...

  2. [WP8] ListBox的Item宽度自动填满

    [WP8] ListBox的Item宽度自动填满 范例下载 范例程序代码:点此下载 问题情景 开发WP8应用程序的时候,常常会需要使用ListBox作为容器来呈现各种数据集合.但是在ListBox呈现 ...

  3. CSS3边框温故

    1.简介:border属性在CSS1中就已经定义了,用来设置元素边框风格,设置不同的边框.颜色.粗细 2.基本属性,包括三个类型值:(1)border-width:设置元素边框的粗细,默认3~4px( ...

  4. Javascript中的Label语句

    在javascript中,我们可能很少会去用到 Label 语句,但是熟练的应用 Label 语句,尤其是在嵌套循环中熟练应用 break, continue 与 Label 可以精确的返回到你想要的 ...

  5. javascript宿主对象之window.location

    location属性是一个用来存储当前页面URL信息的对象. 下面我们通过循环来列出location对象的完整属性列表: for(var i in location){ if(typeof locat ...

  6. Android 多语言

    Android 多语言 在res文件上右击创建新的values文件 在strings文件中设置多语言 3.在layout文件中使用 @strings/key 引用相应资源

  7. 高清SDI编码器|上海视涛科技

    SDI编码器(E500)简介 SDI编码器(E500)是上海视涛科技出品的高性能SDI编码产品.该SDI编码器是上海视涛电子完全自主研发,并适用于各种SDI信号的编码采集及网络传输的专用硬件设备.可兼 ...

  8. 解决Sharepoint 2010 custom display form 不显示附件的问题

    sharepoint 2010用designer添加自定义的 display form默认是不会显示附件的. 需要添加如下代码才会显示附件: <tr> <td width=" ...

  9. ShareSDK短信验证码集成详细步骤

    1.这里使用的是ShareSDK网的短信验证码SDK  官网 http://www.mob.com 先去http://www.mob.com/#/reg 注册成为开发者 填写相应的信息,邮箱账号,然后 ...

  10. win7共享文件夹给局域网

    1.设置共享   2.关闭"需要密码访问"