- PR -

アプリでの最大オープンカーソルのエラーの回避方法

投稿者投稿内容
faulter
会議室デビュー日: 2005/03/06
投稿数: 11
投稿日時: 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;
}


}
永井和彦
ぬし
会議室デビュー日: 2002/07/03
投稿数: 276
お住まい・勤務地: 東京都
投稿日時: 2005-04-08 12:25
forループで回っている間、ResultSetがどんどん増えていくのが原因ではないかなー、と思います。

引用:

コード:
// get ResultSet
 rs = (ResultSet)cStmt.getObject(1);      
 while (rs.next()) {
   //add list of the detail on the day
   details.add(getAppoDate(rs));
 }          	    





ここの部分の直後でrsをcloseしてみてください。
未記入
会議室デビュー日: 2004/06/25
投稿数: 6
投稿日時: 2005-04-08 12:53
 オラクルのJDBCのJDBCドライバーはstmtをクローズしないと
Openしたままになります。
 冗長なようでも、forループの中で、stmtの作成とCloseを行って
はいかがでしょうか。
 また、プールしたConnectionはCloseしても、プール側に開放
されるだけです。
faulter
会議室デビュー日: 2005/03/06
投稿数: 11
投稿日時: 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;
Anthyhime
ぬし
会議室デビュー日: 2002/09/10
投稿数: 437
投稿日時: 2005-04-08 17:19
rs = (ResultSet)cStmt.getObject(1);
で取得したカーソルを開放していないんだからリークしてて当然でしょう。
forループの最後でrs.close()を入れれば解決しそうな気がします。
faulter
会議室デビュー日: 2005/03/06
投稿数: 11
投稿日時: 2005-04-08 18:13
Anthyhime 様
回答ありがとうございます。

rs.close()はfinallyで行っていますが、
間違っていますか?
エラー時でも、きちんとcloseするように
finallyに全て記述しています。

ちなみに、「forループの最後」
と言うのは、
1)ループ内の最後ですか?
2)ループを出た直後と言うことですか?

両方試してみたのですが、
両方ともカーソルは増えています。
(1)では正常な結果が出力できない)

また、
自身の書いたサンプルの<ストアド>箇所で
実際にはパッケージの宣言部に
「TYPE refcur IS REF CURSOR;」
を宣言しているのですが、
何か関係していますかね?
これを修正すると、
影響が結構大きいので
まだ試せてないのですが、
もしも何か知っていましたら、
よろしくお願いします。

山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2005-04-08 18:29
ストアドプロシージャが関連するかどうかは、ストアドプロシージャを使わない場合と比べてみることで切り分けられるかと思います。お試し下さい。
Anthyhime
ぬし
会議室デビュー日: 2002/09/10
投稿数: 437
投稿日時: 2005-04-10 18:45
> rs.close()はfinallyで行っていますが、
> 間違っていますか?

おそらく間違っています。ループのたびにSPでカーソルが取得されるのであればそのたびに開放しなくてはなりません。確かにJDBCではStatementをクローズすることによりそれから生成されたカーソルを開放する仕様になっていますが、SP経由で生成されたカーソルについては未定義です。
提示されたコードではfinallyで開放されるカーソルは最後に生成されたカーソルのみとなるでしょう。その他のカーソルはすべてオープンされたままのはずです。

スキルアップ/キャリアアップ(JOB@IT)