結果集指針的移動
while (resultSet.next()){ //...... }
指針最初指向第一條記錄之前,next()是指向下一個位置,返回的是boolean值,true表示有內容(記錄),false表示無內容(false)。
如果當前是指向最后一條記錄,next()就指向最后一條記錄之后,返回false,退出循環,遍歷完成。
准確地說,應該叫做游標(Cursor),學C++的時候叫指針(Pointer)叫習慣了......
//下面3種都是相對當前位置的移動 resultSet.next(); //指針移到下一個位置 resultSet.previous(); //指針移到前一個位置 resultSet.relative(2); //相對當前位置后移2步 resultSet.relative(-1); //正數表示后移,負數表示前移。相對當前位置前移1步。 //下面5種都是絕對位置的移動 resultSet.absolute(n); //指向結果集中的第n+1條記錄,n是下標,0表示第一條記錄
resultSet.first(); //指針移到第一條記錄上 resultSet.last(); //指針移到最后一條記錄上 resultSet.beforeFirst(); //指針移到第一條記錄之前 resultSet.afterLast(); //指針移到最后一條記錄之后 //對應的判斷方法 resultSet.isFirst(); resultSet.isLast(); resultSet.isBeforeFirst(); resultSet.isAfterLast();
獲取結果集中的記錄總數
1 if(resultSet.last()) { //指針移到最后一條記錄上,使用if是因為結果集可能是空的,要先判斷 2 System.out.println(resultSet.getRow()); //getRow()是返回當前記錄是結果集中的第幾條記錄,int型 3 }
離線查詢
如果要長期使用結果集中的數據,以前有2種方式:
- 一直保持數據庫連接,不關閉。如果保持的連接很多,服務器、數據庫的性能都會受到影響。
- 將結果集中的記錄存儲到多個JavaBean中,數據庫沒影響,但要編寫JavaBean,寫代碼遍歷結果集,將每行記錄賦給JavaBean,自己寫代碼的話很麻煩。如果記錄很多,創建大量的JavaBean是很花時間的,JVM要管理大量的JavaBean對象,開銷很大,程序性能降低;且操作結果集比操作JavaBean的集合要簡單。
離線查詢:在本地搞一個結果集的副本,關閉結果集、數據庫連接,使用本地的這個副本。
ResultSet接口有子接口RowSet,RowSet有子接口CachedRowSet。離線查詢就是通過CachedRowSet來實現的。
1 //從properties文件中加載數據庫配置 2 Properties properties = new Properties(); 3 InputStream inputStream =Class.forName("test.Test").getResourceAsStream("/mysql.properties"); 4 properties.load(inputStream); 5 6 String driver = properties.getProperty("driver"); 7 String url = properties.getProperty("url"); 8 String user = properties.getProperty("user"); 9 String pwd=properties.getProperty("password"); 10 11 Class.forName(driver); 12 Connection connection = DriverManager.getConnection(url, user, pwd); 13 14 String sql = "select * from student_tb"; 15 PreparedStatement preparedStatement = connection.prepareStatement(sql); 16 ResultSet resultSet = preparedStatement.executeQuery(); 17 18 //離線查詢 19 RowSetFactory rowSetFactory = RowSetProvider.newFactory(); //通過RowSetProvider的靜態方法創建RowSetFactory對象 20 CachedRowSet cachedRowSet = rowSetFactory.createCachedRowSet(); //創建CachedRowSet對象 21 cachedRowSet.populate(resultSet); //使用結果集填充cachedRowSet 22 //cachedRowSet.populate(resultSet,2); //可指定從結果集的第幾條記錄開始填充,默認為1,從第一條記錄開始填充 23 24 //關閉數據庫資源 25 resultSet.close(); 26 preparedStatement.close(); //關閉Statement|PreparedStatement對象 27 connection.close(); 28 29 //CachedRowSet是ResultSet的孫接口,繼承了ResultSet的屬性、方法,使用方式和ResultSet一樣 30 while(cachedRowSet.next()){ 31 int id = cachedRowSet.getInt("id"); 32 String name =cachedRowSet.getString("name"); 33 int age = cachedRowSet.getInt("age"); 34 float score = cachedRowSet.getFloat("score"); 35 System.out.println(id+"\t"+name+"\t"+age+"\t"+score); 36 }
更新結果集
ResultSet默認是可滾動的、不可更新的,不能刪除、修改結果集中的記錄。如果要獲取可更新的結果集(可增刪改結果集中的記錄),需要在創建Statement | PreparedStatement對象時傳入2個額外的參數。
CachedRowSet默認是可滾動的、可更新的,可刪除、修改CachedRowSet中的記錄。
可滾動指的是可以使用next()、first()、last()、absolute()等方法移動游標,不可滾動指的是只能使用next()來移動游標。
可更新的ResultSet:
1 //從properties文件中加載數據庫配置 2 Properties properties = new Properties(); 3 InputStream inputStream =Class.forName("test.Test").getResourceAsStream("/mysql.properties"); 4 properties.load(inputStream); 5 6 String driver = properties.getProperty("driver"); 7 String url = properties.getProperty("url"); 8 String user = properties.getProperty("user"); 9 String pwd=properties.getProperty("password"); 10 11 Class.forName(driver); 12 Connection connection = DriverManager.getConnection(url, user, pwd); 13 14 String sql = "select * from student_tb"; 15 /* 16 獲取可更新的結果集 17 18 第二個參數指定結果集是否是可滾動的: 19 ResultSet.TYPE_FORWARD_ONLY 不可滾動,只能使用next()移動游標 20 ResultSet.TYPE_SCROLL_SENSITIVE 可滾動,底層數據的變化會同步到結果集(需要底層數據庫驅動的支持) 21 ResultSet.TYPE_SCROLL_INSENSITIVE 可滾動,底層數據的變化不會同步到結果集 22 23 第三個參數指定結果集是否是可更新的: 24 ResultSet.CONCUR_READ_ONLY 默認值,只讀 25 ResultSet.CONCUR_UPDATABLE 可更新(對結果集中的記錄可增刪改) 26 */ 27 PreparedStatement preparedStatement = connection.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE); 28 ResultSet resultSet = preparedStatement.executeQuery(); 29 30 while (resultSet.next()){ 31 int id = resultSet.getInt("id"); 32 if(id==5){ 33 //updateXxx()修改當前記錄某個字段的值,第一個參數指定列(字段),可以用列名或列索引,第二個參數指定新值 34 resultSet.updateString("name","chy"); //修改當前記錄的name字段的值為chy 35 resultSet.updateRow(); //將對結果集中記錄的改同步到數據庫中,改不會自動同步到數據庫中,需要手動同步 36 } 37 else if(id==10){ 38 resultSet.deleteRow(); //刪除當前記錄(刪除當前行),刪會自動同步到數據庫中 39 } 40 } 41 42 //往結果集中插入一行 43 resultSet.moveToInsertRow(); 44 /* 45 通過updateXxx()設置插入行的字段值 46 47 resultSet.updateInt("id",100); 48 如果不設置主鍵列(id)的值,默認插到結果集的末尾, 49 如果結果集中沒有id=100的記錄,但數據庫中有,同步到數據庫時會出錯 50 */ 51 resultSet.updateString("name","CoCo"); 52 resultSet.updateInt("age",20); 53 resultSet.updateInt("score",90); 54 55 resultSet.insertRow(); //將增同步到數據庫 56 57 //這期間需要保持數據庫連接 58 59 resultSet.close(); 60 preparedStatement.close(); 61 connection.close();
update、insert需要手動同步,delete卻是自動同步,why?
因為delete是一步操作,update、insert均是多步操作,update可能要updateXxx()修改多個字段,insert可能要updateXxx()插入多個字段的值,JVM不知道你的updateXxx()寫完了沒有,所以要寫resultSet.updateRow()|insertRow(); 告訴JVM:我對這條記錄的改|插入字段已經弄完了,JVM才會同步到數據庫中。
注意是updateRow()、insertRow()、deleteRow(),是Row,不是Rows,同步的只是一條記錄。
所以每操作完一條記錄,該手動同步的就要手動同步,不能想着一次性同步所有更新。
CachedRowSet的更新操作:
1 //從properties文件中加載數據庫配置 2 Properties properties = new Properties(); 3 InputStream inputStream =Class.forName("test.Test").getResourceAsStream("/mysql.properties"); 4 properties.load(inputStream); 5 6 String driver = properties.getProperty("driver"); 7 String url = properties.getProperty("url"); 8 String user = properties.getProperty("user"); 9 String pwd=properties.getProperty("password"); 10 11 Class.forName(driver); 12 Connection connection = DriverManager.getConnection(url, user, pwd); 13 14 String sql = "select * from student_tb"; 15 PreparedStatement preparedStatement = connection.prepareStatement(sql); //CachedRowSet默認就是可更新的,不必傳額外的參數 16 ResultSet resultSet = preparedStatement.executeQuery(); 17 resultSet.next(); 18 19 //離線結果集 20 RowSetFactory rowSetFactory = RowSetProvider.newFactory(); 21 CachedRowSet cachedRowSet = rowSetFactory.createCachedRowSet(); 22 cachedRowSet.populate(resultSet); 23 resultSet.close(); 24 preparedStatement.close(); 25 connection.close(); 26 27 while (cachedRowSet.next()){ 28 int id = cachedRowSet.getInt("id"); 29 if (id==15){ 30 //刪除記錄 31 cachedRowSet.deleteRow(); 32 } 33 else if(id==20){ 34 //修改記錄 35 cachedRowSet.updateString("name","chy1"); 36 cachedRowSet.updateRow(); 37 } 38 } 39 40 Connection connection1 = DriverManager.getConnection(url, user, pwd); //連接已關閉了,需要重新獲取一個連接 41 connection1.setAutoCommit(false); //將新連接的自動提交設置為false 42 cachedRowSet.acceptChanges(connection1); //同步到數據庫。
對CachedRowSet使用deleteRow()、updateRow()不會立刻同步到數據庫中,因為連接已經關閉了,同步不了。
這2個方法相當於把這些同步操作放在一個隊列中,當 cachedRowSet.acceptChanges(connection1); 傳入一個連接時,就依次執行隊列中的同步操作。
CachedRowSet不能插入一條記錄,因為CachedRowSet是批量同步的(隊列),插入操作可能會受到隊列中其他記錄的影響。
CachedRowSet是默認可更新的,不是默認自動提交的。
更新操作要求結果集滿足2個條件:
- 結果集只能是單表查詢的結果集
- 結果集中必須含有主鍵列
分頁
分頁有3種實現方式。
1、使用sql語句來限定結果集范圍
1 String sql = "select * from student_tb where id>? and id<?"; 2 PreparedStatement preparedStatement = connection.prepareStatement(sql); 3 preparedStatement.setInt(1,0); 4 preparedStatement.setInt(2,11); 5 ResultSet resultSet = preparedStatement.executeQuery();
這種方式不好,因為區間上的某些記錄可能被刪除了,id沒了。比如1頁10條記錄,[1,10],但id=5,id=8的記錄被刪除了(比如注銷賬戶),實際取到的就比10條少。
1 String sql = "select * from student_tb limit ?,?"; 2 PreparedStatement preparedStatement = connection.prepareStatement(sql); 3 preparedStatement.setInt(1,0); //從滿足條件的記錄中的第1條記錄起(參數是下標) 4 preparedStatement.setInt(2,10); //只取10條記錄 5 /* 6 第一個參數可缺省,默認為0,從第一條記錄起。 7 String sql = "select * from student_tb limit 100"; 只取100條記錄 8 */
能取足10條,但limit是mysql的特性,對應的SQL Server特性是top、Oracle特性是rownum,不跨數據庫。
2、使用游標來實現
1 String sql = "select * from student_tb"; 2 PreparedStatement preparedStatement = connection.prepareStatement(sql); 3 ResultSet resultSet = preparedStatement.executeQuery(); 4 5 int start=0; //開始位置 6 int pageSize=10; //每頁顯示多少條記錄 7 resultSet.absolute(start); //先轉到起始處。參數是下標,0表示第一條記錄 8 while (resultSet.next()){ 9 //...... 10 System.out.println(resultSet.getRow()); 11 if(resultSet.getRow()==start+pageSize) //getRow()是獲取當前記錄是結果集中的第幾條記錄,第一條就是1。也可以設置計數器來判斷 12 break; 13 }
3、使用離線查詢來實現
1 //離線結果集 2 RowSetFactory rowSetFactory = RowSetProvider.newFactory(); 3 CachedRowSet cachedRowSet = rowSetFactory.createCachedRowSet(); 4 cachedRowSet.setPageSize(10); //設置每頁顯示多少條記錄,其實設置的是CachedRowSet的容量 5 cachedRowSet.populate(resultSet,1); //設置從結果集中的第幾條記錄開始填充。cachedRowSet中的記錄是結果集中的[1,10]條 6 resultSet.close(); 7 preparedStatement.close(); 8 connection.close(); 9 10 while (cachedRowSet.next()){ 11 //...... 12 }