2012-07-19 6 views
7

우리는 UTF-16으로 인코딩 된 String을 AL32UTF8 Oracle 데이터베이스에 저장하려고합니다.Oracle JDBC charset 및 4000 char limit

우리 프로그램은 WE8MSWIN1252을 charset으로 사용하는 데이터베이스에서 완벽하게 작동합니다. AL32UTF8을 사용하는 데이터베이스에서 실행하려고하면 java.sql.SQLException: ORA-01461: can bind a LONG value only for insert into a LONG column이됩니다.

아래의 테스트 케이스에서 입력 데이터가 너무 길어지지 않는 한 모든 것이 제대로 작동합니다.

입력 문자열은 4000자를 초과 할 수 있습니다. 우리는 입력을 차단해야한다는 것을 알고 있지만 최대한 많은 정보를 보유하기를 원합니다.

데이터베이스 테이블은 CHAR 키워드 (아래 참조)를 사용하여 정의됩니다. 이렇게하면 4000 자까지 문자 집합을 저장할 수 있습니다. 이 작업을 수행 할 수 있습니까? 그렇다면 어떻게?

ByteBuffer을 사용하여 문자열을 UTF8으로 변환하려고 시도했지만 성공하지 못했습니다. OraclePreparedStatement.setFormOfUse(...)도 도움이되지 않았습니다.

CLOB으로 전환하는 것은 옵션이 아닙니다. 줄이 너무 길면자를 필요가 있습니다.

이 순간에 우리의 코드는 다음과 같습니다

public static void main(String[] args) throws Exception { 
    String ip ="193.53.40.229"; 
    int port = 1521; 
    String sid = "ora11"; 
    String username = "obasi"; 
    String password = "********"; 

    String driver = "oracle.jdbc.driver.OracleDriver"; 
    String url = "jdbc:oracle:thin:@" + ip + ":" + port + ":" + sid; 
    Class.forName(driver); 

    String shortData = ""; 
    String longData = ""; 
    String data; 

    for (int i = 0; i < 5; i++) 
     shortData += "é"; 

    for (int i = 0; i < 4000; i++) 
     longData += "é"; 

    Connection conn = DriverManager.getConnection(url, username, password); 

    PreparedStatement stat = null; 
    try { 
     stat = conn.prepareStatement("insert into test_table_short values (?)"); 
     data = shortData.substring(0, Math.min(5, shortData.length())); 
     stat.setString(1, data); 
     stat.execute(); 

     stat = conn.prepareStatement("insert into test_table_long values (?)"); 
     data = longData.substring(0, Math.min(4000, longData.length())); 
     stat.setString(1, data); 
     stat.execute(); 
    } finally { 
     try { 
      stat.close(); 
     } catch (Exception ex){} 
    } 
} 

이 간단한 테이블의 생성 스크립트입니다

CREATE TABLE test_table_short (
    DATA VARCHAR2(5 CHAR); 
); 

CREATE TABLE test_table_long (
    DATA VARCHAR2(4000 CHAR); 
); 

테스트 케이스가 짧은 데이터를 완벽하게 작동이. 그러나 긴 데이터에서는 오류가 계속 발생합니다. longData의 길이가 3000 자일 경우에도 여전히 성공적으로 실행되지 않습니다.

미리 감사드립니다.

답변

7

오라클 12.1 이전에는 VARCHAR2 열은 VARCHAR2(4000 CHAR)으로 선언 된 경우에도 4000 바이트의 데이터를 데이터베이스 문자 집합에 저장하는 것으로 제한됩니다. 문자열의 모든 문자는 UTF-8 문자 세트에서 2 바이트의 저장 공간을 필요로하므로이 열에 2000자를 초과하여 저장할 수 없습니다. 물론 문자 중 일부가 실제로 1 바이트의 저장 공간 만 필요하거나 그 중 일부가 2 바이트 이상의 저장 공간을 필요로하는 경우 해당 숫자가 변경됩니다. 데이터베이스 문자 집합이 Windows-1252 인 경우 문자열의 모든 문자는 하나의 저장소 바이트 만 필요하므로 열에 4000자를 저장할 수 있습니다.

문자열이 더 길기 때문에 VARCHAR2이 아닌 CLOB으로 열을 선언 할 수 있습니까? 그것은 (사실상) 길이 제한을 제거 할 것입니다 (오라클 버전과 블록 크기에 따라 다르지만 최소한 여러 GB 범위에있는 의 크기에 제한이 있습니다).

오라클 12.1 이상을 사용하는 경우 max_string_size 매개 변수를 사용하면 increase the maximum size of a VARCHAR2 column from 4000 bytes to 32767 bytes을 사용할 수 있습니다.

+0

답장을 보내 주셔서 감사합니다. 슬프게도,이 경우 clob을 사용하는 것은 우리에게 문제가되지 않습니다. [link] (https://forums.oracle.com/forums/thread.jspa?threadID=2369974)에 따르면 이것이 올바른 대답입니다. 그러나, [link] (http://stackoverflow.com/questions/81448/difference-between-byte-and-char-in-column-datatypes)는 저의 겸손한 의견에서 오해의 소지가 있습니다. 설명서에서이 부분이 어디에서 설명되는지 알고 있습니까? 우리는 많은 것을 찾고 있었지만 이것을 찾을 수 없었습니다. – Arolition

+0

@Arolition - SO 스레드에 주석을 추가했습니다. 지금까지와 같은 대답은 정확합니다. 특정 4000 문자가 4000 바이트 이상의 저장 공간을 필요로하고 4000 바이트 용량 제한이 여전히 계속되는 경우에는주의하지 않습니다. –

+1

UTF-8은 가변 길이 인코딩입니다. 많은 아시아 문자는 인코딩하는 데 적어도 3 바이트가 필요합니다. –

4

문자열을 필수 바이트 길이로 잘라서이 문제를 해결했습니다.문제는이가 허용 된 것보다 더 세 번이 될 가능성이있는 UTF-8 문자열을 생성하기 때문에 단순히

stat.substring(0, length) 

를 사용하여 수행 할 수 없습니다. 이 설정 '하여 file.encoding'에 따라 달라집니다 및 Windows-1252 또는 UTF-8 바이트 중 하나를 생산하기 때문에

while (stat.getBytes("UTF8").length > length) { 
    stat = stat.substring(0, stat.length()-1); 
} 

참고 stat.getBytes()를 사용하지 마십시오!

하이버 네이트를 사용한다면 org.hibernate.Interceptor!