一、 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的MetaDataResultSetMetaData 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。