Tengo un código Java que recorta una cadena UTF-8 al tamaño de mi columna Oracle (11.2.0.4.0) que termina arrojando un error porque Java y Oracle ven la cadena como longitudes de bytes diferentes. Verifiqué que mi NLS_CHARACTERSET
parámetro en Oracle es 'UTF8'.
Escribí una prueba que ilustra mi problema a continuación usando el emoji chipmunk unicode (🐿️)
public void test() throws UnsupportedEncodingException, SQLException {
String squirrel = "\uD83D\uDC3F\uFE0F";
int squirrelByteLength = squirrel.getBytes("UTF-8").length; //this is 7
Connection connection = dataSource.getConnection();
connection.prepareStatement("drop table temp").execute();
connection.prepareStatement("create table temp (foo varchar2(" + String.valueOf(squirrelByteLength) + "))").execute();
PreparedStatement statement = connection.prepareStatement("insert into temp (foo) values (?)");
statement.setString(1, squirrel);
statement.executeUpdate();
}
Esto falla en la última línea de la prueba con el siguiente mensaje:
ORA-12899: valor demasiado grande para la columna
"MISQUEMA". "TEMP". "FOO" (real: 9, máximo: 7)
El ajuste de NLS_LENGTH_SEMANTICS
es BYTE
. Desafortunadamente, no puedo cambiar esto, ya que es un sistema heredado. No me interesa aumentar el tamaño de la columna, solo poder predecir de manera confiable el tamaño de Oracle de una cadena.
Respuestas:
Lo que sigue es mi especulación.
Los Java
String
s se representan internamente utilizando la codificación UTF-16 . CuandogetBytes("UTF-8")
Java convierte entre las dos codificaciones, y probablemente usa una plataforma Java actualizada.Cuando intenta almacenar un Java
String
en la base de datos, Oracle también realiza la conversión entre el UTF-16 nativo de Java y el conjunto de caracteres de la base de datos según lo determinado porNLS_CHARACTERSET
.El personaje chipmunk fue aprobado como parte del estándar Unicode en 2014 (de acuerdo con la página que ha vinculado), mientras que la última versión de Oracle 11g rel.2 se publicó en 2013 .
Se podría suponer que Oracle usa un algoritmo de conversión de caracteres diferente u obsoleto, por lo que la representación de byte de 🐿️) en el servidor (9 bytes de longitud) es diferente de la que
getBytes()
devuelve el cliente (7 bytes).Supongo que para resolver este problema, puede actualizar su servidor Oracle o usar UTF-16 como el conjunto de caracteres de la base de datos.
fuente
El problema es con el manejo de Oracle de caracteres Unicode suplementarios cuando
NLS_LENGTH_SEMANTICS
esUTF8
.De la documentación (énfasis agregado).
Además, el último punto de código en la cadena de ardilla es un selector de variación y opcional. Vi esto usando un inspector de caracteres Unicode
Después de cambiar el
NLS_CHARACTERSET
parámetro de la base de datos aAL32UTF8
la prueba aprobada.fuente