Po dlouhém pátrání ve zdrojovém kódu Hibernate a ovladači PostgreSQL JDBC se mi podařilo najít hlavní příčinu problému. Nakonec je vyvolána metoda write() BlobOutputStream (poskytovaná ovladačem JDBC), aby zapsala obsah Clobu do databáze. Tato metoda vypadá takto:
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());
}
}
Tato metoda bere jako argument 'int' (32 bitů/4 bajty) a převádí jej na 'byte' (8 bitů/1 bajt), čímž efektivně ztrácí 3 bajty informace. Reprezentace řetězců v Javě jsou kódovány UTF-16, což znamená, že každý znak je reprezentován 16 bity/2 byty. Znak eura má hodnotu int 8364. Po převodu na bajt zůstane hodnota 172 (v oktetu 254).
Nejsem si jistý, jaké je nyní nejlepší řešení tohoto problému. IMHO ovladač JDBC by měl být zodpovědný za kódování/dekódování znaků Java UTF-16 do jakéhokoli kódování, které databáze potřebuje. Nevidím však žádné možnosti úprav v kódu ovladače JDBC, které by změnily jeho chování (a nechci psát a udržovat svůj vlastní kód ovladače JDBC).
Proto jsem rozšířil Hibernate o vlastní ClobType a podařilo se mi převést znaky UTF-16 na UTF-8 před zápisem do databáze a naopak při načítání Clob.
Řešení je příliš velké na to, aby je bylo možné jednoduše vložit do této odpovědi. Pokud máte zájem, napište mi a já vám to pošlu.
Na zdraví, Francku