Существуют ситуации, в которых ваше JEE-приложение должно поддерживать Postgres и Oracle в качестве базы данных.
Hibernate должен сделать эту работу здесь, однако есть некоторые особенности, которые стоит упомянуть.
При включении Postgres для приложения, уже работающего под управлением Oracle, я натолкнулся на следующие сложные моменты:
- Поддержка
BLOBs, - Поддержка
CLOBs, - Oracle не знает
Booleanтипа (используяInteger) и -
DUALстол.
Это были уловки, которые я должен был применить, чтобы классы @Entity работали на обоих из них.
Обратите внимание, что я использовал Postgres 9.3 с Hibernate 4.2.1.SP1.
Поддержка BLOB
Проблема с Postgres состоит в том, что он предлагает 2 типа хранилища больших двоичных объектов:
-
bytea— данные хранятся в таблице -
oid— таблица содержит только идентификатор данных, хранящихся в другом месте
Я полагаю, что в большинстве ситуаций вы можете жить с bytea так же, как и я. Другой, насколько я прочитал, должен использоваться для больших данных (в гигабайтах), поскольку он поддерживает потоки для операций ввода-вывода.
Что ж, звучит хорошо, что такая поддержка есть, однако использование Hibernate в этом случае может создать некоторые проблемы (из-за необходимости использовать определенные аннотации), особенно если вы пытаетесь достичь совместимости с Oracle.
Чтобы увидеть проблему здесь, смотрите StackOverflow: правильная аннотация спящего режима для byte []
Все комбинации описаны там:
|
1
2
3
4
5
6
|
annotation postgres oracle works on-------------------------------------------------------------byte[] + @Lob oid blob oraclebyte[] bytea raw(255) postgresqlbyte[] + @Type(PBA) oid blob oraclebyte[] + @Type(BT) bytea blob postgresql |
где @Type(PBA) обозначает @Type(type="org.hibernate.type.PrimitiveByteArrayBlobType") а @Type(BT) обозначает: @Type(type="org.hibernate.type.BinaryType") .
Это приводит ко всем видам ошибок Postgres, например:
ОШИБКА: столбец «foo» имеет тип oid, но выражение имеет тип bytea
или же
ОШИБКА: столбец «foo» имеет тип bytea, но выражение имеет тип oid
Что ж, похоже, решение есть, но оно все же включает исправления библиотеки Hibernate (то, что я вижу в качестве последнего варианта при игре с библиотекой 3.rd party).
Существует также ссылка на официальный пост в блоге от ребят из Hibernate на тему: PostgreSQL и BLOB . Тем не менее решение, описанное в сообщении в блоге, кажется, не работает для меня и, основываясь на комментариях, кажется недействительным для большего количества людей.
BLOB решены
ОК, теперь оптимистичная часть.
После некоторой отладки я получил следующее определение Entity:
|
1
2
|
@Lobprivate byte[] foo; |
У Oracle нет проблем с этим, более того, мне пришлось настроить диалект Postgres таким образом:
|
01
02
03
04
05
06
07
08
09
10
|
public class PostgreSQLDialectCustom extends PostgreSQL82Dialect { @Override public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { if (sqlTypeDescriptor.getSqlType() == java.sql.Types.BLOB) { return BinaryTypeDescriptor.INSTANCE; } return super.remapSqlTypeDescriptor(sqlTypeDescriptor); }} |
Это оно! Довольно просто, верно? Это работает для сохранения bytea с bytea в Postgres (поскольку это соответствует моему сценарию использования).
Поддержка CLOB
Ошибки в неправильной конфигурации выглядели примерно так:
|
1
|
org.postgresql.util.PSQLException: Bad value for type long : ... |
Итак, сначала я нашел (в String LOB на PostgreSQL с Hibernate 3.6 ) следующее решение:
|
1
2
3
|
@Lob@Type(type = "org.hibernate.type.TextType")private String foo; |
Ну, это работает, но только для Postgres.
Затем было предложение (в StackOverflow: сгустки Postgres UTF-8 с JDBC ) пойти на:
|
1
2
3
|
@Lob@Type(type="org.hibernate.type.StringClobType")private String foo; |
Это указало мне правильное направление (забавная часть состояла в том, что это был просто комментарий к некоторым ответам). Это было довольно близко, но не работало для меня во всех случаях, все еще приводило к ошибкам в моих тестах.
CLOB решены
Важным был @deprecation javadocs в org.hibernate.type.StringClobType который привел меня к работе:
|
1
2
3
|
@Lob@Type(type="org.hibernate.type.MaterializedClobType")private String foo; |
Это работает как для Postgres, так и для Oracle, без какого-либо дополнительного взлома (на стороне Hibernate).
Логический тип
Oracle не знает Boolean типа, и проблема в том, что Postgres знает. Так как присутствовал также некоторый простой SQL, я оказался в Postgres с ошибкой:
ОШИБКА: столбец «foo» имеет тип boolean, но выражение имеет тип integer
Я решил включить приведение из Integer в Boolean в Postgres, а не фиксировать все обычные места SQL (способом, найденным в Forum: Автоматическое приведение из Integer в Boolean ):
|
1
|
update pg_cast set castcontext = 'i' where oid in ( select c.oid from pg_cast c inner join pg_type src on src.oid = c.castsource inner join pg_type tgt on tgt.oid = c.casttarget where src.typname like 'int%' and tgt.typname like 'bool%'); |
Обратите внимание, что вы должны запускать обновление SQL пользователем с полномочиями для обновления каталогов (вероятно, не ваш пользователь postgres, используемый для подключения к БД из вашего приложения), как я узнал в Stackoverflow: Postgres — в обновлении pg_catalog.pg_cast отказано .
ДВОЙНОЙ стол
Есть еще одна особенность Оракула, с которой я столкнулся. Если у вас простой SQL, в Oracle есть таблица DUAL (см. Дополнительную информацию в Википедии ), которая может повредить вам в Postgres.
Тем не менее решение простое. В Postgres создайте представление, которое бы удовлетворяло аналогичной цели. Это можно создать так:
|
1
|
create or replace view dual as select 1; |
Вывод
Ну, это должно быть. Наслаждайтесь кросс-DB совместимыми приложениями JEE.