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。

时间: 2024-11-03 03:57:41

Java JDBC学习实战(二): 管理结果集的相关文章

Java JDBC学习实战(三): 事务管理

一. 数据库的事务特性 事务是一步或多步组成操作序列组成的逻辑执行单元,这个序列要么全部执行,要么则全部放弃执行. 事务的四个特性:原子性(Atomicity).一致性(Consistency).隔离性(IsoIation)和持续性(Durability)原子性(Atomicity):事务应用最小的执行单元,不可再分.是事务中不可再分的最小逻辑执行体. 一致性(Consistency):事务的执行结果,必须使数据库的从一个一致性的状态变到另一个一致性的状态. 隔离线(IsoIation):各个事

Java JDBC学习实战(一): JDBC的基本操作

一.JDBC常用接口.类介绍 JDBC提供对独立于数据库统一的API,用以执行SQL命令.API常用的类.接口如下: DriverManager,管理JDBC驱动的服务类,主要通过它获取Connection数据库链接,常用方法如下: static synchronized Connection getConnection(String url, String user, String password) throws Exception;该方法获得url对应的数据库的连接. Connection

java io学习(二)ByteArrayOutputStream的简介,源码分析和示例

ByteArrayOutputStream的简介,源码分析和示例(包括OutputStream) 前面学习ByteArrayInputStream,了解了"输入流".接下来,我们学习与ByteArrayInputStream相对应的输出流,即ByteArrayOutputStream. 本章,我们会先对ByteArrayOutputStream进行介绍,在了解了它的源码之后,再通过示例来掌握如何使用它. ByteArrayOutputStream 介绍 ByteArrayOutputS

Java进阶学习(二) 异常处理

程序很难做到完美,不免有各种各样的异常.比如程序本身有bug,比如程序打印时打印机没有纸了,比如内存不足.为了解决这些异常,我们需要知道异常发生的原因.对于一些常见的异常,我们还可以提供一定的应对预案.C语言中的异常处理是简单的通过函数返回值来实现的,但返回值代表的含义往往是由惯例决定的.程序员需要查询大量的资料,才可能找到一个模糊的原因.面向对象语言,比如C++, Java, Python往往有更加复杂的异常处理机制.这里讨论Java中的异常处理机制. Java异常处理 异常处理 Java的异

Java网络学习(二) Servlet开胃酒

在Tomcat初次尝试中,我们搭建了用于测试Servlet和JSP的基本环境.我们现在要转向网络应用的具体开发. 厨师 Web服务器的基本工作方式是请求-处理-回复.请求和回复是在网络上,以HTTP协议为基础的通信(参考HTTP协议).请求是客户点菜,回复像是服务员上菜,而处理则是在后厨中,厨师根据请求的菜单,准备菜品的过程. 厨师通常是服务器上的一个应用程序.这个应用程序可以提取请求中的信息,并根据这些信息准备回复.这样的应用程序可以是许多中语言写成的,比如C, C++, Perl, Ruby

java io学习(二十五) RandomAccessFile

RandomAccessFile RandomAccessFile 是随机访问文件(包括读/写)的类.它支持对文件随机访问的读取和写入,即我们可以从指定的位置读取/写入文件数据. 需要注意的是,RandomAccessFile 虽然属于java.io包,但它不是InputStream或者OutputStream的子类:它也不同于FileInputStream和FileOutputStream. FileInputStream 只能对文件进行读操作,而FileOutputStream 只能对文件进

java io学习(二十四) PrintWriter (字符打印输出流)

PrintWriter 介绍 PrintWriter 是字符类型的打印输出流,它继承于Writer. PrintStream 用于向文本输出流打印对象的格式化表示形式.它实现在 PrintStream 中的所有 print 方法.它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入. PrintWriter 函数列表 PrintWriter(OutputStream out) PrintWriter(OutputStream out, boolean autoFlus

java io学习(二十三) BufferedWriter(字符缓冲输出流)

BufferedWriter 介绍 BufferedWriter 是缓冲字符输出流.它继承于Writer. BufferedWriter 的作用是为其他字符输出流添加一些缓冲功能. BufferedWriter 函数列表 // 构造函数 BufferedWriter(Writer out) BufferedWriter(Writer out, int sz) void close() // 关闭此流,但要先刷新它. void flush() // 刷新该流的缓冲. void newLine()

java io学习(二十二) BufferedReader(字符缓冲输入流)

BufferedReader 介绍 BufferedReader 是缓冲字符输入流.它继承于Reader. BufferedReader 的作用是为其他字符输入流添加一些缓冲功能. BufferedReader 函数列表 BufferedReader(Reader in) BufferedReader(Reader in, int size) void close() void mark(int markLimit) boolean markSupported() int read() int