Статьи

Совместимость Postgres и Oracle с Hibernate

Существуют ситуации, в которых ваше 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.

Ссылка: Совместимость Postgres и Oracle с Hibernate от нашего партнера по JCG Питера Бутковича в блоге pb о жизни и ИТ- блоге.