zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    BZOJ 4511 洛谷3131 USACO 16.Jan 七子共
    Atcoder Code Festival 2017 qual C 10.22 D题题解
    hdu 5122(2014ACM/ICPC亚洲区北京站) K题 K.Bro Sorting
    HDU 5115 (2014ACM/ICPC亚洲区北京站) D题(Dire Wolf)
    POJ
    hihocoder 1032 最长回文子串(Manacher)
    hihocoder 1015 KMP算法
    Trie树 hihocoder 1014
    POJ 3468 线段树区间修改查询(Java,c++实现)
    atCoder Ants on a Circle(又是蚂蚁问题。。。)
  • 原文地址:https://www.cnblogs.com/hehe520/p/6330016.html
Copyright © 2011-2022 走看看