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