到学习四个核心对象的最后一个ResultSet,也就是结果集。前面一篇是操作SQL,这篇是对操作结果进行处理。结果集在客户端表现是一张虚拟的表,也就是存储在内存里。ResultSetz有两个功能,一个是对结果集进行封装,第二个是有可移动游标方法。
1.对结果集进行封装
前面的例子,我们对结果集的操作就是打印到控制台。在实际编程中,肯定是需要返回给前端数据的。这个在Java中,是这样处理的。例如Student表中,一行表示一个学生的信息。那么如果把一行看做一个对象,一行里面的年龄,id就是这个对象的属性。我们可以把对象存储在一个集合里,这样就方便后续操作和给前端数据。所以,接下来,我们对结果集进行封装成对象,然后存储到集合里。在封装之前,我们需要写一个Java Bean类。什么是Bean类呢?就是一个类,里面属性全部是私有的,提供set和get方法,和一个无参构造方法。
package demo;
public class Student {
private int Id;
private String Name;
private String Gender;
private int Age;
public Student() {}
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getGender() {
return Gender;
}
public void setGender(String gender) {
Gender = gender;
}
public int getAge() {
return Age;
}
public void setAge(int age) {
Age = age;
}
@Override
public String toString() {
return "Student [Id=" + Id + ", Name=" + Name + ", Gender=" + Gender + ", Age=" + Age + ", toString()="
+ super.toString() + "]";
}
}
Bean类创建好了之后,接下来,我们要思考,如何把数据库中不同类型的数据,转成Java中的数据类型。例如数据库中也有int float double char varchar date这些类型和Java中的int byte short float double String如何对应起来呢。
下面这个表格,左侧是Java的数据类型/对象,右侧是数据库的数据类型的对应关系
byte | tityint |
short | smallint |
int | int |
long | bigint |
float | float |
double | double |
String | char varchar |
Date | date |
我们Student表中的,ID和Age对应数据类型是int, Name和性别对应的是varchar,也就是Java Bean类我们用String对象来保存从数据库拿到的内容。搞清楚了这个数据类型对应关系,接下来我们要学习JDBC中ResultSet提供的相关get方法。
Object getObject(int columnIndex) | 根据列的序号取值,索引从1开始 |
Object getObject(String columnName) | 根据列的名称取值 |
上面这两个方法可以拿到结果集中任意的一列,那么如何拿到列中的一些字段/属性的值呢,下面表格的方法就是获取结果集当前行的不同数据类型的get方法。
int getInt(int colIndex) | 以int形式获取ResultSet结果集当前行指定列号值 |
int getInt(String colLabel) | 以int形式获取ResultSet结果集当前行指定列名值 |
float getFloat(int colIndex) | 以float形式获取ResultSet结果集当前行指定列号值 |
float getFloat(String colLabel) | 以float形式获取ResultSet结果集当前行指定列名值 |
String getString(int colIndex) | 以String形式获取ResultSet结果集当前行指定列号值 |
String getString(String colLabel) | 以String形式获取ResultSet结果集当前行指定列名值 |
Date getDate(int colIndex) | 以Date形式获取ResultSet结果集当前行指定列号值 |
Date getDate(String colLabel) | 以Date形式获取ResultSet结果集当前行指定列名值 |
下面,我们来用Junit写一个测试例子,把查询到全部信息封装成Student对象并存储到ArrayList中去,然后遍历ArrayList看看效果。
package demo;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
public class TestDemo1 {
@Test
public void test1() throws Exception {
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接Connection对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "root", "123456");
//得到执行SQL语句的Statement对象
Statement stmt = conn.createStatement();
//执行SQL语句,拿到结果集
ResultSet rs = stmt.executeQuery("SELECT * FROM student");
//封装结果集成Java 自定义对象
List<Student> list = new ArrayList<Student>();
while(rs.next()) {
Student s = new Student();
s.setName(rs.getString("Name"));
s.setId(rs.getInt("ID"));
s.setGender(rs.getString("Gender"));
s.setAge(rs.getInt("Age"));
// 把student对象添加到集合中
list.add(s);
}
//遍历集合
for (Student student : list) {
System.out.println(student.toString());
}
//关闭资源
rs.close();
stmt.close();
conn.close();
}
}
测试结果:
Student [Id=192101, Name=Tom, Gender=male, Age=19, toString()=demo.Student@5bcea91b]
Student [Id=192102, Name=Lucy, Gender=female, Age=18, toString()=demo.Student@5f3a4b84]
Student [Id=192103, Name=Dniel, Gender=male, Age=20, toString()=demo.Student@27f723]
Student [Id=192104, Name=Sunny, Gender=female, Age=18, toString()=demo.Student@670b40af]
Student [Id=192105, Name=Anthony, Gender=male, Age=23, toString()=demo.Student@4923ab24]
通过打印内容,我们知道确实存储到了Bean类,也就是高级对象中,如果前端开发需要利用这些数据,可能还需要进一步序列化成JSON数据或者文件。上面我们只尝试了几个get方法,还有几个类型没有用到,例如getDate(), 如果内容是一个日期时间,我们在Java中建议使用util包下的Date,而不是sql包下的Date,因为sql包下Date继承了util包下的Date,所以使用父类来保存变量就好。
2.可移动游标方法
boolean next() | 将游标从当前位置向下移动一行 |
boolean previous() | 将游标从当前位置向上移动一行 (很少用) |
boolean absolute(int row) | 参数是当前行的索引,从1开始,根据行的索引定位移动到指定行 |
void afterLast() | 游标移动到最后一行之后 |
void beforeFirst() | 游标移动到开头,也就是第一行之前,表头 |
什么是游标,在ResultSet结果集中提供了游标方法。JDK文档中有这么一句话:ResultSet
对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next
方法将光标移动到下一行;因为该方法在 ResultSet
对象没有下一行时返回 false
,所以可以在 while
循环中使用它来迭代结果集。
如何理解游标默认放在第一行之前,一张表中,第一行之前也就是表头的列名。这里也提到next方法,就是游标从第一行之前,一行一行往下走,如果next()方法返回true,所以,我们前面用到while(rs.next()),为什么这么用,就是这个原因。既然有Next方法,这里也说下previous方法,现在基本上不用这个方法。就是让游标往上移动一行,我们习惯使用next方法。
最后,简单看看ResultSet 的close方法,这个方法就是用来关闭资源的,没什么好说的。下面简单些一个例子,游标移动到最后一行之后位置,然后使用previous方法定位到最后一行,读取最后一行的数据。
@Test
public void test2() throws Exception {
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接Connection对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "root", "123456");
//得到执行SQL语句的Statement对象
Statement stmt = conn.createStatement();
//执行SQL语句,拿到结果集
ResultSet rs = stmt.executeQuery("SELECT * FROM student");
//封装结果集成Java 自定义对象
List<Student> list = new ArrayList<Student>();
//移动最后一行
rs.afterLast();
rs.previous(); //此时就在最后一行
Student s = new Student();
s.setName(rs.getString("Name"));
s.setId(rs.getInt("ID"));
s.setGender(rs.getString("Gender"));
s.setAge(rs.getInt("Age"));
// 把student对象添加到集合中
list.add(s);
//遍历集合
for (Student student : list) {
System.out.println(student.toString());
}
//关闭资源
rs.close();
stmt.close();
conn.close();
}
测试结果
Student [Id=192105, Name=Anthony, Gender=male, Age=23, toString()=demo.Student@7a3d45bd]
根据前面test1()测试打印全部表内容,这里打印一行数据确实是最后一行获取的数据。