• 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) + "	"
    					+ rs.getString(2) + "	" + 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。

  • 相关阅读:
    Java.io.outputstream.PrintStream:打印流
    Codeforces 732F. Tourist Reform (Tarjan缩点)
    退役了
    POJ 3281 Dining (最大流)
    Light oj 1233
    Light oj 1125
    HDU 5521 Meeting (最短路)
    Light oj 1095
    Light oj 1044
    HDU 3549 Flow Problem (dinic模版 && isap模版)
  • 原文地址:https://www.cnblogs.com/hehe520/p/6330016.html
Copyright © 2020-2023  润新知