MS-SQL JDBC "ResultSet Can Not Re-Read Row Data" 예외 처리 방법
작성자 : 김영철
등록날짜 : 2009.01.24 23:13
MS-SQL 2000 서버를 JDBC를 이용해서 사용하다 보면 "ResultSet Can Not Re-Read Row Data"' Error When Reading Data from a JDBC ResultSet Object"라는 에러가 발생하는 경우가 있다. 일반적인 VARCHAR 필드나 NUMBER 타입의 필드를 사용할 때는 발생하지 않는다. 그러나 TEXT, NTEXT, IMAGE 타입 필드(BLOB 타입)를 사용할 경우에는 위와 같은 예외 메시지가 나타나는 경우가 있다.
그러한 예외가 발생하는 이유는 BLOB 데이터를 읽어 올 때는 Cache 문제 때문에 무조건 순서대로 읽어 와야 하는데 있다. 자세한 이유는 여기에서 확인할 수 있다.
이를 해결하는 방법은 간단하다. 무조건 앞의 것 부터 읽어 오도록 설정을 해주면 된다. 즉 다음과 같은 방식으로 처리 해주면된다.
rs.getString(1);
rs.getString(2);
그렇지만 이를 어기고
rs.getString(2);
rs.getString(1);
과 같이 데이터를 가져오려고 하면 위와 같은 에러가 발생한다. 경우에 따라서 (내 경우가 이에 속한다) 결과 Set을 처리하기 위한 Utility 클래스를 만들어서 사용하는 경우가 있다. 예를 들어 결과 데이터를 가져와서 HashMap이나 Hashtable을 이용하는 경우이다. 내 경우에는 아래와 같은 메소드를 만들어 사용하고 있다.
public HashMap getGeneralEntity()
throws SQLException
{
if (m_rs == null || m_rsmd == null)
return null;
HashMap result = new HashMap();
String strColumnName = null;
BigDecimal numberValue = null;
String stringValue = null;
Boolean booleanValue = null;
Timestamp dateValue = null;
// 2004-10-22 : 기존에 데이터를 역순으로 읽도록 되어 있는 것을 수정하여 처음 것 부터 순서대로 읽어 오도록 한다.
// 기존에 역순으로 되어 있는 것은 Hashmap을 사용하므로 동일 이름의 컬럼이 존재하는 경우 첫번째 것 부터 읽혀지므로
// 결과 적으로 나중의 것이 읽혀 지게 된다. 그래서 첫번째 데이터에 우선 순위를 주기 위해서 사용한다.
// for (int i=(m_rsmd.getColumnCount()); i > 0; i--)
for (int i=1; i <= m_rsmd.getColumnCount(); i++)
{
strColumnName = m_rsmd.getColumnName(i).trim().toUpperCase();
// 컬럼의 이름은 TRIM 해준다.
if (strColumnName != null)
strColumnName = strColumnName.trim();
else
continue;
// 동일 이름의 데이터가 이미 저장되어 있는지 검사하여 존재하는 경우에는 SKIP 해준다.
if (result.containsKey(strColumnName))
continue;
switch (checkType(m_rsmd.getColumnType(i)))
{
case TYPE_STRING :
if (m_rsmd.getColumnType(i) != Types.CLOB)
stringValue = m_rs.getString(i);
else
stringValue = SQLUtil.getClobData(m_rs.getClob(i));
result.put(strColumnName, stringValue);
break;
case TYPE_NUMBER :
numberValue = m_rs.getBigDecimal(i);
result.put(strColumnName, numberValue);
break;
case TYPE_DATE :
dateValue = m_rs.getTimestamp(i);
result.put(strColumnName, dateValue);
break;
case TYPE_BOOLEAN :
booleanValue = new Boolean(m_rs.getBoolean(i));
result.put(strColumnName, booleanValue);
break;
default :
result.put(strColumnName, null);
}
}
return result;
}
위의 소스는 사용하고 있는 클래스의 일부분이다. 간단히 설명하면 ResultSetMetaData로 부터 컬럼 정보를 받은 다음에 컬럼 정보를 가지고 데이터를 적절한 형태로 빼오는 기능이다. (내부적으로 사용하고 있는 checkType(int) 등은 별도로 만든 메소드이다.)
이와 같은 식으로 구현을 하면 위의 예외를 피해 갈 수 있다.
그러한 예외가 발생하는 이유는 BLOB 데이터를 읽어 올 때는 Cache 문제 때문에 무조건 순서대로 읽어 와야 하는데 있다. 자세한 이유는 여기에서 확인할 수 있다.
이를 해결하는 방법은 간단하다. 무조건 앞의 것 부터 읽어 오도록 설정을 해주면 된다. 즉 다음과 같은 방식으로 처리 해주면된다.
rs.getString(1);
rs.getString(2);
그렇지만 이를 어기고
rs.getString(2);
rs.getString(1);
과 같이 데이터를 가져오려고 하면 위와 같은 에러가 발생한다. 경우에 따라서 (내 경우가 이에 속한다) 결과 Set을 처리하기 위한 Utility 클래스를 만들어서 사용하는 경우가 있다. 예를 들어 결과 데이터를 가져와서 HashMap이나 Hashtable을 이용하는 경우이다. 내 경우에는 아래와 같은 메소드를 만들어 사용하고 있다.
public HashMap getGeneralEntity()
throws SQLException
{
if (m_rs == null || m_rsmd == null)
return null;
HashMap result = new HashMap();
String strColumnName = null;
BigDecimal numberValue = null;
String stringValue = null;
Boolean booleanValue = null;
Timestamp dateValue = null;
// 2004-10-22 : 기존에 데이터를 역순으로 읽도록 되어 있는 것을 수정하여 처음 것 부터 순서대로 읽어 오도록 한다.
// 기존에 역순으로 되어 있는 것은 Hashmap을 사용하므로 동일 이름의 컬럼이 존재하는 경우 첫번째 것 부터 읽혀지므로
// 결과 적으로 나중의 것이 읽혀 지게 된다. 그래서 첫번째 데이터에 우선 순위를 주기 위해서 사용한다.
// for (int i=(m_rsmd.getColumnCount()); i > 0; i--)
for (int i=1; i <= m_rsmd.getColumnCount(); i++)
{
strColumnName = m_rsmd.getColumnName(i).trim().toUpperCase();
// 컬럼의 이름은 TRIM 해준다.
if (strColumnName != null)
strColumnName = strColumnName.trim();
else
continue;
// 동일 이름의 데이터가 이미 저장되어 있는지 검사하여 존재하는 경우에는 SKIP 해준다.
if (result.containsKey(strColumnName))
continue;
switch (checkType(m_rsmd.getColumnType(i)))
{
case TYPE_STRING :
if (m_rsmd.getColumnType(i) != Types.CLOB)
stringValue = m_rs.getString(i);
else
stringValue = SQLUtil.getClobData(m_rs.getClob(i));
result.put(strColumnName, stringValue);
break;
case TYPE_NUMBER :
numberValue = m_rs.getBigDecimal(i);
result.put(strColumnName, numberValue);
break;
case TYPE_DATE :
dateValue = m_rs.getTimestamp(i);
result.put(strColumnName, dateValue);
break;
case TYPE_BOOLEAN :
booleanValue = new Boolean(m_rs.getBoolean(i));
result.put(strColumnName, booleanValue);
break;
default :
result.put(strColumnName, null);
}
}
return result;
}
위의 소스는 사용하고 있는 클래스의 일부분이다. 간단히 설명하면 ResultSetMetaData로 부터 컬럼 정보를 받은 다음에 컬럼 정보를 가지고 데이터를 적절한 형태로 빼오는 기능이다. (내부적으로 사용하고 있는 checkType(int) 등은 별도로 만든 메소드이다.)
이와 같은 식으로 구현을 하면 위의 예외를 피해 갈 수 있다.
[출처] 민주가인
"쇼핑몰·홈페이지·오픈마켓
블로그·페이스북·이메일 등의 각종 마케팅 글쓰기, 각종 광고, 영업, 판매, 제안서, 전단지 반응율 3배×10배 이상 높이는 마법의 8단계 공식" |
☞자세히보기 |
|
|