правильная спящая аннотация для байта [] - PullRequest
105 голосов
/ 09 сентября 2010

У меня есть приложение, использующее аннотации hibernate 3.1 и JPA.Он имеет несколько объектов с атрибутами byte [] (размером от 1 до 200 КБ).Он использует аннотацию 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, он использует тип bytea postgresql (который работает, но только на 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 []?
  • Исправлено ли это в какой-то недавней версии hibernate?

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

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

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

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

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

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


Вот фрагмент из 3.5.5. Финал из MaterializedBlobType (тип Blob sql).Согласно блогу Стива, postgresql хочет, чтобы вы использовали Streams для bytea (не спрашивайте меня, почему) и пользовательский тип Blob для og.Также обратите внимание, что использование setBytes () в JDBC также для bytea (из прошлого опыта).Таким образом, это объясняет, почему потоки использования не оказывают никакого влияния, они оба принимают '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 [])?Это работает , ДО вы пытаетесь сохранить нулевой байт [].Что драйвер postgreSQL считает выражением типа OID, а тип столбца - bytea - это потому, что hibernate (справедливо) вызывает JDBC.setNull () вместо JDBC.setBytes (null), который ожидает драйвер PG.

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

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

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

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


Великолепная цитата 2004 года: «Подводя итог моим разговорам, я бы сказал, что нам следует подождать, пока драйвер JDBC правильно выполнит большие объекты, прежде чем менять Hibernate».

Ссылки:

Ответы [ 8 ]

61 голосов
/ 18 сентября 2010

Что такое переносимый способ аннотировать свойство 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 аннотация указывает, что постоянное свойство или поле должно быть сохранился как большой объект для поддерживаемый базой данных тип большого объекта. Портативные приложения должны использовать Lob аннотация при сопоставлении с база данных Lob тип. Lob аннотация может использоваться вместе с Основная аннотация или с ElementCollection аннотация, когда значение коллекции элементов имеет основное значение тип. A Lob может быть двоичным или тип персонажа. Тип Lob вытекает из типа постоянное поле или свойство и, кроме строковых и символьных типов, по умолчанию Blob.

И Hibernate сопоставит его с SQL BLOB, который PostgreSQL обрабатывает с oid .

Исправлено ли это в какой-то недавней версии hibernate?

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

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

hibernate.jdbc.use_streams_for_binary=false

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

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

Ссылки

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

Ресурсы

9 голосов
/ 17 сентября 2010

Вот что говорит 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() {

ОБНОВЛЕНИЕ

Здесь извлечено здесь

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

...

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

, что можно объяснить здесь

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

...

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

Интересно, потому что, когда он отображает Types.BOLB как bytea (см. CustomPostgreSQLDialect), он получает

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

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

6 голосов
/ 17 сентября 2010

У меня наконец-то все заработало. Он расширяет решение от A. Garcia, однако, поскольку проблема заключается в спящем типе MaterializedBlob, просто отображение Blob> bytea недостаточно, нам нужна замена для MaterializedBlobType, который работает с поддержкой разбитых BLOB-объектов спящего режима. Эта реализация работает только с 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 () действительно нужен новый экземпляр?), Но я не совсем понимаю внутренний hibernate, так что это в основном копирование + вставка + изменение.

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 голосов
/ 05 марта 2014

Я использую 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 см. В моем блоге .

1 голос
/ 09 ноября 2017

я исправил мою проблему, добавив аннотацию @Lob, которая создаст байт [] в oracle как blob, но эта аннотация создаст поле как oid, которое не работает должным образом, чтобы сделать byte [] созданным как bytea, который я сделалдиалект клиента для 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);
  }
 }

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

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

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

0 голосов
/ 29 марта 2018

Спасибо, Джастин, Паскаль, за то, что направил меня в правильном направлении.Я также столкнулся с той же проблемой с Hibernate 3.5.3.Ваши исследования и указатели на правильные классы помогли мне определить проблему и исправить ее.

Для тех, кто все еще застрял в Hibernate 3.5 и использует комбинацию oid + byte [] + @LoB, следуя инструкциямэто то, что я сделал, чтобы решить эту проблему.

  1. Я создал собственный BlobType, расширяющий MaterializedBlobType и переопределяющий набор и методы get с доступом в стиле oid.

    public class CustomBlobType extends MaterializedBlobType {
    
    private static final String POSTGRESQL_DIALECT = PostgreSQLDialect.class.getName();
    
    /**
     * Currently set dialect.
     */
    private String dialect = hibernateConfiguration.getProperty(Environment.DIALECT);
    
    /*
     * (non-Javadoc)
     * @see org.hibernate.type.AbstractBynaryType#set(java.sql.PreparedStatement, java.lang.Object, int)
     */
    @Override
    public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
        byte[] internalValue = toInternalFormat(value);
    
        if (POSTGRESQL_DIALECT.equals(dialect)) {
            try {
    
    //I had access to sessionFactory through a custom sessionFactory wrapper.
    st.setBlob(index, Hibernate.createBlob(internalValue, sessionFactory.getCurrentSession()));
                } catch (SystemException e) {
                    throw new HibernateException(e);
                }
            } else {
                st.setBytes(index, internalValue);
            }
        }
    
    /*
     * (non-Javadoc)
     * @see org.hibernate.type.AbstractBynaryType#get(java.sql.ResultSet, java.lang.String)
     */
    @Override
    public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
        Blob blob = rs.getBlob(name);
        if (rs.wasNull()) {
            return null;
        }
        int length = (int) blob.length();
        return toExternalFormat(blob.getBytes(1, length));
      }
    }
    
  2. Зарегистрируйте CustomBlobType в Hibernate.Вот что я сделал, чтобы добиться этого.

    hibernateConfiguration= new AnnotationConfiguration();
    Mappings mappings = hibernateConfiguration.createMappings();
    mappings.addTypeDef("materialized_blob", "x.y.z.BlobType", null);
    
0 голосов
/ 13 июля 2017

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

@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 - имя пакета.

Теперь все работает нормально. ПРИМЕЧАНИЕ - Моя версия Hibernate - 5.2.8. Окончательный Версия Postgres - 9.6.3

0 голосов
/ 21 мая 2013

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...