- - PR -
アプリでの最大オープンカーソルのエラーの回避方法
| 投稿者 | 投稿内容 | ||||||||
|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2005-04-08 12:04
お世話になっております。
現在、ストアドでデータを取得し、 結果を表示するプログラムを作成しているのですが、 V$OPEN_CURSORで調べてみると、 カーソルがOPENしたままになっているようです。 実際、 ORA-10000:最大オープンカーソルのエラー が出てしまうことがあるので。。。 カーソルFORループ(自動OPEN、CLOSEする) までOPEN_CURSORで 確認できるので、自分的に混乱しています。。 以下、開発環境と記述コーディングの典型的な形です。 個人的には、connection関係も全て閉じているので カーソルも閉じると考えているのですが、 考え方が間違っていますか? アプリ側でこの現象を回避できるように できることがありましたら、ご教授下さい。 よろしくお願いします。 <開発環境> OS:RedHat3 Tomcat5.5.7 Struts1.1 JDK1.5 Oracle10g(ストアド使用) <コディング> package svt.appointment.db; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import svt.appointment.AppoDateData; import svt.appointment.AppoDetails; import svt.appointment.DataMap; import svt.appointment.ShowDate; import svt.indexetc.UserInfo; import oracle.jdbc.OracleTypes; public class AppoListDAO { //Variables // --------------------------- private DataSource ds=null; // --------------------------- /************************************************* * AppoListDAO:default constractor * * @throws SQLException */ public AppoListDAO() throws Exception { try { Context initContext = new InitialContext(); // get Context Context envContext = (Context)initContext.lookup("java:/comp/env"); //Context environment ds = (DataSource)envContext.lookup("jdbc/Oracle"); // Datasource name }catch(NamingException ex){ ex.printStackTrace();// output error to error stream throw ex; // thow error } } /************************************************* * getAppoList * @param brno1 * * @return List * @throws SQLException */ public List getAppoList(ShowDate wk_Day, UserInfo userInfo) throws Exception { // initialized parameters Connection con = null; CallableStatement cStmt =null; ResultSet rs = null; List list = new ArrayList(); AppoDateData appo = null; // getDateInformation List wk_Week = wk_Day.getOnWeek(); //get UserInformation String userId= userInfo.getUserId(); String ss= userInfo.getAa(); String brno2= userInfo.getBb(); try { //get Connection con = ds.getConnection(); cStmt = con.prepareCall("{call stored.test(?,?,?,?,?)}"); for(int i=0; i < wk_Week.size(); i++) { // set AppoData of each Days String onDate=(String)wk_Week.get(i); DataMap query = wk_Day.getQuery(i); appo = new AppoDateData(); appo.setOnDate(onDate); String withDate = onDate + wk_Day.getDayWeek()[i]; appo.setWithDay(withDate); appo.setQuery(query); List details = new ArrayList();// Details Data //動的SQL を作成し、refCUROSRを返却 cStmt.registerOutParameter(1, OracleTypes.CURSOR); cStmt.setString(2,userId); // set UserId cStmt.setString(3,ss); // set ss cStmt.setString(4,onDate); // set fromdate cStmt.setString(5,onDate); // set todate ////cStmt.setString(4,wk_Day.getFromDate()); // set fromdate ////cStmt.setString(5,wk_Day.getToDate()); // set todate cStmt.execute(); //execute procedure // get ResultSet rs = (ResultSet)cStmt.getObject(1); while (rs.next()) { //add list of the detail on the day details.add(getAppoDate(rs)); } //add list of AppoDateData appo.setDetails(details); list.add(appo); } } catch (SQLException ex) { ex.printStackTrace();// output error to error stream throw ex; // thow error } finally { if (rs != null)rs.close(); if(cStmt != null)cStmt.close(); if(con != null)con.close(); } // return Appointment List return list; } /************************************************* * getAppoDate * @param rs * * @return AppoDetails * @throws Exception */ private AppoDetails getAppoDate(ResultSet rs) throws Exception { // AppoDetails details = new AppoDetails(); ////details.setAction_dt(rs.getString("aaa")); details.setId(rs.getLong("bbb")); return details; } } | ||||||||
|
投稿日時: 2005-04-08 12:25
forループで回っている間、ResultSetがどんどん増えていくのが原因ではないかなー、と思います。
ここの部分の直後でrsをcloseしてみてください。 | ||||||||
|
投稿日時: 2005-04-08 12:53
オラクルのJDBCのJDBCドライバーはstmtをクローズしないと
Openしたままになります。 冗長なようでも、forループの中で、stmtの作成とCloseを行って はいかがでしょうか。 また、プールしたConnectionはCloseしても、プール側に開放 されるだけです。 | ||||||||
|
投稿日時: 2005-04-08 16:08
皆々 様
早速のご回答ありがとうございました。 お二人の意見を参考に、 For文内でclose処理を connection、Statement、Resultsetそれぞれ 行ってみましたが、 駄目でした。 もともと、コーディングの仕方にも 疑問があったので、思い切って ストアドと、コーディングを書き換え、 Oracleとの接続は一度のみにし、 その後の処理はコーディングで行うように修正しました。 が、駄目でした。。。 V$OPEN_CURSORを確認すると 確実に増えていっています。。。 違う画面を検索すると カーソルが減少しますが、 それがどういうことを示すのかが 思いつきません。。。 以下に、変更したコーディングと、 ストアドの文章を抜粋しておきます。 何か良いご指摘を思いつく方が いらっしゃいましたら、よろしくお願いします。 <コーディング> /************************************************* * getAppoList * @param brno1 * * @return List * @throws SQLException */ public List getAppoList(ShowDate wk_Day, UserInfo userInfo) throws Exception { // initialized parameters Connection con = null; CallableStatement cStmt =null; ResultSet rs = null; List list = new ArrayList(); AppoDateData appo = null; // getDateInformation List wk_Week = wk_Day.getOnWeek(); //get UserInformation String userId= userInfo.getUserId(); String aa= userInfo.getAa(); try { //get Connection con = ds.getConnection(); cStmt = con.prepareCall("{call stored.test(?,?,?,?,?)}"); cStmt.registerOutParameter(1, OracleTypes.CURSOR); cStmt.setString(2,userId); // set UserId cStmt.setString(3,aa); // set aa cStmt.setString(4,wk_Day.getFromDate()); // set fromdate cStmt.setString(5,wk_Day.getToDate()); // set todate cStmt.execute(); //execute procedure // get ResultSet rs = (ResultSet)cStmt.getObject(1); boolean isFirst =false; for(int i=0; i < wk_Week.size(); i++) { // set AppoData of each Days String onDate=(String)wk_Week.get(i); DataMap query = wk_Day.getQuery(i); appo = new AppoDateData(); appo.setOnDate(onDate); String withDate = onDate + wk_Day.getDayWeek()[i]; appo.setWithDay(withDate); appo.setQuery(query); List details = new ArrayList();// Details Data while(rs.isAfterLast() != true) { // add list of the detail on the day if(isFirst){ if(onDate.equals(rs.getString("sOnDate"))){ details.add(getAppoDate(rs)); }else{ break; } } isFirst = true; rs.next(); } //add list of AppoDateData appo.setDetails(details); list.add(appo); } } catch (SQLException ex) { ex.printStackTrace();// output error to error stream throw ex; // thow error } finally { if (rs != null)rs.close(); if(cStmt != null)cStmt.close(); if(con != null)con.close(); } // return Appointment List return list; } <ストアド> /*procedures*/ /*----------------------------------------------------------------------*/ /*test */ PROCEDURE test(rcset OUT refcur, vUserId IN tt.user_name%type, vAa IN tt.aa%type, vFromDate IN VARCHAR2, vToDate IN VARCHAR2) IS /*parameters*/ TYPE refcur IS REF CURSOR; vDateFormat VARCHAR2(20); BEGIN /*initialize*/ vDateFormat := '''YYYY/MM/DD'''; /*make sql statement*/ s_Sql := 'SELECT id FROM tt'; s_Sql := s_Sql || ' WHERE user_name = :vUserId'; s_Sql := s_Sql || ' AND aa = :vAa'; s_Sql := s_Sql || ' AND action_dt >= to_date(:vFromDate,'; s_Sql := s_Sql || vDateFormat; s_Sql := s_Sql || ')'; s_Sql := s_Sql || ' AND action_dt < (1 + to_date(:vToDate,'; s_Sql := s_Sql || vDateFormat; s_Sql := s_Sql || '))'; s_Sql := s_Sql || ' ORDER BY action_dt'; /*set cursor*/ OPEN rcset FOR s_Sql using vUserId, vAa; END test; | ||||||||
|
投稿日時: 2005-04-08 17:19
rs = (ResultSet)cStmt.getObject(1);
で取得したカーソルを開放していないんだからリークしてて当然でしょう。 forループの最後でrs.close()を入れれば解決しそうな気がします。 | ||||||||
|
投稿日時: 2005-04-08 18:13
Anthyhime 様
回答ありがとうございます。 rs.close()はfinallyで行っていますが、 間違っていますか? エラー時でも、きちんとcloseするように finallyに全て記述しています。 ちなみに、「forループの最後」 と言うのは、 1)ループ内の最後ですか? 2)ループを出た直後と言うことですか? 両方試してみたのですが、 両方ともカーソルは増えています。 (1)では正常な結果が出力できない) また、 自身の書いたサンプルの<ストアド>箇所で 実際にはパッケージの宣言部に 「TYPE refcur IS REF CURSOR;」 を宣言しているのですが、 何か関係していますかね? これを修正すると、 影響が結構大きいので まだ試せてないのですが、 もしも何か知っていましたら、 よろしくお願いします。 | ||||||||
|
投稿日時: 2005-04-08 18:29
ストアドプロシージャが関連するかどうかは、ストアドプロシージャを使わない場合と比べてみることで切り分けられるかと思います。お試し下さい。
| ||||||||
|
投稿日時: 2005-04-10 18:45
> rs.close()はfinallyで行っていますが、
> 間違っていますか? おそらく間違っています。ループのたびにSPでカーソルが取得されるのであればそのたびに開放しなくてはなりません。確かにJDBCではStatementをクローズすることによりそれから生成されたカーソルを開放する仕様になっていますが、SP経由で生成されたカーソルについては未定義です。 提示されたコードではfinallyで開放されるカーソルは最後に生成されたカーソルのみとなるでしょう。その他のカーソルはすべてオープンされたままのはずです。 | ||||||||
