前面的入門程序展示了如何從Result獲取對(duì)象,然后進(jìn)行對(duì)象的映射
這篇文章主要從下面的幾個(gè)方面講解:
ResultSet 接口從行檢索出列值游標(biāo)(Cursors)更新ResultSet 對(duì)象中行批量更新在ResultSet插入行
1.ResultSet 接口
ResultSet接口提供了檢索和操作執(zhí)行查詢結(jié)果的方法,ResultSet對(duì)象可以具有不同的功能和特征。這些特性是類型、并發(fā)性和游標(biāo)保持性。
-
ResultSet Types(結(jié)果集類型)
ResultSet對(duì)象的類型在兩個(gè)方面決定了它的功能級(jí)別:游標(biāo)操作的方式,以及對(duì)底層數(shù)據(jù)源的并發(fā)
ResultSet對(duì)象的靈敏度由三種不同的ResultSet類型之一決定:
TYPE_FORWARD_ONLY: 無(wú)法滾動(dòng)結(jié)果集;它的光標(biāo)僅向前移動(dòng),從第一行之前移動(dòng)到最后一行之后TYPE_SCROLL_INSENSITIVE:滾動(dòng)的結(jié)果集,光標(biāo)可以向前移動(dòng)或者向后移動(dòng)到相對(duì)位置和絕對(duì)位置,結(jié)果集不敏感TYPE_SCROLL_SENSITIVE:滾動(dòng)的結(jié)果集,光標(biāo)可以向前移動(dòng)或者向后移動(dòng)到相對(duì)位置和絕對(duì)位置,結(jié)果集敏感
默認(rèn)的結(jié)果集類型為: TYPE_FORWARD_ONLY,并不是所有的數(shù)據(jù)庫(kù)都支持所有的結(jié)果集類型,如果調(diào)用 DatabaseMetaData.supportsResultSetType返回true則說(shuō)明支持該結(jié)果集類型。
TYPE_SCROLL_INSENSITIVE和TYPE_SCROLL_SENSITIVE兩者的區(qū)別:
TYPE_SCROLL_INSENSITIVE:當(dāng)獲取結(jié)果集的時(shí)候是緩存結(jié)果集到內(nèi)存中,當(dāng)調(diào)用next方法的時(shí)候直接從內(nèi)存中獲取
TYPE_SCROLL_SENSITIVE:對(duì)返回的結(jié)果集是敏感的,那么是不是意味著拿到結(jié)果集后,數(shù)據(jù)庫(kù)中的數(shù)據(jù)變化都會(huì)反映到結(jié)果集中呢?不是這樣的,這里此時(shí)拿到的結(jié)果集只是記錄某種條件的的rowid
當(dāng)調(diào)用next方法的時(shí)候,在根據(jù)rowid去數(shù)據(jù)庫(kù)區(qū)查詢。
在調(diào)用next方法之前對(duì)數(shù)據(jù)庫(kù)進(jìn)行了更新操作,此時(shí)調(diào)用next方法可以獲取最新的數(shù)據(jù)
對(duì)于數(shù)據(jù)進(jìn)行插入的數(shù)據(jù),由于緩存的rowid并沒(méi)有,所以不會(huì)查詢出來(lái)。
但對(duì)于刪除操作。因?yàn)閿?shù)據(jù)庫(kù)刪除記錄只是記錄上做一個(gè)標(biāo)記,不再被檢索,但原來(lái)被緩存的ROWID還在,根據(jù)它還可以通過(guò)數(shù)據(jù)庫(kù)自己的底層操作正確地把數(shù)據(jù)提取出來(lái)
-
ResultSet Concurrency(結(jié)果集并發(fā)性)
ResultSet對(duì)象的并發(fā)性決定了支持什么級(jí)別的更新功能。
有兩種更新的級(jí)別
CONCUR_READ_ONLY: ResultSet結(jié)果集不能被更新CONCUR_UPDATABLE: ResultSet結(jié)果集可以被更新
默認(rèn)的結(jié)果集并發(fā)性為CONCUR_READ_ONLY
并不是所有的數(shù)據(jù)庫(kù)都支持所有的并發(fā)類型,可以用 DatabaseMetaData.supportsResultSetConcurrency()來(lái)檢測(cè)是否支持哪種結(jié)果集并發(fā)類型,返回true,則說(shuō)明支持
-
Cursor Holdability(游標(biāo)保持性)
調(diào)用方法Connection.commit可以關(guān)閉在當(dāng)前事務(wù)期間創(chuàng)建的ResultSet對(duì)象。然而,在某些情況下,這可能不是我們想要的行為。ResultSet屬性的可保持性使應(yīng)用程序能夠控制在調(diào)用commit時(shí)ResultSet對(duì)象(游標(biāo))是否關(guān)閉。
下面有兩種游標(biāo)保持性
HOLD_CURSORS_OVER_COMMIT:當(dāng)調(diào)用commit方法的時(shí)候,游標(biāo)沒(méi)有關(guān)閉,當(dāng)繼續(xù)保持。CLOSE_CURSORS_AT_COMMIT:調(diào)用commit方法的時(shí)候,游標(biāo)關(guān)閉
并不是所有的數(shù)據(jù)庫(kù)都支持游標(biāo)的關(guān)閉和持有型。
下面給出一個(gè)具體的實(shí)例:
/**
* @Project: jdk
* @description: mysql的ResultSet測(cè)試
* @author: sunkang
* @create: 2018-10-14 14:33
* @ModificationHistory who when What
**/
public class ResultSetTest {
public static void main(String[] args) throws SQLException {
ResultSetTest resultSetTest = new ResultSetTest();
Connection con = resultSetTest.getDefalutConnection();
//驗(yàn)證mysql 支持哪些ResultSet類型
DatabaseMetaData dbMetaData = con.getMetaData();
System.out.println("Supports TYPE_FORWARD_ONLY? "+dbMetaData.supportsResultSetType(ResultSet.TYPE_FORWARD_ONLY));
System.out.println("Supports TYPE_SCROLL_INSENSITIVE? "+dbMetaData.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE));
System.out.println("Supports TYPE_SCROLL_SENSITIVE? "+dbMetaData.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE));
/* 上面的輸出結(jié)果為:
Supports TYPE_FORWARD_ONLY? false
Supports TYPE_SCROLL_INSENSITIVE? true
Supports TYPE_SCROLL_SENSITIVE? false
說(shuō)明mysql支持類型為 TYPE_SCROLL_INSENSITIVE
*/
//驗(yàn)證mysql 支持哪些ResultSet并發(fā)性
System.out.println("Supports CONCUR_READ_ONLY? "+dbMetaData.supportsResultSetConcurrency(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY));
System.out.println("Supports CONCUR_UPDATABLE? "+dbMetaData.supportsResultSetConcurrency(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE));
/* 上面的輸出結(jié)果為:
Supports CONCUR_READ_ONLY? true
Supports CONCUR_UPDATABLE? true
說(shuō)明mysql 可以在ResultSet對(duì)象更改結(jié)果集
*/
//驗(yàn)證mysql 支持哪些游標(biāo)保持性
System.out.println("Supports HOLD_CURSORS_OVER_COMMIT? "+dbMetaData.supportsResultSetHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT));
System.out.println("Supports CLOSE_CURSORS_AT_COMMIT? "+dbMetaData.supportsResultSetHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT));
/**
* Supports HOLD_CURSORS_OVER_COMMIT? true
* Supports CLOSE_CURSORS_AT_COMMIT? false
* 說(shuō)明mysql在commit提交之后,游標(biāo)還是打開(kāi)的
*/
//創(chuàng)建了一個(gè)類型為HOLD_CURSORS_OVER_COMMIT,并發(fā)性為:CONCUR_UPDATABLE,以及游標(biāo)的保持性為:HOLD_CURSORS_OVER_COMMIT的語(yǔ)句集
Statement statement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE,ResultSet.HOLD_CURSORS_OVER_COMMIT);
ResultSet resultSet = statement.executeQuery("SELECT * FROM user");
//設(shè)置連接的自動(dòng)提交為false
con.setAutoCommit(false);
//更新user 的address統(tǒng)一后面加上:update語(yǔ)句
while (resultSet.next()) {
//address為大寫和小寫都是可以的
String address = resultSet.getString("address");
resultSet.updateString("address", address+":update");
//更新此行
resultSet.updateRow();
//在這里提交,游標(biāo)還是打開(kāi)的,所以不會(huì)有問(wèn)題
con.commit();
}
//釋放資源
resultSet.close();
statement.close();
con.close();
}
public Connection getDefalutConnection(){
Connection connection = null;
try {
//1.加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)
Class.forName("com.mysql.jdbc.Driver");
//2.通過(guò)驅(qū)動(dòng)管理類獲取數(shù)據(jù)庫(kù)鏈接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "123");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
2.從行檢索出列值
入門程序已經(jīng)給出了,這里不再描述
可以根據(jù)索引來(lái)獲取對(duì)應(yīng)的列值,也可以根據(jù)列名獲取列值,這里的列名可以使列的別名, 根據(jù)列名獲取列的值的時(shí)候,這里輸入名稱可以大寫也可以是小寫
while(resultSet.next()){
User user = new User();
user.setId(resultSet.getInt("id"));
user.setUsername(resultSet.getString("username"));
user.setSex(resultSet.getString("sex"));
user.setBirthday(resultSet.getTimestamp("birthday"));
user.setAddress(resultSet.getString("address"));
userList.add(user);
}
3.cursors(游標(biāo))
你可以通過(guò)一個(gè)游標(biāo)來(lái)訪問(wèn)ResultSet的內(nèi)部數(shù)據(jù),游標(biāo)表示執(zhí)行ResultSet的某一行,當(dāng)ResultSet對(duì)象創(chuàng)建的時(shí)候,游標(biāo)指向的是第一行的前一行,調(diào)用next方法,可以使游標(biāo)向下移動(dòng),下面是移動(dòng)游標(biāo)的方法
next:移動(dòng)游標(biāo)執(zhí)行下一行,當(dāng)游標(biāo)的執(zhí)行的行存在數(shù)據(jù)的時(shí)候返回true,當(dāng)游標(biāo)執(zhí)行最后一行的后面的時(shí)候返回false
previous: 將游標(biāo)向上移動(dòng),該方法返回boolean型數(shù)據(jù),當(dāng)移到結(jié)果集第一行之前時(shí),返回false。
beforeFirst: 將游標(biāo)移動(dòng)到結(jié)果集的初始位置,即在第一行之前。
afterLast: 將游標(biāo)移到結(jié)果集最后一行之后。
first: 將游標(biāo)移到結(jié)果集的第一行。
last: 將游標(biāo)移到結(jié)果集的最后一行。
relative(int row):將游標(biāo)移動(dòng)到相對(duì)于當(dāng)前位置的指定行
absolute(int row): 將游標(biāo)移動(dòng)到指定的行數(shù)
getRow(): 得到當(dāng)前游標(biāo)所指向行的行號(hào),行號(hào)從1開(kāi)始,依次類推
一般數(shù)據(jù)庫(kù)默認(rèn)的ResultSet的敏感類型為TYPE_FORWARD_ONLY,這集意味著游標(biāo)不能滾動(dòng),除了調(diào)用next方法之外,其他的方法不可以進(jìn)行調(diào)用,所以游標(biāo)只能向下移動(dòng)
4.更新ResultSet 對(duì)象中行
上面的mysql的ResultSet測(cè)試已經(jīng)展示如何更新ResultSet中的行,更新ResultSet對(duì)象的行,需要數(shù)據(jù)庫(kù)支持ResultSet并發(fā)性為CONCUR_UPDATABLE,不然是無(wú)法更新的
while (resultSet.next()) {
//address為大寫和小寫都是可以的
String address = resultSet.getString("address");
resultSet.updateString("address", address+":update");
//更新此行
resultSet.updateRow();
}
5.批量更新
下面展示的是一個(gè)數(shù)據(jù)庫(kù)批量更新的例子
/**
* @Project: jdk
* @description: 批量更新
* @author: sunkang
* @create: 2018-10-14 15:46
* @ModificationHistory who when What
**/
public class BatchUpdateTest{
public static void main(String[] args) {
BatchUpdateTest batchUpdateTest = new BatchUpdateTest();
Connection con =batchUpdateTest.getDefalutConnection();
Statement statement = null;
try {
//默認(rèn)的事務(wù)提交類型是自動(dòng)提交,但是在這里需要設(shè)置自動(dòng)提交類型為false,在出現(xiàn)錯(cuò)誤的時(shí)候需要回滾
con.setAutoCommit(false);
statement = con.createStatement();
for(int i=0;i<10;i++){
String username = "sunkang"+ i;
statement.executeUpdate("insert user(username,birthday,sex,address) values ('"+username+"',NOW(),1,'hanghzou')");
}
//表示批量語(yǔ)句集返回的受影響的行數(shù)
int[] updateCounts = statement.executeBatch();
con.commit();
System.out.println(Arrays.toString(updateCounts));
} catch (SQLException e) {
//出現(xiàn)異常的時(shí)候回滾事務(wù)
try {
con.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
//關(guān)閉語(yǔ)句集
if(statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
try {
//設(shè)置回來(lái)
con.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public Connection getDefalutConnection(){
Connection connection = null;
try {
//1.加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)
Class.forName("com.mysql.jdbc.Driver");
//2.通過(guò)驅(qū)動(dòng)管理類獲取數(shù)據(jù)庫(kù)鏈接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "123");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
上面程序展示的普通的statement 執(zhí)行批量更新,如果是預(yù)編譯的statement呢?下圖展示了預(yù)編譯的statement的批量更新
String sql = "insert user(username,birthday,sex,address) values (?,now(),?,?)";
PreparedStatement preparedStatement = con.prepareStatement(sql);
for(int i=10;i<12;i++){
String username = "sunkang"+ i;
preparedStatement.setString(1,username);
preparedStatement.setString(2,"1");
preparedStatement.setString(3,"hanghzou");
preparedStatement.addBatch();
}
preparedStatement.executeBatch();
con.commit();
6.在ResultSet插入行
并不是所有數(shù)據(jù)都支持在ResultSet執(zhí)行插入的,前面的例子已經(jīng)測(cè)試mysql支持結(jié)果集并發(fā)性為CONCUR_UPDATABLE
/**
* @Project: jdk
* @description: 插入的測(cè)試
* @author: sunkang
* @create: 2018-10-14 16:18
* @ModificationHistory who when What
**/
public class InsertRowByResultSet {
public static void main(String[] args) throws SQLException {
ResultSetTest resultSetTest = new ResultSetTest();
Connection con = resultSetTest.getDefalutConnection();
Statement statement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql = "select * from user";
ResultSet resultSet = statement.executeQuery(sql);
//移動(dòng)游標(biāo)到插入的行
resultSet.moveToInsertRow();
//插入一行數(shù)據(jù)
resultSet.updateString("username","wangzhi");
resultSet.updateString("sex","1");
resultSet.updateTimestamp("birthday",new Timestamp(System.currentTimeMillis()));
resultSet.updateString("address","han");
//插入該條記錄
resultSet.insertRow();
//游標(biāo)移動(dòng)到最開(kāi)始的地方
resultSet.beforeFirst();
//釋放資源
resultSet.close();
statement.close();
}
public Connection getDefalutConnection(){
Connection connection = null;
try {
//1.加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)
Class.forName("com.mysql.jdbc.Driver");
//2.通過(guò)驅(qū)動(dòng)管理類獲取數(shù)據(jù)庫(kù)鏈接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "123");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}