數據庫爲oracle 11g
經測試,發如今定義PreparedStatement 時,若是採用如下方式,會拋此異常.
PreparedStatement stm = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
PreparedStatement stm = conn.prepareStatement(sql,new String[] {"ID"});
經Google後,發現
Oracle的sequence實現很是靈活,因此也帶來一些易用性問題,如何取到新插入記錄生成的sequence值與其它數據庫有較大差異,本文詳國介紹了5種實現讀取新插入記錄sequence值的方法。html
測試用的數據庫腳本:
SQL> create table T
(
ID NUMBER
);
Table created
SQL> create sequence SEQ_T1;
Sequence created
//公共代碼:獲得數據庫鏈接
public Connection getConnection() throws Exception{
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1 :1521:dbname", "username", "password");
return conn;
}
//方法一
//先用select seq_t1.nextval as id from dual 取到新的sequence值。
//而後將最新的值經過變量傳遞給插入的語句:insert into t1(id) values(?)
//最後返回開始取到的sequence值。
//這種方法的優勢代碼簡單直觀,使用的人也最多,缺點是須要兩次sql交互,性能不佳。
public int insertDataReturnKeyByGetNextVal() throws Exception {
Connection conn = getConnection();
String vsql = "select seq_t1.nextval as id from dual";
PreparedStatement pstmt =(PreparedStatement)conn.prepareStatement(vsql);
ResultSet rs=pstmt.executeQuery();
rs.next();
int id=rs.getInt(1);
rs.close();
pstmt.close();
vsql="insert into t1(id) values(?)";
pstmt =(PreparedStatement)conn.prepareStatement(vsql);
pstmt.setInt(1, id);
pstmt.executeUpdate();
System.out.print("id:"+id);
return id;
}
//方法二
//先用insert into t1(id) values(seq_t1.nextval)插入數據。
//而後使用select seq_t1.currval as id from dual返回剛纔插入的記錄生成的sequence值。
//注:seq_t1.currval表示取出當前會話的最後生成的sequence值,因爲是用會話隔離,只要保證兩個SQL使用同一個Connection便可,對於採用鏈接池應用須要將兩個SQL放在同一個事務內纔可保證併發安全。
//另外若是會話沒有生成過sequence值,使用seq_t1.currval語法會報錯。
//這種方法的優勢能夠在插入記錄後返回sequence,適合於數據插入業務邏輯很差改造的業務代碼,缺點是須要兩次sql交互,性能不佳,而且容易產生併發安全問題。
public int insertDataReturnKeyByGetCurrVal() throws Exception {
Connection conn = getConnection();
String vsql = "insert into t1(id) values(seq_t1.nextval)";
PreparedStatement pstmt =(PreparedStatement)conn.prepareStatement(vsql);
pstmt.executeUpdate();
pstmt.close();
vsql="select seq_t1.currval as id from dual";
pstmt =(PreparedStatement)conn.prepareStatement(vsql);
ResultSet rs=pstmt.executeQuery();
rs.next();
int id=rs.getInt(1);
rs.close();
pstmt.close();
System.out.print("id:"+id);
return id;
}
//方法三
//採用pl/sql的returning into語法,能夠用CallableStatement對象設置registerOutParameter取得輸出變量的值。
//這種方法的優勢是隻要一次sql交互,性能較好,缺點是須要採用pl/sql語法,代碼不直觀,使用較少。
public int insertDataReturnKeyByPlsql() throws Exception {
Connection conn = getConnection();
String vsql = "begin insert into t1(id) values(seq_t1.nextval) returning id into :1;end;";
CallableStatement cstmt =(CallableStatement)conn.prepareCall ( vsql);
cstmt.registerOutParameter(1, Types.BIGINT);
cstmt.execute();
int id=cstmt.getInt(1);
System.out.print("id:"+id);
cstmt.close();
return id;
}
//方法四
//採用PreparedStatement的getGeneratedKeys方法
//conn.prepareStatement的第二個參數能夠設置GeneratedKeys的字段名列表,變量類型是一個字符串數組
//注:對Oracle數據庫這裏不能像其它數據庫那樣用prepareStatement(vsql,Statement.RETURN_GENERATED_KEYS)方法,這種語法是用來取自增類型的數據。
//Oracle沒有自增類型,所有采用的是sequence實現,若是傳Statement.RETURN_GENERATED_KEYS則返回的是新插入記錄的ROWID,並非咱們相要的sequence值。
//這種方法的優勢是性能良好,只要一次sql交互,實際上內部也是將sql轉換成oracle的returning into的語法,缺點是隻有Oracle10g才支持,使用較少。
public int insertDataReturnKeyByGeneratedKeys() throws Exception {
Connection conn = getConnection();
String vsql = "insert into t1(id) values(seq_t1.nextval)";
PreparedStatement pstmt =(PreparedStatement)conn.prepareStatement(vsql,new String[]{"ID"});
pstmt.executeUpdate();
ResultSet rs=pstmt.getGeneratedKeys();
rs.next();
int id=rs.getInt(1);
rs.close();
pstmt.close();
System.out.print("id:"+id);
return id;
}
//方法五
//和方法三相似,採用oracle特有的returning into語法,設置輸出參數,可是不一樣的地方是採用OraclePreparedStatement對象,由於jdbc規範裏標準的PreparedStatement對象是不能設置輸出類型參數。
//最後用getReturnResultSet取到新插入的sequence值,
//這種方法的優勢是性能最好,由於只要一次sql交互,oracle9i也支持,缺點是隻能使用Oracle jdbc特有的OraclePreparedStatement對象。
public int insertDataReturnKeyByReturnInto() throws Exception {
Connection conn = getConnection();
String vsql = "insert into t1(id) values(seq_t1.nextval) returning id into :1";
OraclePreparedStatement pstmt =(OraclePreparedStatement)conn.prepareStatement(vsql);
pstmt.registerReturnParameter(1, Types.BIGINT);
pstmt.executeUpdate();
ResultSet rs=pstmt.getReturnResultSet();
rs.next();
int id=rs.getInt(1);
rs.close();
pstmt.close();
System.out.print("id:"+id);
return id;
}
以上5種方法均可以實現功能,如下是5種方法的優缺點彙總,我的推薦性能要求通常的業務採用第一種方法,性能要求很是高業務採用第五種方法。sql
以上5種方法均可以實現功能,如下是5種方法的優缺點彙總,我的推薦性能要求通常的業務採用第一種方法,性能要求很是高業務採用第五種方法。數據庫
方法數組 |
簡介安全 |
優勢併發 |
缺點oracle |
方法一性能 |
先用seq.nextval取出值,而後用轉入變量的方式插入測試 |
代碼簡單直觀,使用的人也最多spa |
須要兩次sql交互,性能不佳 |
方法二 |
先用seq.nextval直接插入記錄,再用seq.currval取出新插入的值 |
能夠在插入記錄後返回sequence,適合於數據插入業務邏輯很差改造的業務代碼 |
須要兩次sql交互,性能不佳,而且容易產生併發安全問題 |
方法三 |
用pl/sql塊的returning into語法,用CallableStatement對象設置輸出參數取到新插入的值 |
只要一次sql交互,性能較好 |
須要採用pl/sql語法,代碼不直觀,使用較少 |
方法四 |
設置PreparedStatement須要返回新值的字段名,而後用getGeneratedKeys取得新插入的值 |
性能良好,只要一次sql交互 |
只有Oracle10g才支持,使用較少 |
方法五 |
returning into語法,用OraclePreparedStatement對象設置輸出參數,再用getReturnResultSet取得新增入的值 |
性能最好,由於只要一次sql交互,oracle9i也支持 |
只能使用Oracle jdbc特有的OraclePreparedStatement對象 |
來源:http://www.360doc.com/content/11/1102/17/1290342_161130566.shtml