Po długim grzebaniu w kodzie źródłowym Hibernate i sterowniku PostgreSQL JDBC udało mi się znaleźć przyczynę problemu. Na koniec wywoływana jest metoda write() BlobOutputStream (dostarczana przez sterownik JDBC) w celu zapisania zawartości Cloba w bazie danych. Ta metoda wygląda tak:
public void write(int b) throws java.io.IOException
{
checkClosed();
try
{
if (bpos >= bsize)
{
lo.write(buf);
bpos = 0;
}
buf[bpos++] = (byte)b;
}
catch (SQLException se)
{
throw new IOException(se.toString());
}
}
Ta metoda przyjmuje argument „int” (32 bity/4 bajty) i konwertuje go na „bajt” (8 bitów/1 bajt), skutecznie tracąc 3 bajty informacji. Reprezentacje łańcuchowe w Javie są zakodowane w UTF-16, co oznacza, że każdy znak jest reprezentowany przez 16 bitów/2 bajty. Znak Euro ma wartość int 8364. Po konwersji na bajt pozostaje wartość 172 (w reprezentacji oktetu 254).
Nie jestem pewien, jakie jest teraz najlepsze rozwiązanie tego problemu. IMHO sterownik JDBC powinien być odpowiedzialny za kodowanie/dekodowanie znaków Java UTF-16 do dowolnego kodowania wymaganego przez bazę danych. Jednak nie widzę żadnych możliwości ulepszeń w kodzie sterownika JDBC, aby zmienić jego zachowanie (i nie chcę pisać i utrzymywać własnego kodu sterownika JDBC).
Dlatego rozszerzyłem Hibernate o niestandardowy ClobType i udało mi się przekonwertować znaki UTF-16 na UTF-8 przed zapisaniem do bazy danych i odwrotnie podczas pobierania Clob.
Rozwiązania są zbyt duże, aby po prostu wkleić je w tej odpowiedzi. Jeśli jesteś zainteresowany, napisz do mnie, a ja wyślę ci to.
Pozdrawiam, Frank