2012-07-19 31 views
7

Estamos intentando almacenar una cadena codificada en UTF-16 en una base de datos Oracle AL32UTF8.Juego de caracteres Oracle JDBC y límite de caracteres 4000

Nuestro programa funciona perfectamente en una base de datos que usa WE8MSWIN1252 como charset. Cuando tratamos de ejecutarlo en una base de datos que utiliza AL32UTF8 se pone a un java.sql.SQLException: ORA-01461: can bind a LONG value only for insert into a LONG column.

En el caso de prueba a continuación todo funciona bien siempre que nuestros datos de entrada no sean demasiado largos.

la cadena de entrada puede exceder de 4000 caracteres. Deseamos retener la mayor cantidad de información posible, aunque nos damos cuenta de que la entrada tendrá que cortarse.

Nuestras tablas de base de datos se definen utilizando la palabra clave CHAR (ver a continuación). Esperábamos que esto nos permitiera almacenar hasta 4000 caracteres de cualquier conjunto de caracteres. se puede hacer esto? ¿Si es así, cómo?

Hemos intentado convertir el String a UTF8 utilizando un ByteBuffer sin éxito. OraclePreparedStatement.setFormOfUse(...) tampoco nos ayudó.

Cambiar a CLOB no es una opción. Si la cuerda es demasiado larga, debe cortarse.

Este es nuestro código en la actualidad

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){} 
    } 
} 

Este es el script de creación de la tabla simple:

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

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

El caso de prueba funciona perfectamente en los datos cortos. En los datos largos, sin embargo, sigue obteniendo el error. Incluso cuando nuestro longData tiene solo 3000 caracteres de longitud, todavía no se ejecuta correctamente.

¡Gracias de antemano!

Respuesta

7

Antes de Oracle 12.1, una columna VARCHAR2 está limitada a almacenar 4000 bytes de datos en el conjunto de caracteres de la base de datos, incluso si está declarado VARCHAR2(4000 CHAR). Puesto que cada carácter en la cadena requiere 2 bytes de almacenamiento en el juego de caracteres UTF-8, usted no será capaz de almacenar más de 2000 caracteres en la columna. Por supuesto, ese número cambiará si algunos de sus personajes realmente requieren solo 1 byte de almacenamiento o si algunos de ellos requieren más de 2 bytes de almacenamiento. Cuando el conjunto de caracteres de base de datos es Windows-1252, todos los personajes de la cadena requiere solamente un solo byte de almacenamiento por lo que será capaz de almacenar 4000 caracteres en la columna.

Dado que tiene cadenas más largas, ¿sería posible declarar la columna como CLOB en lugar de VARCHAR2? Eso (efectivamente) Retire la limitación de la longitud (hay un límite en el tamaño de un CLOB que depende de la versión de Oracle y el tamaño de bloque, pero es al menos en el rango múltiple GB).

Si está utilizando Oracle 12.1 o posterior, el parámetro max_string_size le permite increase the maximum size of a VARCHAR2 column from 4000 bytes to 32767 bytes.

+0

Gracias por su respuesta. Tristemente, en este caso, usar Clob's está fuera de nuestra consideración. Según [link] (https://forums.oracle.com/forums/thread.jspa?threadID=2369974) esta es la respuesta correcta. Sin embargo, [link] (http://stackoverflow.com/questions/81448/difference-between-byte-and-char-in-column-datatypes) es bastante engañoso en mi humilde opinión. ¿Sabrías dónde se explica esto en la documentación? Hemos estado buscando mucho, pero no pudimos encontrar esto. – Arolition

+0

@Arolition - He añadido un comentario al hilo SO. La respuesta es correcta hasta donde llega. Simplemente no tiene en cuenta que si un 4000 caracteres en particular requiere más de 4000 bytes de almacenamiento, el límite de capacidad de 4000 bytes aún se activa. –

+1

UTF-8 es una codificación de longitud variable. Muchos caracteres asiáticos requieren al menos tres bytes para codificar. –

4

Resolvió este problema cortando el String a la longitud requerida de byte.Tenga en cuenta que esto no puede hacerse simplemente utilizando

stat.substring(0, length) 

ya que esto produce una cadena UTF-8 que podrían ser hasta tres veces más de lo permitido.

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

Nota No utilice stat.getBytes() ya que esto depende del conjunto 'file.encoding' y produce tanto en Windows-1252 o UTF-8 bytes!

Si utiliza Hibernate puede hacerlo utilizando org.hibernate.Interceptor!

Cuestiones relacionadas