Правильная аннотация для спящего режима для байта []

У меня есть приложение, использующее аннотации hibernate 3.1 и JPA. Он имеет несколько объектов с байтовыми атрибутами (размером 1k - 200k). Он использует аннотацию JPA @Lob, а hibernate 3.1 может читать их просто отлично во всех основных базах данных - похоже, скрывает особенности поставщика JDBC Blob (как и должно быть).

@Entity
public class ConfigAttribute {
  @Lob
  public byte[] getValueBuffer() {
    return m_valueBuffer;
  }
}

Нам пришлось обновить до 3.5, когда мы обнаружили, что hibernate 3.5 разбивает (и не исправляет) эту комбинацию аннотаций в postgresql (с нет обходного пути). Я до сих пор не нашел четкого исправления, но заметил, что если я просто удалю @Lob, он использует тип postgresql типа bytea (который работает, но только для postgres).

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

once you use @Type, @Lob seems to not be relevant
note: oracle seems to have deprecated the "raw" type since 8i.

Я ищу способ иметь один аннотированный класс (со свойством blob), который переносится через основные базы данных.

  • Что такое переносимый способ аннотировать свойство byte []?
  • Исправлено ли это в какой-то новой версии спящего режима?

Update: После прочтения этого блога Я, наконец, выяснил, каково было обходное решение проблемы JIRA: очевидно, вы должны отказаться от @Lob и аннотировать свойство как:

@Type(type="org.hibernate.type.PrimitiveByteArrayBlobType") 
byte[] getValueBuffer() {...

Однако это не работает для меня - я все равно получаю OID вместо bytea; он, однако, работал для автора проблемы JIRA, который, казалось, хотел бы получить.

После ответа от A. Garcia я попробовал эту комбо, которая на самом деле работает на postgresql, но не на оракуле.

@Type(type="org.hibernate.type.BinaryType") 
byte[] getValueBuffer() {...

То, что мне действительно нужно сделать, - это контролировать, что @org.hibernate.annotations.Type комбинация (@Lob + byte [] получает сопоставление) до (на postgresql).


Вот фрагмент из 3.5.5.Final из MaterializedBlobType (sql type Blob). Согласно блогу Стива, postgresql хочет, чтобы вы использовали Streams for bytea (не спрашивайте меня почему) и пользовательский тип BLOB postgresql для oids. Также обратите внимание, что использование setBytes() в JDBC также для байта (из прошлого опыта). Таким образом, это объясняет, почему использование потоков не влияет на то, что оба они предполагают "bytea".

public void set(PreparedStatement st, Object value, int index) {
 byte[] internalValue = toInternalFormat( value );
 if ( Environment.useStreamsForBinary() ) {
  // use streams = true
   st.setBinaryStream( index, 
    new ByteArrayInputStream( internalValue ), internalValue.length );
 }
 else {
  // use streams = false
  st.setBytes( index, internalValue );
 }
}

Это приводит к:

ERROR: column "signature" is of type oid but expression is of type bytea

Обновление Следующий логический вопрос: "Почему бы не просто изменить определения таблиц вручную на bytea" и сохранить (@Lob + byte [])? Это работает, UNTIL вы пытаетесь сохранить нулевой байт []. Что думает драйвер postgreSQL, является выражением типа OID, а тип столбца - bytea - это потому, что hibernate (по правде) вызывает JDBC.setNull() вместо JDBC.setBytes(null), который ожидает драйвер PG.

ERROR: column "signature" is of type bytea but expression is of type oid

Система типов в спящем режиме в настоящее время является "незавершенной" (согласно 3.5.5 комментариям об устаревании). На самом деле такая часть кода 3.5.5 устарела, трудно понять, на что следует обратить внимание при подклассификации PostgreSQLDialect).

AFAKT, Types.BLOB/'oid' в postgresql должны быть сопоставлены с каким-то настраиваемым типом, который использует доступ JDBC в стиле OID (т.е. объект PostgresqlBlobType и NOT MaterializedBlobType). Я никогда фактически не использовал Blobs с postgresql, но я знаю, что bytea просто работает как один/я ожидаю.

В настоящее время я смотрю на BatchUpdateException - возможно, что драйвер не поддерживает пакетную обработку.


Отличная цитата из 2004:   "Подводя итоги, я бы сказал, что мы должны дождаться, когда драйвер JDBC правильно выполнит LOB, прежде чем сменить Hibernate."

Литература:

Ответ 1

Каков переносимый способ аннотировать свойство byte []?

Это зависит от того, что вы хотите. JPA может сохраняться без аннотаций byte[]. Из спецификации JPA 2.0:

11.1.6 Основная аннотация

Аннотация Basic является самой простой тип отображения в столбец базы данных. Аннотацию Basic можно применить к постоянному свойству или экземпляру переменная любого из следующих типы: примитив Java, типы, обертки из примитивных типов, java.lang.String java.math.BigInteger java.math.BigDecimal java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp, byte[], byte[], char[], Character[], перечисления и любые другие тип, который реализует Serializable. Как описано в разделе 2.8, использование аннотации Basic не является обязательным для постоянных полей и свойств этих типов. Если базовый аннотация не указывается для такого поле или свойство, значения по умолчанию базовой аннотации.

И Hibernate сопоставляет его по умолчанию с SQL VARBINARY (или SQL LONGVARBINARY в зависимости от размера Column?), который PostgreSQL обрабатывает с помощью bytea.

Но если вы хотите, чтобы byte[] хранился в Большом объекте, вы должны использовать @Lob. Из спецификации:

11.1.24 Лоб Аннотация

A Lob аннотация указывает, что a постоянное свойство или поле должно быть сохранялся как большой объект для поддерживаемый базой данных большой тип объекта. Портативные приложения должны использовать Lob аннотации при сопоставлении с база данных Lob. Аннотации Lobможет использоваться в сочетании с Основная аннотация или ElementCollection аннотации, когда значение коллекции элементов тип. A Lob может быть либо двоичным, либо характер type. Тип Lobвытекает из типа постоянное поле или свойство и, кроме строк и символов, по умолчанию используется Blob.

И Hibernate сопоставляет его с SQL BLOB, который обрабатывает PostgreSQL с помощью oid .

Является ли это исправлено в некоторой новой версии спящего режима?

Ну, проблема в том, что я не знаю, в чем проблема. Но я могу хотя бы сказать, что ничего не изменилось с 3.5.0-Beta-2 (где и было изменено) в ветки 3.5.x.

Но мое понимание таких проблем, как HHH-4876, HHH-4617 и PostgreSQL и BLOB (упомянутый в javadoc PostgreSQLDialect) заключается в том, что вы должны установить следующее свойство

hibernate.jdbc.use_streams_for_binary=false

если вы хотите использовать oid i.e. byte[] с @Lob (это мое понимание, так как VARBINARY не то, что вы хотите в Oracle). Вы попробовали это?

В качестве альтернативы HHH-4876 предлагает использовать устаревший PrimitiveByteArrayBlobType, чтобы получить старое поведение (pre Hibernate 3.5).

Ссылки

  • Спецификация JPA 2.0
    • Раздел 2.8 "Отображение значений по умолчанию для полей или свойств без привязки"
    • Раздел 11.1.6 "Основная аннотация"
    • Раздел 11.1.24 "Аннотации Лоб"

Ресурсы

Ответ 2

Вот что говорит O'reilly Enterprise JavaBeans, 3.0:

JDBC имеет специальные типы для этих очень больших объектов. Тип java.sql.Blob представляет двоичные данные, а java.sql.Clob представляет символьные данные.

Здесь находится исходный код PostgreSQLDialect

public PostgreSQLDialect() {
    super();
    ...
    registerColumnType(Types.VARBINARY, "bytea");
    /**
      * Notice it maps java.sql.Types.BLOB as oid
      */
    registerColumnType(Types.BLOB, "oid");
}

Итак, что вы можете сделать

Переопределить PostgreSQLDialect следующим образом

public class CustomPostgreSQLDialect extends PostgreSQLDialect {

    public CustomPostgreSQLDialect() {
        super();

        registerColumnType(Types.BLOB, "bytea");
    }
}

Теперь просто определите свой пользовательский диалект

<property name="hibernate.dialect" value="br.com.ar.dialect.CustomPostgreSQLDialect"/>

И используйте переносимую аннотацию JPA @Lob

@Lob
public byte[] getValueBuffer() {

UPDATE

Здесь был извлечен здесь

У меня есть приложение, работающее в hibernate 3.3.2, и приложения работают нормально, причем все поля blob используют oid (byte [] в java)

...

Миграция в hibernate 3.5 все поля blob больше не работают, и журнал сервера показывает: ERROR org.hibernate.util.JDBCExceptionReporter - ERROR: column имеет тип oid, но выражение имеет тип bytea

, который можно объяснить здесь

Это обобщение не является ошибкой в ​​PG JDBC, , но изменение по умолчанию реализации Hibernate в версии 3.5. В моей ситуации настройка совместимого свойства при подключении не помогли.

...

Гораздо больше того, что я видел в 3.5 - бета-2, и я не знаю, было ли это исправлено - Hibernate - без аннотации @Type - будет автоматически создавать столбец типа oid, но попытается прочитать это как bytea

Интересно, что, когда он сопоставляет типы .BOLB как bytea (см. CustomPostgreSQLDialect), он получает

Не удалось выполнить пакетное обновление JDBC

при вставке или обновлении

Ответ 3

Наконец-то я получил эту работу. Однако он расширяет решение от A. Garcia, поскольку проблема заключается в типе спящего типа MaterializedBlob. Простое отображение Blob > bytea недостаточно, нам нужна замена для MaterializedBlobType, которая работает с поддержкой спящего режима спящего режима. Эта реализация работает только с bytea, но, возможно, парень из проблемы JIRA, который хотел OID, мог бы внести вклад в реализацию OID.

Печально заменить эти типы во время выполнения - это боль, поскольку они должны быть частью диалекта. Если бы только это усиление JIRA попало в 3.6, это было бы возможно.

public class PostgresqlMateralizedBlobType extends AbstractSingleColumnStandardBasicType<byte[]> {
 public static final PostgresqlMateralizedBlobType INSTANCE = new PostgresqlMateralizedBlobType();

 public PostgresqlMateralizedBlobType() {
  super( PostgresqlBlobTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE );
 }

  public String getName() {
   return "materialized_blob";
  }
}

В большинстве случаев это может быть статичным (действительно ли getBinder() действительно нужен новый экземпляр?), но я действительно не понимаю внутренний спящий режим, поэтому в основном это copy + paste + modify.

public class PostgresqlBlobTypeDescriptor extends BlobTypeDescriptor implements SqlTypeDescriptor {
  public static final BlobTypeDescriptor INSTANCE = new PostgresqlBlobTypeDescriptor();

  public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
   return new PostgresqlBlobBinder<X>(javaTypeDescriptor, this);
  }
  public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
   return new BasicExtractor<X>( javaTypeDescriptor, this ) {
    protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException { 
      return (X)rs.getBytes(name);
    }
   };
  }
}

public class PostgresqlBlobBinder<J> implements ValueBinder<J> {
 private final JavaTypeDescriptor<J> javaDescriptor;
 private final SqlTypeDescriptor sqlDescriptor;

 public PostgresqlBlobBinder(JavaTypeDescriptor<J> javaDescriptor, SqlTypeDescriptor sqlDescriptor) { 
  this.javaDescriptor = javaDescriptor; this.sqlDescriptor = sqlDescriptor;
 }  
 ...
 public final void bind(PreparedStatement st, J value, int index, WrapperOptions options) 
 throws SQLException {
  st.setBytes(index, (byte[])value);
 }
}

Ответ 4

Я использую Hibernate 4.2.7.SP1 с Postgres 9.3 и следующие работы для меня:

@Entity
public class ConfigAttribute {
  @Lob
  public byte[] getValueBuffer() {
    return m_valueBuffer;
  }
}

поскольку у Oracle нет проблем с этим, а для Postgres я использую пользовательский диалект:

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);
  }
}

Преимущество этого решения я считаю, что я могу оставить безмозглые баночки.

Дополнительные сведения о проблемах совместимости Postgres/Oracle с Hibernate см. в сообщении .

Ответ 5

Я получил эту работу, переопределив аннотацию с XML файлом для Postgres. Аннотации хранятся для Oracle. На мой взгляд, в этом случае было бы лучше всего переопределить отображение этой проблемы - некоторого enity с xml-сопоставлением. Мы можем переопределить одиночные/множественные объекты с отображением xml. Поэтому мы будем использовать аннотацию для нашей базы данных, поддерживаемой в основном, и XML файл для каждой другой базы данных.

Примечание: нам просто нужно переопределить один класс, поэтому это не имеет большого значения. Подробнее из моего примера Пример переопределения аннотации с XML

Ответ 6

В Postgres @Lob ломается для байта [], поскольку он пытается сохранить его как oid, а для String также возникает одна и та же проблема. Ниже код разбивается на postgres, который отлично работает на оракуле.

@Lob
private String stringField;

и

@Lob
private byte[]   someByteStream;

Чтобы зафиксировать выше на postgres, написанный ниже пользовательский hibernate.dialect

public class PostgreSQLDialectCustom extends PostgreSQL82Dialect{

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

 @Override
 public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
    if (Types.CLOB == sqlTypeDescriptor.getSqlType()) {
      return LongVarcharTypeDescriptor.INSTANCE;
    }
    return super.remapSqlTypeDescriptor(sqlTypeDescriptor);
  }
}

Теперь настройте настраиваемый диалект в спящем режиме

hibernate.dialect=X.Y.Z.PostgreSQLDialectCustom   

X.Y.Z - имя пакета.

Теперь он работает нормально. ПРИМЕЧАНИЕ. - Моя версия спящего режима - 5.2.8.     Версия для Postgres-9.6.3

Ответ 7

i исправлена ​​моя проблема, добавив аннотацию @Lob, которая создаст байт [] в oracle как blob, но эта аннотация приведет к созданию поля в виде oid, которое не работает должным образом. Чтобы сделать байт [] создан как bytea я made клиент Диалект для postgres, как показано ниже

Public class PostgreSQLDialectCustom extends PostgreSQL82Dialect {
    public PostgreSQLDialectCustom() {
        System.out.println("Init PostgreSQLDialectCustom");
        registerColumnType( Types.BLOB, "bytea" );

      }

    @Override
    public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
    if (sqlTypeDescriptor.getSqlType() == java.sql.Types.BLOB) {
      return BinaryTypeDescriptor.INSTANCE;
    }
    return super.remapSqlTypeDescriptor(sqlTypeDescriptor);
  }
 }

Также необходимо переопределить параметр для Dialect

spring.jpa.properties.hibernate.dialect = com.ntg.common.DBCompatibilityHelper.PostgreSQLDialectCustom

больше намека можно найти: https://dzone.com/articles/postgres-and-oracle