상세 컨텐츠

본문 제목

ORACLE JDBC 문제와 해결방안들...

IT 세상/DB세상

by 이현민 (지후지율아빠) 2008. 2. 28. 10:56

본문

JDBC FAQ

Q1. Oracle 7.x 에서 한글 입출력이 안되요..

DB에 한글 입출력이 되려면 Oracle DB의 CHARACTER SET이 US7ASCII이어야만 가능합니다.
또 프로그램 소스에서 한글 Character Set을 DB에 넣고 뺄때마다 Character Set 변경을 해
주어야 합니다.
아래에 이러한 일을 해 주는 Utility class를 참고 하세요.

[CharConversion.java]
--------------------------------------------------------------------
import java.lang.*;
import java.io.UnsupportedEncodingException;
public class CharConversion
{
    private static String korean = null;
    private static String english = null;

    public static synchronized String E2K( String english )
    {
        if (english == null ) return "";
        try {
            korean = new String(english.getBytes("8859_1"), "KSC5601");
        }
        catch( UnsupportedEncodingException e ){
            korean = new String(english);
        }
        return korean;
    }

    public static synchronized String K2E( String korean )
    {
        if (korean == null ) return "";
        try {
            english = new String(korean.getBytes("KSC5601"), "8859_1");
        }
        catch( UnsupportedEncodingException e ){
            english = new String(korean);
        }
        return english;
    }
}
--------------------------------------------------------------------

즉, 응용프로그램에서 DB에 넣을 때는 CharConversion.K2E( "DB에 넣을 문자열" ) 을 사용하고,
반대로 DB에서 데이타를 꺼내와서 사용할 때는 CharConversion.E2K( "DB에서 꺼내온 문자열" )
을 사용하여 CharacterSet을 변경하신 후 사용하여야 합니다.

사용 예)
--------------------------------------------------------------------
            createStmt.executeUpdate(
                "INSERT INTO tb_group ( name, fk_user_id ) " +
                "VALUES ('" +
                    CharConversion.K2E(group_name) +  "', '" +
                    CharConversion.K2E(user_id) +  "')"
            );

--------------------------------------------------------------------
            ResultSet rs = null;
            rs = selectStmt.executeQuery(
                "SELECT name " +
                "FROM tb_group " +
                "WHERE name = '" + CharConversion.K2E(group_name) + "' " +
                "and   fk_user_id = '" + CharConversion.K2E(user_id) + "' " +
                "ORDER BY name"
            );

            if ( rs.next() ) {
                selectedGroup.setName(CharConversion.E2K(rs.getString(1)));
            }
--------------------------------------------------------------------

그러나, Oracle 7.3의 경우는 한국 Oracle사에 연락하시면, JDBC를 위한 패치가 있습니다.
패치를 설치하시면, Oracle7.3에서는 위와 같은 변환없이도 한글 입출력이 잘 됩니다.
또한 CharacterSet을 KO16KSC5601 로 마추어 사용할 수 있습니다.


Q2. Oracle 8.0.x에서 한글 입출력이 안되요.

Oracle 8.0 version부터는 DB Character Set이
KSC5601일지라도 한글사용에 전혀 문제가 없습니다.
그냥 그대로 사용하면 한글 입출력이 됩니다.
Oracle 8.0.x대의 정확한 JDBC Driver를 Oracle사
홈페이지에서 다시 받으세요, 버젼별로 JDBC Driver가
다릅니다.
http://www.oracle.com/products/free_software/index.html



Q3. security.Couldn't connect to 'db' with origin from'local-classpath-classes'.

위의 error메세지는 음....
autoexec.bat에 Oracle JDBC Driver path가 걸려 있지는 않나요?

이러한 문제를 해결하는 하나의 방법으로 autoexec.bat파일에 들어 있는
set JAVA_HOME=....
set PATH=......;%JAVA_HOME%\bin
set CLASSPATH=.;%JAVA_HOME%\lib\clases.zip
등과 같은 환경변수를 삭제하세요.

대신 javaenv.bat라는 DOS Batch파일을 만들어 두고 필요할 시에만 DOS창에서 만드신
배치파일을 실행한 후 자바 컴파일이나 키타작업을 하시면 됩니다.

이렇게 하는 이유는 Local CLASSPATH에 Oracle JDBC Classes가 걸려 있을 경우
Netscape는 이 환경변수도 같이 사용을 합니다.
즉, PC의 CLASSPATH에 c:\java_api\jdbc\lib\classes111.zip이 걸려있으면
원격지의 Applet과 함께 Download되는 classes111.zip에 있는 class가 Instance화
되지 않고 Local PC의 JDBC Driver가 Instance화 됩니다.
그러나 Local 환경변수에 걸려있는 jdbc Driver로는 원격지 DB로의 접속이 불가능합니
다. 그것이 웹서버가 있는 머신일지라도 안됩니다. Applet의 Security제약에 걸리는
거죠.
Netscape가 뜨는 시점에서는 모든 CLASSPATH가 비어있는 상태에서 실행하도록 하세요.
물론 Jdbc Driver인 classes111.zip파일은 웹서버로 부터 가져오도록 해야 겠지요.

<applet code=XXX.class archive=classes111.zip
width=... height=...>
</applet>


Q4. Oracle Thin JDBC Driver 연결이 안되요.... "Connection fefused: connect"

  [확인사항]
  1. Oracle Listener를 제대로 띄웠습니까?
  1-1. Oracle 이 정말 starting 되어 있습니까?
  power:/home1/oracle73/app/oracle/product/7.3.2/network/admin# ps -ef|grep ORA
  oracle73 3214 1 0 Oct 13 ? 0:07 oracleORA73 (LOCAL=NO)
  oracle73 455 1 0 Oct 09 ? 0:03 ora_pmon_ORA7
  oracle73 457 1 0 Oct 09 ? 0:07 ora_dbwr_ORA7
  oracle73 459 1 0 Oct 09 ? 0:04 ora_lgwr_ORA7
  oracle73 461 1 0 Oct 09 ? 8:06 ora_smon_ORA7
  oracle73 463 1 0 Oct 09 ? 0:00 ora_reco_ORA7
  oracle73 8705 8662 0 17:52:05 pts/5 0:00 grep ORA

  만약 없다면 Oracle을 띄우세요.
  $ svrmgrl
  ORA> connect internal;
  ORA> startup

  1-2. $ORACLE_HOME/network/admin/listener.ora 파일에
  아래와 같은 설정이 되어 있습니까?
  LISTENER=
  (ADDRESS_LIST=
    (ADDRESS=
      (PROTOCOL=TCP)
      (HOST=javaservice.net) <== www의 IP address
      (PORT=1521)
    )
  )
  ....
  SID_LIST_LISTENER=
    (SID_LIST=
       (SID_DESC=
          (SID_NAME=ORA7)
          (ORACLE_HOME=/home1/oracle73/app/oracle/product/7.3.2)
       )
    .....
    )
  ...

  1-3. lsnrctl status 로 확인 했을 시 아래처럼 ORA7이라는
  SID에 대하여 listener가 떠 있음이 확인 됩니까?
  power:/home1/oracle73/app/oracle/product/7.3.2/network/admin# lsnrctl status
  LSNRCTL for Solaris: Version 2.3.2.1.0 - Production on 19-OCT-98 17:48:56
  Copyright (c) Oracle Corporation 1994. All rights reserved.
  Connecting to (ADDRESS=(PROTOCOL=TCP)(HOST=javaservice.net)(PORT=1521))
  STATUS of the LISTENER
  ------------------------
  Alias LISTENER
  Version TNSLSNR for Solaris: Version 2.3.2.1.0 - Production
  Start Date 09-OCT-98 13:12:48
  Uptime 10 days 4 hr. 32 min. 31 sec
  Trace Level off
  Security OFF
  SNMP OFF
  Listener Parameter File /home1/oracle73/app/oracle/product/7.3.2/network/
  admin/listener.ora
  Listener Log File /home1/oracle73/app/oracle/product/7.3.2/network/log/
  listener.log
  Services Summary...
  BMT has 1 service handler(s)
  ORA7 has 1 service handler(s)
  The command completed successfully

  만약 없다면 $ lsnrctl start 로 띄우세요.

  2. CLASSPATH가 제대로 걸려 있습니까?
  power:/tmp$ env|grep CLASSPATH
  CLASSPATH=.../oracle/jdbc/classes111.zip:.... (JDK 1.1)
  CLASSPATH=.../orcle/jdbc/classes12.zip:.... (JDK 1.2)


  3. listener port가 1521이 맞습니까?
  혹시 1721아닙니까? 대부분 1521 이나 1721 번을 사용합니다.

  4. id/passwd가 정확합니까?(이건 맞겠지...)

  위 사항을 정확하게만 확인하시면 될 겁니다.


Q5. Java Applet + Oracle Thin JDBC Driver 연결이 안되요....

  경우1:
  그럼 Applet에서 Oracle Thin Driver가
  같이 Downloading되도록 해 주었나요?

  Oracle Sample을 수정하지 않고 그대로 사용하시면
  안됩니다.
  먼저 Oracle thin Driver인
  classes111.zip(jdk1.1.1) 을 Applet과 *.html이
  있는 디렉토리에 카피하시구요,
  html화일 내에 아래와 같이 수정하세요.

  <applet codebase=. archive=classes111.zip
  code=XXX.class width=XX height=XX>
  </applet>

  그러면 classes111.zip이 Applet이 다운로딩될 때
  같이 오겠죠?

  PS: Netscape는 동시에 두개의 jar을 설정하시려면
  <applet ....  archive="XXX.jar, YYY.jar, ZZZ.jar" ...>
  </applet>
  와 같은 형식으로 사용하시면 됩니다.

  경우2: oracle이 Web Server가 있는 머신과 같은
  머신에 있는 것이 확실하시죠?
  Applet은 JDBC든, Socket이든, RMI든 전부 기본적으로
  Web Server가 있는 머신과 Connection을 맺을 수 있습니다.

  경우3: Netscape 3.x을 사용하면서 Oracle의 classes111.zip을
  사용하고 있지는 않습니까?
  classes102.zip은 jdk 1.0.2용이고,
  classes111.zip은 jdk 1.1.x용입니다.


Q6. APPLET에서 JDBC : no suitable driver error 문제인데요...
  IE 에서 classes11.zip을 다운은 받았는데 No suitable driver라고 나올경우

  Class.forName("oracle.jdbc.driver.OracleDriver"); 이렇게 사용하지 말구요

  DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
  혹은
  Class c = Class.forName("oracle.jdbc.driver.OracleDriver");
  DriverManager.registerDriver ((Driver)c.newInstance());
  이렇게 사용하세요..
  Netscape에서는 둘 다 되지만 IE에서는 후자만 되네요...


Q6.1 전 Applet 이 아닌데, 위의 에러가 나요.....

  이 것 말고도, DB URL을 잘못 기술하였을 경우도 이 메세지가 나타납니다.
  예를 들어, Oracle thin Driver 의 URL은 다음과 같습니다.
  "jdbc:oracle:thin:@192.168.0.1:1521:ORCL"
  근데, 실수로 아래와 같이 엉뚱한 URL을 기술했을 경우도 위의 에러가 발생합니다.
  "jdbc:thin:@192.168.0.1:1521:ORCL"



Q7. security.Couldn't connect to '172.16.150.1' with origin from '172.16.1.134'

  Java Applet은 JDBC Thin Driver를 사용할 경우 웹서버가 있는 머신의 DB만이 연결
  이 가능합니다. 이것은 JDBC Driver의 제약이 아니라 Java applet의 보안정책인
  거지요.
  Netscape 4.0.x가 되면서 Signed Applet을 이용하면 Web Server Machine 이외의
  host로 연결이 가능하게 됩니다.
  실제 어떻게 하는지는 여기서 많이 기술할 수 없지만, signed applet을
  이용해 구현 해 놓으면 Applet 실행 시에 사용자에게
  "경고! 다른 Host로 접근하려 한다. 허락하겠느냐?"라는 메세지가
  나오고, 사용자가 "Grant"를 하게 되면 그 때 부터 접근제약이 풀리게
  됩니다. 다소 귀찮지요...
  제시하신 환경을 그대로 사용하여 돌릴려면 signed applet를 이용해야
  합니다.

  또 이경우 2-tier로 하지 마시고 가능한 3-tier로 구현하실 것을 권합니다.
  2-tier로 하실경우 Jdbc Driver가 함께 Applet에 포함되어 내려와야
  하므로 size가 다소 커지고(340KB), JDBC Driver가 DB Connection맺는 시간도
  만만찮으며(일부 JDBC Driver는 PooledConnection을 제공해 이 문제를 해결하고
  있습니다만), sql문장이 Client측에 있다보니 여러모로 수행속도가
  많이 떨어 집니다.
  RMI을 이용하거나, VisiBroker for java를 이용해 CORBA로 구현하시거나
  Tengah(혹은 OAS, GemStone, etc..) 등과 같은 EJB 아키텍춰 구조를 사용하는 것이
  좋으리라 생각됩니다.
  Web Server가 있는 곳에 RMI(혹은 CORBA, EJB) Application 서버를 구성하고
  그 녀석(?)이 다른 곳에 있는 자원(DB,혹은 다른 Object Server)를 이용할
  수 있게 하면 되지요... DB를 처음부터 물고 있으니 DB Connection Time이 절약되고
  Load Balance, Fault Tolance, Distributed Transaction Service등을 받을 수 있어
  동시 사용자가 많을 때는 반드시 이런 아키텍춰를 사용해야 합니다.
  2-tier는 간단하거나 작고 단순한 비즈니스 업무에만 사용하셔야 하구요.
  그렇지 않으면 속도문제로 인하여 틀림없이 다시 다 뜯어 고치게 될 겁니다.


Q8. JDBC를 사용하는데, Fail to convert between UTF8 and UCS2 와 같은 SQL Exception이
     나요...

  환경이 무엇이냐에 따라 다를 수 있습니다. 그러나 분명한 것은 UCS2 CharacterSet으로
  되어 있는 문자열을 UTF8로 변환할 수 없다는 소리인 것은 분명하지요.

  이러한 상황이 발생할 수 있는 많은 이유 중 하나가 Oracle 7.3 이하를 사용하고 있을
  경우입니다.
  만약 DB가 Oracle이라면 Oracle7.2의 경우는 본 문서의 Q1을 참조하셔서
  DB에 넣고 빼실때, Character Set을 변환하여 사용하시면 됩니다.
  Oracle 7.3의 경우는 Oracle 7.2처럼 똑같은 방법으로 사용하실 수도 있지만, 더 좋은
  방법은 한국 Oracle사에 연락하셔서 JDBC에서 한글을 원할히 사용하기위한 Oracle패치를
  받아 설치하세요.
  만약 Oracle 8.x 대 버전이라면 특별한 조치 없이 한글을 사용할 수 있는데, 가장 먼저
  DB의 Character Set을 뭘로 마추어 놓으셨는지 확인하세요.

  그러나 여러 프로젝트에서 Oracle버전에 상관없이 이 에러를 토로하고 있습니다.
  다른 ROWS는 잘 되는데 왜 유독 그 컬럼의 값만 그러냐는 것이지요.
  예를 들어, Oracle 7.3 에서 DB CharacterSet을 KO16KSC5601로 되어 있으며, JDBC를
  이용하여 DB에 INSERT를 하였는데, 대부분 잘 되지만, 어떨 때는 위와 같은 에러를 내면서
  SELECT가 안되었습니다. telnet 으로 직접 sqlplus로 들어가서 select 를 해 보면,
  이상이 없는 듯도 하지만, 특정 칼럼의 값이 이상한 문자열로 채워져 있을 수 있습니다.
  왜 이러한 현상이 일어나는 지는 아직 밝혀 진바가 없습니다.

  따라서 이 경우라면, 문제를 야기하는 해당 Data 만 DB에서 날려 버리세요. 그 담부턴
  잘 될 겁니다.

  아래의 나우상님의 글도 참조하세요

  "UTF8과 UCS2 간에 서로 변환할 수 없습니다!"
  http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=959057708


Q9. PreparedStatement Query 추출
preparedStatement를 사용하면, ? 로 남겼던 부분을 완성된 쿼리문으로 보고 싶습니다.
(debugging을 위해) 어떻게 찍어줘야 하나요??
  ex)
    PreparedStatement pstmt = null;
    String query = "UPDATE userinfo SET username= ? ";
    pstmt = conn.prepareStatement( query );

    pstmt.setString(1, uptUser.username);
    // 이 시점에서 query를 출력해 봤자 ?는 그대로 남구요.
    // pstmt를 찍어봤자 reference만 남구요...
    // 완성된 query를 보려면 Statement로 해야 하나요??

그리고, preparedStatement와 Statement 의 차이, 어떨 때 어떤 것을 사용하는 것이 나은지도
알고 싶네요....

A1.   PreparedStatement 의 완전한 Query를 찍는 부분은 저두 무지 찾아보려고
   노력했었지만, 결론은 '안된다'는 것이었습니다.

   그래서, Statement는 query를 그냥 찍고, Preparedstatement는 query와
   해당 field를 모두 찍어야 합니다.

   이곳, 예산청에서 사용하는 방식의 소스를 첨부하니 참조하세요


A2.     PreparedStatement와 Statement의 사용의 조언
  사실 PreparedStatement는 DB에서 한번 parsing하고 계속 쓰겠다는 것이
  멋진 요지인데, Connection이 매번 끊기는 상황에선 속도면에서는 별 의미가
  없습니다.  하지만 잘 보면, 동일한 Connection에 대해서 동일한  SQL이 여러번
  수행되는 때도 있긴 합니다.   그렇지만, 이것을 구현하려면, "동일한 Connection인가?
  그렇다면, PreparedStatement가 이미 생성되었는가? 그렇다면 그걸 쓰자" 식의
  Logic이 DbWrapper에 들어가야 하는데, 하나의 DbWrap에는 여러개의 SQL문장이
  함께 있고, 이러다 보면 가독성을 너무 떨어뜨리게 됩니다.

  결국, 개인적인 생각으로는 대부분 그냥 Statement를 사용하되,  게시판의 긴 글을
  올릴때 특수문자가 포함되어 있을 가능성이 있는, 그래서 SQL 문장을 깨뜨리는 상황일 경우와
  같이 사용범위를 제한하고 있습니다.

  물론, Batch성 Daemon프로그램에선 PreparedStatement를 사용하서 preformance를
  높이는 것이 당연하다고 봅니다.

  --------------
  2002.02.06 정정
  위 A2 답글은 아래의 서민구님의 지적에 따라, 잘못되었음을 인정합니다.
  http://www.javaservice.net/~java/bbs/read.cgi?m=dbms&b=jdbc&c=r_p&n=1010527963


Q10. 오라클에서 직접 다음의 쿼리를 날리면 결과가 잘 나오는데...
select buydate, memberid, identifyid from purchase where buydate > '2005-04-30';

    sql.append("select buydate, memberid, identifyid from purchase where
    buydate > '2005-04-30'");
    rs = stmt.executeQuery(sql.toString()); --1)

라인을 실행시 다음과 같이 에러가 납니다.

java.sql.SQLException: ORA-01861: literal does not match format string
oracle.jdbc.driver.OracleStatement.executeQuery(OracleStatement.java:259)

쿼리문내의 where buydate > '2005-04-30' 부분이 문제인거 같은데...
오라클에선 잘 돌아가는데...왜 이런 에러가 나는 거죠??
정말 답답해서 메일 드립니다.  어떻게 고쳐야 하는지요... 꼬~옥 알려주세요..
감사합니다.

A10. Default Date Format 의 내용이 틀려서 입니다.  sqlplus 로 들어갈 때와
  어플리케이션으로 통해서 들어갈 때, 환경변수와 같은 기타 정보의 내용이
  서로 다르기 때문에 발생합니다.

  따라서 통상 어플리케이션에서 특정 DB Vender 종속적인 코딩을 하지 않는 것이
  바람직하지만, Oracle 의 경우는 너무나 많이들 사용하고 있기 때문에 직접
  Oracle function 을 사용하기도 합니다.

  Query 문은 다음과 같이 사용하세요...

  "... where buydate > '2005-04-30'"
  ==>
  "... where buydate > to_date('2005-04-30','YYYY-MM-DD')"

  NOTE: TO_DATE 함수는 Oracle 내장함수입니다.
  또한 to_char(sysdate, 'YYYY-MM-DD') 와 같이 반대의 경우도 있다는 것 알아두세요...



Q11 JDK 버전과 Oracle JDBC Driver

  JDK 1.1 용은 classes111.zip (혹은 classes111_01.zip)
  JDK 1.2 이상은 classes12.zip (혹은 classes12_01.zip)


Q12 : Protocal Violation 이라니...???
글쓴이: 박승무(bluepark@nownuri.net) 2001/04/17 18:11:22
sun site의 포럼두 다 뒤져보구..이원영씨 말대루.. url일 잘못표기 되었는지..
JDBC가 잘못된건지 다 해봤지만..결국 에러는 전혀 예상치 못했던데.. 있더군요..
테이블에 저장된 데이터를 보다가..깨져서 들어간 데이터가 있더라구여..
한글이...
DB에... 자료들을 부어 넣다가 column length의 제한에 걸려서 한글자료 들이
들어가다 바이트수가 모자라 깨져서 들어간 자료 때문에 그런 에러가 나더라구요..
즉.. 정상적인 자료들을 검색하는 jsp에서는 당연 에러가 안나는데.. 깨진 한글
자료를 select 할 시 Protocal Violation에러가 나더라는 겁니다..
이 에러가 발생하는 경우에는 여러가지 경우가 있겠지만..
결론은 제 경우에..coding에는 전혀 문제가 없었고..DB에 저장된 자료가 깨져서
난 문제였죠..


------------------------------------------------------- 
  본 문서는 자유롭게 배포/복사 할 수 있으나 반드시
  이 문서의 저자에 대한 언급을 삭제하시면 안됩니다
  또한, 관련 인용글의 저작권은 해당글 작성자에게
  있습니다.
================================================
  자바서비스넷 이원영
  E-mail: javaservice@hanmail.net
  PCS:019-310-7324
================================================
반응형

관련글 더보기