Java JDBC学习实战(二): 管理结果集

简介:
在我的上一篇博客《Java JDBC学习实战(一): JDBC的基本操作》中,简要介绍了jdbc开发的基本流程,并详细介绍了Statement和PreparedStatement的使用:利用这两个API可以执行SQL语句,完成基本的CURD操作。那么,当我们进行查询操作,查询到了结果集,该如何处理呢? Java提供了一个API,专门用于表示查询的结果集——ResultSet。此外,还提供了一个结果集的分析工具——ResultSetMetaData。


一、 ResultSet的介绍

1.1 可移动、可更新的ResultSet
 《Java JDBC学习实战(一): JDBC的基本操作》一文里,介绍过ResultSet的相关方法,可以通过一系列的方法来移动记录指针,如:absolute、previous、next、first、last、beforeFirst、afterLast等方法。
ResultSet默认是不支持更新的,如果希望ResultSet完成更新操作,必须在创建Statement或PrepareStatement时传入一些参数。
Connection对象在创建Statement或PrepareStatement时可以传入两个参数:
A、 resultSetType:控制ResultSet的类型,该参数有以下三个值:
    a、 ResultSet.TYPE_FORWARD_ONLY该常量控制记录指针只能向前移动。
    b、 ResultSet.TYPE_SCROLL_INSENSITIVE:该常量控制记录指针自由移动(可滚动结果集),但底层的数据改变不影响结果集ResultSet的内容
    c、 ResultSet.TYPE_SCROLL_SENSITIVE:该常量控制记录指针自由移动,但底层数据的影响会改变结果集ResultSet的内容
B、 resultSetConcurrency:控制ResultSet的并发类型,该参数可以接收如下两个值:
    a、 ResultSet.CONCUR_READ_ONLY:该常量表示ResultSet是只读并发模式
    b、 ResultSet.CONCUR_UPDATABLE:该常量表示ResultSet是更新并发模式
通过PrepareStatement、Statement的创建时进行参数设置来创建可滚动、可更新的ResultSet,然后通过rs的updateXxx方法来完成某列的更新值设置,通过updateRow来提交修改。

// 使用Connection创建一个PreparedStatement对象
// 传入控制结果集可滚动、可更新的参数
PreparedStatement pstmt = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
		



1.2、 ResultSet中的二进制Blob数据处理

Blob类型通常用来存储文件,如:图片、音频、视频文件。将文件转换成二进制保存在数据库中,取出来的时候可以二进制数据恢复成文件。

如果要插入图片到数据库,显然不能直接设置SQL参数拼接字符串进行插入。因为二进制常量无法表示。

但是将Blob类型数据插入到数据可以用PrepareStatement,通过PrepareStatement对象的setBinaryStream方法将参数传入到二进制输入流;也可以用Blob对象的getBytes方法直接取出数据。


二、 操作可滚动可更新的结果集

示例:(来自《疯狂Java讲义》)

public class ResultSetTest
{
	private String driver;
	private String url;
	private String user;
	private String pass;
	public void initParam(String paramFile)throws Exception
	{
		// 使用Properties类来加载属性文件
		Properties props = new Properties();
		props.load(new FileInputStream(paramFile));
		driver = props.getProperty("driver");
		url = props.getProperty("url");
		user = props.getProperty("user");
		pass = props.getProperty("pass");
	}
	public void query(String sql)throws Exception
	{
		// 加载驱动
		Class.forName(driver);
		try(
			// 获取数据库连接
			Connection conn = DriverManager.getConnection(url
				, user , pass);
			// 使用Connection来创建一个PreparedStatement对象
			// 传入控制结果集可滚动,可更新的参数。
			PreparedStatement pstmt = conn.prepareStatement(sql 
				, ResultSet.TYPE_SCROLL_INSENSITIVE
				, ResultSet.CONCUR_UPDATABLE);
			ResultSet rs = pstmt.executeQuery())
		{
			rs.last();// 指针移动到结果集的最后
			int rowCount = rs.getRow();
			for (int i = rowCount; i > 0 ; i-- )
			{
				rs.absolute(i);// 指针移动到指定位置
				System.out.println(rs.getString(1) + "\t"
					+ rs.getString(2) + "\t" + rs.getString(3));
				// 修改记录指针所有记录、第2列的值
				rs.updateString(2 , "学生名" + i);
				// 提交修改
				rs.updateRow();
			}
		}
	}
	public static void main(String[] args) throws Exception
	{
		ResultSetTest rt = new ResultSetTest();
		rt.initParam("mysql.ini");
		rt.query("select * from student_table");
	}
}


注: 如果要创建可更新的结果集,则使用查询的数据通常只能来自一个数据表,而且查询结果集中的数据列必须包含主键列,否则将会更新失败。

三、 处理Blob类型数据

比如我们有如下数据表,表中的字段img_data类型为mediumblob,专门保存图片数据

create table img_table(

   img_id int auto_increment primary key,

   img_name varchar(255),

   #创建一个mediumblob类型的数据列,用于保存图片数据

   img_data mediumblob

);


之前已经讲过,操作图片数据,需要通过PrepareStatement对象的setBinaryStream方法来实现.

public void upload(String fileName)
{
  // 截取文件名
  String imageName = fileName.substring(fileName.lastIndexOf('\\')+ 1 , fileName.lastIndexOf('.'));
  File f = new File(fileName);
  try(
       InputStream is = new FileInputStream(f))
       {
          // 设置图片名参数
          insert.setString(1, imageName);
          // 设置二进制流参数
          insert.setBinaryStream(2, is , (int)f.length());  
          int affect = insert.executeUpdate();
          if (affect == 1)
          {
              // 重新更新ListModel,将会让JList显示最新的图片列表
              fillListModel();
          }
       }
       catch (Exception e)
       {
          e.printStackTrace();
       }
}	

可见,上述程序已经能完成图片数据的插入操作,那如何读取数据库的图片数据呢?ResultSet结果集可以直接通过getBlob()方法,得到Blob数据,可以再将其转为Stream进行操作。

// ---------根据图片ID来显示图片----------
	public void showImage(int id)throws SQLException
	{
		// 设置参数
		query.setInt(1, id);
		try(	
			// 执行查询
			ResultSet rs = query.executeQuery())
		{
			if (rs.next())
			{
				// 取出Blob列
				Blob imgBlob = rs.getBlob(1);
				// 取出Blob列里的数据
				ImageIcon icon=new ImageIcon(imgBlob.getBytes(1L
					,(int)imgBlob.length()));
				imageLabel.setIcon(icon);
			}
		}
	}
	public static void main(String[] args)throws SQLException
	{
		new BlobTest().init();
	}
}


四、 使用ResultSetMetaData分析结果集

在我们查询数据返回的结果集中,我们不清楚结果集存放的数据类型、数据列数。
那样我们就可以用ResultSetMetaData来读取ResultSet的信息。
通过ResultSet的getMetaData()的方法可以获取ResultSetMetaData对象。
然后可以用ResultSetMetaData对象的方法来操作ResultSet,常用方法如下:
int getColumnCount():返回ResultSet的列名数量
int getColumnType(int column):返回指定索引的类型
String getColumnName(int column):返回指定索引的列名


     try(
          // 根据用户输入的SQL执行查询
          ResultSet rs = stmt.executeQuery(sqlField.getText()))
          {
             // 取出ResultSet的MetaData
             ResultSetMetaData rsmd = rs.getMetaData();
             Vector<String> columnNames =  new Vector<>();
             Vector<Vector<String>> data = new Vector<>();
             // 把ResultSet的所有列名添加到Vector里
             for (int i = 0 ; i < rsmd.getColumnCount(); i++ )
             {
                columnNames.add(rsmd.getColumnName(i + 1));
             }
             // 把ResultSet的所有记录添加到Vector里
             while (rs.next())
             {
                 Vector<String> v = new Vector<>();
                 for (int i = 0 ; i < rsmd.getColumnCount(); i++ )
                 {
                      v.add(rs.getString(i + 1));
                 }
                 data.add(v);
              }

          }
          catch (Exception e)
          {
              e.printStackTrace();
      }


注:虽然,ResultSetMetaData可以准确地分析出ResultSet里包含了多少列,以及每列的列名、数据类型等,但使用ResuleSetMetaData需要一定的系统开销,开发中尽量不要使用该API。

相关文章
|
3月前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
94 2
|
19天前
|
Java 数据库连接 数据库
springboot java.lang.ClassNotFoundException: dm.jdbc.driver.DmDriver应该如何解决
通过上述步骤,可以有效解决Spring Boot项目中遇到的 `java.lang.ClassNotFoundException: dm.jdbc.driver.DmDriver`问题。确保在项目中正确添加达梦数据库的JDBC驱动依赖,并在配置文件中正确配置数据源信息,是解决此问题的关键。通过这些方法,可以确保Spring Boot项目能够正确连接达梦数据库并正常运行。
144 31
|
3月前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
110 43
Java学习十六—掌握注解:让编程更简单
|
22天前
|
Java
Java基础却常被忽略:全面讲解this的实战技巧!
本次分享来自于一道Java基础的面试试题,对this的各种妙用进行了深度讲解,并分析了一些关于this的常见面试陷阱,主要包括以下几方面内容: 1.什么是this 2.this的场景化使用案例 3.关于this的误区 4.总结与练习
|
1月前
|
Java 程序员
Java基础却常被忽略:全面讲解this的实战技巧!
小米,29岁程序员,分享Java中`this`关键字的用法。`this`代表当前对象引用,用于区分成员变量与局部变量、构造方法间调用、支持链式调用及作为参数传递。文章还探讨了`this`在静态方法和匿名内部类中的使用误区,并提供了练习题。
44 1
|
2月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
73 6
|
2月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
3月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
3月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
49 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
2月前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。