18 de septiembre de 2013

Sybase, Unicode, y Java JDBC

Hoy necesité insertar un texto similar al siguiente en una base de datos Sybase ASE 15.x desde Java usando JDBC:

aaa aáa bbb’ aaa

El texto tenía tanto acentos como otros caracteres usuales en español, así como algunos otros no tan usuales. En la cadena de ejemplo, el noveno caracter no es un apóstrofo (RAE), sino comillas simples de apertura (RAE). Además, al final los puntos supensivos (RAE) no son tres caracter de punto (RAE), sino un solo caracter. Estos caracteres están definidos en el estándar Unicode.

Al intentar insertarla, el driver JDBC JConnect para Sybase lanzaba una excepción, diciendo que se había intentado insertar un registro con una columna con un valor de nulo. El problema no era que estuviera vacía la cadena, sino tenía que ver con la codificación de los caracteres.

Encontré dos soluciones:

1. Especificar el código de caracteres

Al conectarse desde Java usando JDBC, especificar la codificación que se usará [1][2].
Properties props = new Properties();
props.put("charset", "utf8");
Connection conn = DriverManager.getConnection(JDBC_URL, props);

Esto quizá implicará que se deberá cuidar cuáles funciones de texto en Sybase se ocupan. En Java, JDBC se encarga de hacer la conversión transparente.

2. Eliminar los caracteres inválidos

Hacer que Java no envíe caracteres que sean inválidos en la codificación que utiliza el servidor Sybase. En mi caso, estaba usando la codificación ISO-8859-1, así que en Java yo tenia que quitar o convertir los caracteres que no tenían representación en esa codificación. Para lo que yo necesitaba, no me importaba sustituirlos todos por algún caracter que no fuera una letra.

// texto es un String con la cadena "aaa aáa ‘bbb’ aaa…"
byte[] bytes = texto.getBytes(StandardCharsets.ISO_8859_1);
texto = new String(bytes, StandardCharsets.ISO_8859_1);

Esto provoca que los caracteres que no se puedan convertir se sustituyan con un caracter de cierre interrogación.

aaa aáa ?bbb? aaa?

Las letras acentuadas se convervan, pues la codificación ISO-8859-1 sí las contienen.

3. Una solución que no es solución

En Sybase se puede especificar que la columna almacenará una cadena que estará codificada usando Unicode [3][4].
CREATE TABLE Tabla (
   Texto UNIVARCHAR(50)
)
Esto hace que Sybase ocupe dos bytes para almacenar cada caracter. Sin embargo, a mí no me funcionó esta solución.

Finalmente

Existen otros detalles de este asunto: Java utiliza internamente Unicode, y específicamente UTF-16 para codificar las cadenas de caracteres, y que bajo esa codificación existen caracteres que ocupan 4 bytes, lo cual implica que en JDBC y Sybase su manejo deberá ser un poco diferente. Sin embargo, eso no lo necesito por hoy.

Referencias:

[1]
https://www.java.net//node/688046
[2]
http://www.sybase.com/detail?id=1009812#MARKER-9-243
[3]
http://es.wikipedia.org/wiki/ISO_8859-1
[4]
http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.ase_15.0.sag1/html/sag1/sag1417.htm

No hay comentarios.:

Publicar un comentario