Eu tenho código java aparando uma string UTF-8 para o tamanho da minha coluna Oracle (11.2.0.4.0) que acaba gerando um erro porque java e Oracle veem a string como comprimentos de bytes diferentes. Eu verifiquei que meu NLS_CHARACTERSET
parâmetro no Oracle é 'UTF8'.
Escrevi um teste que ilustra meu problema abaixo usando o emoji de esquilo 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();
}
Isso falha na última linha do teste com a seguinte mensagem:
ORA-12899: valor muito grande para a coluna
"MYSCHEMA". "TEMP". "FOO" (real: 9, máximo: 7)
A configuração de NLS_LENGTH_SEMANTICS
é BYTE
. Infelizmente, não posso mudar isso, pois é um sistema legado. Não estou interessado em aumentar o tamanho da coluna, apenas em poder prever com precisão o tamanho do Oracle de uma string.
Respostas:
O que se segue é minha especulação.
Java
String
s são representados internamente usando a codificação UTF-16 . Quando vocêgetBytes("UTF-8")
converte Java entre as duas codificações, e provavelmente usa uma plataforma Java atualizada.Quando você tenta armazenar um Java
String
no banco de dados, o Oracle também realiza a conversão entre o UTF-16 nativo do Java e o conjunto de caracteres do banco de dados, conforme determinado porNLS_CHARACTERSET
.O caractere de esquilo foi aprovado como parte do padrão Unicode em 2014 (de acordo com a página que você vinculou), enquanto a versão mais recente do Oracle 11g rel.2 foi publicada em 2013 .
Pode-se supor que o Oracle use um algoritmo de conversão de caracteres diferente ou desatualizado, de modo que a representação de bytes de 🐿️) no servidor (9 bytes de comprimento) seja diferente do que
getBytes()
retorna no cliente (7 bytes).Acho que para resolver esse problema, você pode atualizar o servidor Oracle ou usar UTF-16 como o conjunto de caracteres do banco de dados.
fonte
O problema está no manuseio de caracteres unicode suplementares pela Oracle, quando
NLS_LENGTH_SEMANTICS
houverUTF8
.A partir da documentação (ênfase adicionada).
Além disso, o último ponto de código na cadeia de esquilo é um seletor de variação e opcional. Vi isso usando um inspetor de caracteres unicode
Depois de alterar o
NLS_CHARACTERSET
parâmetro do banco de dados paraAL32UTF8
o teste aprovado.fonte