Na veel speurwerk in de broncode van Hibernate en de PostgreSQL JDBC-driver heb ik de oorzaak van het probleem kunnen vinden. Uiteindelijk wordt de methode write() van de BlobOutputStream (geleverd door de JDBC-driver) aangeroepen om de inhoud van de Clob in de database te schrijven. Deze methode ziet er als volgt uit:
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());
}
}
Deze methode neemt een 'int' (32 bits/4 bytes) als argument en converteert het naar een 'byte' (8 bits/1 byte) waarbij effectief 3 bytes aan informatie verloren gaan. Stringrepresentaties binnen Java zijn UTF-16-gecodeerd, wat betekent dat elk teken wordt weergegeven door 16 bits/2 bytes. Het Euro-teken heeft de int-waarde 8364. Na conversie naar byte blijft de waarde 172 over (in octetweergave 254).
Ik weet niet zeker wat nu de beste oplossing is voor dit probleem. IMHO zou het JDBC-stuurprogramma verantwoordelijk moeten zijn voor het coderen/decoderen van de Java UTF-16-tekens voor elke codering die de database nodig heeft. Ik zie echter geen aanpassingsmogelijkheden in de JDBC-stuurprogrammacode om het gedrag ervan te wijzigen (en ik wil niet mijn eigen JDBC-stuurprogrammacode schrijven en onderhouden).
Daarom heb ik Hibernate uitgebreid met een aangepast ClobType en erin geslaagd om de UTF-16-tekens naar UTF-8 te converteren voordat ik naar de database schreef en vice versa bij het ophalen van de Clob.
De oplossingen zijn te groot om gewoon in dit antwoord te plakken. Als je geïnteresseerd bent, stuur me dan een bericht, en ik stuur het naar je.
Proost, Franck