it-roy-ru.com

правильная спящая аннотация для байта []

У меня есть приложение, использующее аннотации 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.

После ответа А. Гарсии я попробовал эту комбинацию, которая действительно работает на 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 для postgresql. Также обратите внимание, что использование 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). На самом деле я никогда не использовал успешно Blobs с postgresql, но я знаю, что bytea просто работает так, как я ожидал.

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


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

Ссылки:

105
Justin

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

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

9
Arthur Ronald

Я наконец получил это работает. Он расширяет решение от 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 () действительно нужен новый экземпляр?), Но я не совсем понимаю внутреннее состояние гибернации, так что это в основном копирование + вставка + изменение.

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

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

4
Peter Butkovic

я исправил проблему, добавив аннотацию @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

1
El Ghandor Yasser

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

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

0
Vinh Vo

На Postgres @Lob разрывается для byte [], поскольку он пытается сохранить его как 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
gajendra kumar

Спасибо Джастину, Паскалю за то, что он направил меня в правильном направлении. Я также столкнулся с той же проблемой с 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));
      }
    }
    
    1. Зарегистрируйте CustomBlobType в Hibernate. Вот что я сделал, чтобы добиться этого.

      hibernateConfiguration= new AnnotationConfiguration();
      Mappings mappings = hibernateConfiguration.createMappings();
      mappings.addTypeDef("materialized_blob", "x.y.z.BlobType", null);
      
0
Nibin Jacob Panicker