JDBC的查询操作
ResultSet 是 JDBC (Java 数据库连接) API 提供的接口,它用于表示 SQL 查询的结果集。ResultSet 对象中包含了查询结果的所有行,可以通过 next() 方法逐行地获取并处理每一行的数据。它最常用于执行 SELECT 语句查询出来的结果集。
ResultSet 的遍历是基于 JDBC 的流式处理机制的,即一行一行地获取结果,避免将所有结果全部取出后再进行处理导致内存溢出问题。
在使用 ResultSet 遍历查询结果时,一般会采用以下步骤:
- 执行 SQL 查询,获取 ResultSet 对象。
- 使用 ResultSet 的 next() 方法移动游标指向结果集的下一行,判断是否有更多的数据行。
- 如果有更多的数据行,则使用 ResultSet 对象提供的 getXXX() 方法获取当前行的各个字段(XXX 表示不同的数据类型)。例如,getLong(“id”) 方法用于获取当前行的 id 列对应的 Long 类型的值。
- 处理当前行的数据,例如将其存入 Java 对象中。
- 重复执行步骤 2~4,直到结果集中的所有行都被遍历完毕。
- 调用 ResultSet 的 close() 方法释放资源。
需要注意的是,在使用完 ResultSet 对象之后,需要及时关闭它,以释放数据库资源并避免潜在的内存泄漏问题。否则,如果在多个线程中打开了多个 ResultSet 对象,并且没有正确关闭它们的话,可能会导致数据库连接过多,从而影响系统的稳定性和性能。
通过列索引获取数据(以String类型获取)
需求:获取t_user表中所有数据,在控制台打印输出每一行的数据。
1
| select id,name,password,realname,gender,tel from t_user;
|
要查询的数据如下图:
代码如下(重点关注第4步 第5步 第6步):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.util.ResourceBundle; import java.sql.ResultSet;
public class JDBCTest09 { public static void main(String[] args){ ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); String driver = bundle.getString("driver"); String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password");
Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
String sql = "select id,name,password,realname,gender,tel from t_user"; rs = stmt.executeQuery(sql);
while(rs.next()){ String id = rs.getString(1); String name = rs.getString(2); String pwd = rs.getString(3); String realname = rs.getString(4); String gender = rs.getString(5); String tel = rs.getString(6); System.out.println(id + "\t" + name + "\t" + pwd + "\t" + realname + "\t" + gender + "\t" + tel); } } catch(SQLException | ClassNotFoundException e){ e.printStackTrace(); } finally { if(rs != null){ try{ rs.close(); }catch(SQLException e){ e.printStackTrace(); } } if(stmt != null){ try{ stmt.close(); }catch(SQLException e){ e.printStackTrace(); } } if(conn != null){ try{ conn.close(); }catch(SQLException e){ e.printStackTrace(); } } } } }
|
执行结果如下:
代码解读:
1 2 3
| String sql = "select id,name,password,realname,gender,tel from t_user"; rs = stmt.executeQuery(sql);
|
执行insert delete update语句的时候,调用Statement接口的executeUpdate()方法。
执行select语句的时候,调用Statement接口的executeQuery()方法。执行select语句后返回结果集对象:ResultSet。
代码解读:
1 2 3 4 5 6 7 8 9 10
| while(rs.next()){ String id = rs.getString(1); String name = rs.getString(2); String pwd = rs.getString(3); String realname = rs.getString(4); String gender = rs.getString(5); String tel = rs.getString(6); System.out.println(id + "\t" + name + "\t" + pwd + "\t" + realname + "\t" + gender + "\t" + tel); }
|
- rs.next() 将游标移动到下一行,如果移动后指向的这一行有数据则返回true,没有数据则返回false。
- while循环体当中的代码是处理当前游标指向的这一行的数据。(注意:是处理的一行数据)
- rs.getString(int columnIndex) 其中 int columnIndex 是查询结果的列下标,列下标从1开始,以1递增。
- rs.getString(…) 方法在执行时,不管底层数据库中的数据类型是什么,统一以字符串String类型来获取。
代码解读:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| if(rs != null){ try{ rs.close(); }catch(SQLException e){ e.printStackTrace(); } } if(stmt != null){ try{ stmt.close(); }catch(SQLException e){ e.printStackTrace(); } } if(conn != null){ try{ conn.close(); }catch(SQLException e){ e.printStackTrace(); } }
|
ResultSet最终也是需要关闭的。先关闭ResultSet,再关闭Statement,最后关闭Connection。
通过列名获取数据(以String类型获取)
获取当前行的数据,不仅可以通过列下标获取,还可以通过查询结果的列名来获取,通常这种方式是被推荐的,因为可读性好。
例如这样的SQL:
1
| select id, name as username, realname from t_user;
|
执行结果是:
我们可以按照查询结果的列名来获取数据:
注意:是根据查询结果的列名,而不是表中的列名。以上查询的时候将字段name起别名username了,所以要根据username来获取,而不能再根据name来获取了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.util.ResourceBundle; import java.sql.ResultSet;
public class JDBCTest10 { public static void main(String[] args){ ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); String driver = bundle.getString("driver"); String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password");
Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
String sql = "select id,name as username,realname from t_user"; rs = stmt.executeQuery(sql);
while(rs.next()){ String id = rs.getString("id"); String name = rs.getString("username"); String realname = rs.getString("realname"); System.out.println(id + "\t" + name + "\t" + realname); } } catch(SQLException | ClassNotFoundException e){ e.printStackTrace(); } finally { if(rs != null){ try{ rs.close(); }catch(SQLException e){ e.printStackTrace(); } } if(stmt != null){ try{ stmt.close(); }catch(SQLException e){ e.printStackTrace(); } } if(conn != null){ try{ conn.close(); }catch(SQLException e){ e.printStackTrace(); } } } } }
|
执行结果如下:
如果将上面代码中rs.getString("username")
修改为rs.getString("name")
,执行就会出现以下错误:
提示name列是不存在的。所以一定是根据查询结果中的列名来获取,而不是表中原始的列名。
以指定的类型获取数据
前面的程序可以看到,不管数据库表中是什么数据类型,都以String类型返回。当然,也能以指定类型返回。
使用PowerDesigner再设计一张商品表:t_product,使用Navicat for MySQL工具准备数据如下:
id以long类型获取,name以String类型获取,price以double类型获取,create_time以java.sql.Date类型获取,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.util.ResourceBundle; import java.sql.ResultSet;
public class JDBCTest11 { public static void main(String[] args){ ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); String driver = bundle.getString("driver"); String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password");
Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
String sql = "select id,name,price,create_time as createTime from t_product"; rs = stmt.executeQuery(sql);
while(rs.next()){ long id = rs.getLong("id"); String name = rs.getString("name"); double price = rs.getDouble("price"); java.sql.Date createTime = rs.getDate("createTime"); System.out.println(id + "\t" + name + "\t" + price * 2 + "\t" + createTime); } } catch(SQLException | ClassNotFoundException e){ e.printStackTrace(); } finally { if(rs != null){ try{ rs.close(); }catch(SQLException e){ e.printStackTrace(); } } if(stmt != null){ try{ stmt.close(); }catch(SQLException e){ e.printStackTrace(); } } if(conn != null){ try{ conn.close(); }catch(SQLException e){ e.printStackTrace(); } } } } }
|
执行结果如下:
获取结果集的元数据信息(了解)
ResultSetMetaData 是一个接口,用于描述 ResultSet 中的元数据信息,即查询结果集的结构信息,例如查询结果集中包含了哪些列,每个列的数据类型、长度、标识符等。
ResultSetMetaData 可以通过 ResultSet 接口的 getMetaData() 方法获取,一般在对 ResultSet 进行元数据信息处理时使用。例如,可以使用 ResultSetMetaData 对象获取查询结果中列的信息,如列名、列的类型、列的长度等。通过 ResultSetMetaData 接口的方法,可以实现对查询结果的基本描述信息操作,例如获取查询结果集中有多少列、列的类型、列的标识符等。以下是一段通过 ResultSetMetaData 获取查询结果中列的信息的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.util.ResourceBundle; import java.sql.ResultSet; import java.sql.ResultSetMetaData;
public class JDBCTest12 { public static void main(String[] args){ ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); String driver = bundle.getString("driver"); String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password");
Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
String sql = "select id,name,price,create_time as createTime from t_product"; rs = stmt.executeQuery(sql);
ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); for (int i = 1; i <= columnCount; i++) { System.out.println("列名:" + rsmd.getColumnName(i) + ",数据类型:" + rsmd.getColumnTypeName(i) + ",列的长度:" + rsmd.getColumnDisplaySize(i)); } } catch(SQLException | ClassNotFoundException e){ e.printStackTrace(); } finally { if(rs != null){ try{ rs.close(); }catch(SQLException e){ e.printStackTrace(); } } if(stmt != null){ try{ stmt.close(); }catch(SQLException e){ e.printStackTrace(); } } if(conn != null){ try{ conn.close(); }catch(SQLException e){ e.printStackTrace(); } } } } }
|
执行结果如下:
在上面的代码中,我们首先创建了一个 Statement 对象,然后执行了一条 SQL 查询语句,生成了一个 ResultSet 对象。接下来,我们通过 ResultSet 对象的 getMetaData() 方法获取了 ResultSetMetaData 对象,进而获取了查询结果中列的信息并进行输出。需要注意的是,在进行列信息的获取时,列的编号从 1 开始计算。该示例代码将获取查询结果集中所有列名、数据类型以及长度等信息。
获取新增行的主键值
有很多表的主键字段值都是自增的,在某些特殊的业务环境下,当我们插入了新数据后,希望能够获取到这条新数据的主键值,应该如何获取呢?
在 JDBC 中,如果要获取插入数据后的主键值,可以使用 Statement 接口的 executeUpdate() 方法的重载版本,该方法接受一个额外的参数,用于指定是否需要获取自动生成的主键值。然后,通过以下两个步骤获取插入数据后的主键值:
- 在执行 executeUpdate() 方法时指定一个标志位,表示需要返回插入的主键值。
- 调用 Statement 对象的 getGeneratedKeys() 方法,返回一个包含插入的主键值的 ResultSet 对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.util.ResourceBundle; import java.sql.ResultSet;
public class JDBCTest13 { public static void main(String[] args){ ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); String driver = bundle.getString("driver"); String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password");
Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
String sql = "insert into t_user(name,password,realname,gender,tel) values('zhangsan','111','张三','男','19856525352')"; int count = stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); rs = stmt.getGeneratedKeys(); if(rs.next()){ int id = rs.getInt(1); System.out.println("新增数据行的主键值:" + id); } } catch(SQLException | ClassNotFoundException e){ e.printStackTrace(); } finally { if(rs != null){ try{ rs.close(); }catch(SQLException e){ e.printStackTrace(); } } if(stmt != null){ try{ stmt.close(); }catch(SQLException e){ e.printStackTrace(); } } if(conn != null){ try{ conn.close(); }catch(SQLException e){ e.printStackTrace(); } } } } }
|
执行结果如下:
以上代码中,我们将 Statement.RETURN_GENERATED_KEYS 传递给 executeUpdate() 方法,以指定需要获取插入的主键值。然后,通过调用 Statement 对象的 getGeneratedKeys() 方法获取包含插入的主键值的 ResultSet 对象,通过 ResultSet 对象获取主键值。需要注意的是,在使用 Statement 对象的 getGeneratedKeys() 方法获取自动生成的主键值时,主键值的获取方式具有一定的差异,需要根据不同的数据库种类和版本来进行调整。
使用IDEA工具编写JDBC程序
创建空的工程并设置JDK
创建一个空的工程:mypro
工程结构:
设置JDK以及编译器版本:
创建一个模块
将驱动加入到CLASSPATH
在模块jdbc下创建一个目录:lib
将mysql的驱动jar包拷贝到lib目录当中:
将jar包加入到classpath:
编写JDBC程序
新建软件包:com.powernode.jdbc
新建JDBCTest01类:
在JDBCTest01类中编写main方法,main方法中编写JDBC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| package com.powernode.jdbc;
import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.util.ResourceBundle; import java.sql.ResultSet;
public class JDBCTest01 { public static void main(String[] args){
ResourceBundle bundle = ResourceBundle.getBundle("com.powernode.jdbc.jdbc"); String driver = bundle.getString("driver"); String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password");
Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
String sql = "select id,name,password from t_user"; rs = stmt.executeQuery(sql); while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); String pwd = rs.getString("password"); System.out.println(id + "," + name + "," + pwd); }
} catch(SQLException | ClassNotFoundException e){ e.printStackTrace(); } finally { if(rs != null){ try{ rs.close(); }catch(SQLException e){ e.printStackTrace(); } } if(stmt != null){ try{ stmt.close(); }catch(SQLException e){ e.printStackTrace(); } } if(conn != null){ try{ conn.close(); }catch(SQLException e){ e.printStackTrace(); } } } } }
|
提供配置文件,在com.powernode.jdbc包下新建jdbc.properties文件:
jdbc.properties文件中如下配置:
1 2 3 4
| driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/jdbc?useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=utf-8 user=root password=123456
|
执行结果如下: