Существуют ситуации, в которых ваше 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 oracle byte[] bytea raw(255) postgresql byte[] + @Type(PBA) oid blob oracle byte[] + @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
|
@Lob private 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.