Datové typy v PostgreSQL

V nedávné době jsme se u nás ve firmě potýkali s problémy při používání databáze PostgreSQL. Normálně využíváme MySQL, ale rozhodli jsme se vyzkoušet PostgreSQL pro jeho rychlost.
V tomto článku představím, čeho se problémy týkaly a jaké řešení jsem nakonec našel a použil.

schema-blog

Datové typy MySQL vs PostgreSQL

Problém vznikl u databázových objektů typu LOB – Large Object. Hibernate totiž při použití MySQL vytvoří v databázi sloupec typu BLOB, ale při použití PostgreSQL použije buď OID nebo BYTEA (závisí na anotaci, popsáno níže).
V dalších odstavcích vás stručně seznámím s jmenovanými typy.

MySQL – BLOB

Klasický typ pro velká binární data. Dělí se na 4 další typy:

  • TINYBLOB – nejmenší typ, umožňuje uložit pouze 255 bytů
  • BLOB – umožňuje uložit 65535 bytů
  • MEDIUMBLOB – umožňuje uložit 16777215 bytů
  • LONGBLOB – největší typ, používaný v našem případě, umožňuje uložit až 4096 megabytů

Data se u všech typů ukládají přímo do databáze v binární podobě. Hibernate tento typ vytvoří při anotaci „@Lob“.

PostgreSQL – OID

Tento typ nedrží data přímo v databázi, ale má zde pouze odkaz na data uložená jinde. Díky této konstrukci u nás vznikaly problémy při čtení takového sloupce. Hibernate tento typ vytvoří při anotaci „@Lob“.

PostgreSQL – BYTEA

Tento typ drží data přímo v databázi. Je velmi podobný typu BLOB používanému v MySQL. Hibernate vytvoří tento typ bez použité anotace „@Lob“.

Problém a jeho řešení

Problém se u nás vyskytl při používání sloupců typu LOB. Používali jsme totiž všude anotaci „@Lob“, tím pádem se v PostgreSQL databázi vytvářeli sloupce typu OID. S tímto typem ale mělo problémy naše CzechIdM, konkrétně při naplánovaných úlohách, kde nemohlo načíst data z tohoto sloupce. To způsobilo například nefungování synchronizací, které jsou pro IdM nepostradatelné.

Bylo tedy potřeba buď změnit sloupec na jiný typ, nebo zprovoznit fungování s tímto typem. Rozhodnul jsem se pro první variantu, jelikož chceme mít data uložena v databázi.

Jak je zřejmé z předchozího textu, vybral jsem typ BYTEA, který je nejbližší typu BLOB. Při současném nastavení byl ale vždy vytvořen sloupec typu OID.

První možností, kterou jsme zkoušeli, bylo překrýt výchozí dialekt pro PostgreSQL. To jsme provedli touto konstrukcí:

public PostgreSQLByteaDialect() {
	super();
	registerColumnType(Types.BLOB,"bytea");
}

To se bohužel neprojevilo a stále jsme měli v databázi sloupce typu OID.

Zkusili jsme tedy změnit sloupce v databázi ručně na typ bytea a nad tímto spustit IdM. Při pokusu o čtení z toho sloupce ale vyskočila tato chyba:

ERROR: column "sync_token" is of type bytea but expression is of type oid.

Takže hibernate se stále snažil mapovat sloupec na typ OID.

Změna, která nakonec pomohla se týkala anotací u jednotlivých sloupců.  U těch, u kterých se vyskytovala chyba, jsem jí odebral a v tu chvíli začalo IdM správně vytvářet typ bytea. V tu chvíli začali fungovat všechny potřebné operace nad těmito sloupci a IdM bylo provozuschopné.

Závěr

V průběhu hledání této chyby a jejího řešení jsem narazil na spoustu zajímavostí a rozdílů mezi jednotlivými typy databází. Nejvíce mi asi pomohla tato stránka: http://stackoverflow.com/questions/3677380/proper-hibernate-annotation-for-byte. Pokud byste se s tímto problémem také setkali, či potřebovali poradit s něčím podobným, neváhejte se na mě obrátit na adam.lenger@bcvsolutions.eu.