最近幾天在研究hibernate、JPA對存儲過程的調用,主要是針對有返回結果集的存儲過程的調用方法,個人感覺存儲過程是個好東西,雖然說heibernate對數據訪問封裝的比較不錯,再加上他的緩存機制,確實很多情況下足夠了,不過存儲過程還是有他的用武之地,如果能用他提高性能,為何不用....
好吧,不討論他有沒有用處,掌握了以后備用也是不錯的選擇,重點看看oracle的存儲過程調用,mysql的要簡單的多。
(一)、oracle存儲過程調用
先在pl/sql中建立一個存儲過程,代碼如下:
--創建帶有游標的包
create or replace package mypackage astype mycursoris ref cursor;endmypackage;--創建存儲過程
create or replace procedure searchUsers(user_cursor out mypackage.mycursor) is
begin
open user_cursor for select * from wyuser order by id asc;end;
建立完成以后,如果想測試,可以先看打印出來的結果測試一下,下面是我的測試代碼:
--執行存儲過程
declareoutcursor mypackage.mycursor;
myidnumber;
mydate Date;
mypasswordvarchar2(20);
mynamevarchar2(20);begindbms_output.put_line('輸出');
searchUsers(outcursor);
loopfetch outcursor intomyid,mydate, mypassword,myname;exit when outcursor%notfound;
dbms_output.put_line(myname||' '||mypassword);endloop;end;
執行后是沒有問題的,然后我們重點在如何調用。
①hibernate中調用
在hibernate中調用oracle的存儲過程,可能會繞過heibernate,直接用jdbc操作,所以,我一直在找可有什么方法可以得到connection數據庫連接,在hibernate3.0之前提供session.connection()方法可以直接獲取到連接,不過我用的是hibernate4.0這個方法已經被廢棄了,而是用dowork方式代替,下面就看看這種方式下的調用:
/*** 調用存儲過程訪問數據*/
public ListsearchProc() {final List rtnObjs = new ArrayList();try{
Session session= this.sessionFactory.openSession();
session.beginTransaction();
Work work= newWork() {public void execute(Connection cn) throwsSQLException {
CallableStatement csmt=cn
.prepareCall("{call searchUsers(?)}");
csmt.registerOutParameter("user_cursor", OracleTypes.CURSOR);
csmt.execute();
ResultSet rs= (ResultSet) csmt.getObject("user_cursor");while(rs.next()) {
//結果用數組保存,這里只是為了測試,使用定長數組****不通用 Object[] objects= new Object[4];
objects[0] = rs.getLong("ID");
objects[1] = rs.getTimestamp("createtime");
objects[2] = rs.getString("password");
objects[3] = rs.getString("username");
rtnObjs.add(objects);
}
}
};
session.doWork(work);
session.getTransaction().commit();
}catch(Exception e) {
System.out.println("DAO層訪問存儲過程失敗");
}returnrtnObjs;
}
返回的結果是一個Object數組集合,這樣,就可以在業務層對這個數組進行處理,可以將其實例化為需要的對象等等.....
②Spring JDBC訪問oracle存儲過程
本來想用JPA訪問存儲過程的,不過貌似對於有返回值的存儲過程JPA處理起來不夠強大,故,如法炮制,依然繞到繞過去,用SpringJDBC來處理。存儲過程依然是上面使用的那個,下面看看JAVA的調用代碼:
/*** Spring JDBC方式調用ORACLE存儲過程,返回結果集*/@Overridepublic Listsearch() {
List rtnObjs = new ArrayList();
rtnObjs= this.jdbcTemplate.execute(newCallableStatementCreator() {
@OverridepublicCallableStatement createCallableStatement(Connection con)throwsSQLException {
CallableStatement cs= con.prepareCall("{call searchUsers(?)}");
cs.registerOutParameter("user_cursor", OracleTypes.CURSOR);returncs;
}
},new CallableStatementCallback>() {
@Overridepublic ListdoInCallableStatement(CallableStatement cs)throwsSQLException, DataAccessException {
cs.execute();
ResultSet rs= (ResultSet) cs.getObject("user_cursor");
List objs = new ArrayList();while(rs.next()) {
Object[] objects= new Object[4];
objects[0] = rs.getLong("ID");
objects[1] = rs.getTimestamp("createtime");
objects[2] = rs.getString("password");
objects[3] = rs.getString("username");
objs.add(objects);
}returnobjs;
}
});returnrtnObjs;
}
翻看了jdbcTemplate中的方法,很多都是回調方法,而且很多大部分都可以可以返回List和List>這種類型,說實話這種結構在實際開發中很有用途,所以在上面的代碼中也按照這種方法,返回了List交給業務層可以靈活處理。上面的方法首先繼承CallableStatementCreator中創建CallableStatement的方法注冊存儲過程和定義返回類型,在CallableStatementCallback回調接口中的重寫do方法,讓之前注冊的存儲過程執行並得到起返回值,當然也可以自己寫兩個類分別繼承CallableStatementCreator接口Callable- -StatementCallback這兩個接口,實現其中的方法,然后在jdbcTemplate的execute中調用.....
(二)、mysql存儲過程調用
相對於oracle的存儲過程,mysql的存儲過程就簡單多了,這里也同樣編寫一個返回結果集的存儲過程,然后利用jdbcTemplate對其進行調用
CREATE DEFINER=`root`@`localhost` PROCEDURE`getuser`()BEGIN
select * fromwyuser;END
真是巨簡單,看看存儲過程的調用是不是也一樣簡單
/*** 調用mysql存儲過程
*
*@return
*/@Overridepublic ListgetUser() {
List rtnObjs = new ArrayList();
rtnObjs= this.getJdbcTemplate().execute("{call getuser()}",new CallableStatementCallback>() {
@Overridepublic ListdoInCallableStatement(
CallableStatement cs)throwsSQLException,
DataAccessException {
List objects = new ArrayList();
ResultSet rs=cs.executeQuery();while(rs.next()) {
Object[] objArr= new Object[4];
objArr[0] = rs.getLong("ID");
objArr[1] = rs.getTimestamp("createtime");
objArr[2] = rs.getString("password");
objArr[3] = rs.getString("username");
objects.add(objArr);
}returnobjects;
}
});returnrtnObjs;
}
好吧,呵呵,調用當然還是和調用oracle的procedure方式一樣,如果是針對有參數的調用也基本上沒有一致。
CREATE DEFINER=`root`@`localhost` PROCEDURE `finduser`(in uid int)BEGIN
select * from wyuser where id=uid;END
調用代碼:
/*** 調用mysql存儲過程,根據ID查找用戶*/@Overridepublic Object[] findUser(intid) {final int uid =id;
Object[] objArr= new Object[4];
objArr= this.getJdbcTemplate().execute("{call finduser(?)}",new CallableStatementCallback() {
@OverridepublicObject[] doInCallableStatement(CallableStatement cs)throwsSQLException, DataAccessException {
Object[] arr= new Object[4];
cs.setInt("uid", uid); //為存儲過程中參數賦值 ResultSet rs=(ResultSet) cs.executeQuery();while(rs.next()) {
arr[0] = rs.getLong("ID");
arr[1] = rs.getTimestamp("createtime");
arr[2] = rs.getString("password");
arr[3] = rs.getString("username");
}returnarr;
}
});returnobjArr;
}