належна анотація до сплячки для байтів []


120

У мене є додаток, який використовує анотації зі сплячого режиму 3.1 та JPA. У нього є кілька об’єктів з атрибутами byte [] (розміром 1 к - 200 к). Він використовує анотацію JPA @Lob, і в сплячому режимі 3.1 можна прочитати ці відмінно на всіх основних базах даних - це, схоже, приховує особливості постачальника JDBC Blob (як це слід робити).

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

Нам довелося перейти до 3,5, коли ми виявили, що сплячий режим 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), який переноситься через основні бази даних.

  • Який портативний спосіб анотувати властивість байт []?
  • Це зафіксовано в останній версії сплячки?

Оновлення: Прочитавши цей блог, я нарешті з’ясував, що було оригінальним вирішенням проблеми JIRA: Мабуть, ви повинні скинути @Lob та анотувати властивість як:

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

Однак це не працює для мене - я все одно отримую OID замість bytea; Однак це все-таки спрацювало з автором випуску JIRA, який, схоже, хотів висловитись.

Після відповіді А. Гарсіа я спробував це комбо, яке насправді працює на postgresql, але не на oracle.

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

Що мені дійсно потрібно зробити, це контролювати, який @ org.hibernate.annotations.Введіть комбінацію (@Lob + байт [] отримує відображення) на (на postgresql).


Ось фрагмент від 3.5.5. Фінал від MaterializedBlobType (sql типу Blob). Згідно з блогом Стіва, postgresql хоче, щоб ви використовували потоки для bytea (не запитуйте мене чому) і спеціальний тип Blob для oid для postgresql. Зауважте також, що використання setBytes () на JDBC також призначене для bytea (з минулого досвіду). Таким чином, це пояснює, чому use-потоки не впливають, вони обидва вважають "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 + байт [])? Це робить роботу, ПОКИ ви намагаєтеся зберегти нульові байти []. На думку драйвера postgreSQL, це вираз типу OID, а тип стовпця - bytea - це тому, що сплячий (справедливо) викликає 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 зробить LOBs належним чином перед тим, як змінити сплячий режим".

Список літератури:


Це, здається, було зафіксовано в 3.6, не впевнене в 3.5.6; клас MaterializedBlobType був повністю переписаний з 3.5.5> 3.6. Тепер тип OID працює, оскільки вони змінили реалізацію.
Джастін

Приємно! Цікаво, що питання Джири відстежує цю перезапис, якщо вона є (можливо, переписування є наслідком більш глибокої зміни). Було б непогано підтримати зміни в 3,5, якщо це можливо. Погані новини, якщо це неможливо.
Паскаль Thivent

Насправді мій тест дав мені хибний позитив вперше (знав, що я повинен був почекати!) - його все ще не виправлено, помилка просто перейшла на BlobTypeDescriptor.
Джастін

Дякую. @Type (type = "org.hibernate.type.BinaryType") працював для мене в таблиці, в якій зберігаються файли PDF. Я перемістив базу даних з Oracle на Postgres за допомогою Oracle-To-PostgreSQL від Intelligent Converters, і вона автоматично перетворювалася та вставлялася з BLOB в BYTEA, але BlobType не працював для мене.
jmoran

Відповіді:


68

Який портативний спосіб анотувати властивість байт []?

Це залежить від того, що ви хочете. 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анотаціями , коли значення елемента колекції має базовий типу. А Lobможе бути двійковим або символьним типом. 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тобто byte[]з @Lob(що моє розуміння, оскільки VARBINARYце не те, що ви хочете з Oracle). Ви пробували це?

В якості альтернативи, HHH-4876 пропонує використовувати застаріле, PrimitiveByteArrayBlobTypeщоб отримати стару поведінку (до сплячого режиму 3.5).

Список літератури

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

Ресурси


OMG, я розумію, що це питання сильно змінилося з моменту початку відповідей. Буде прочитано всі зміни пізніше та оновить мої answes після перетравлення змін, якщо потрібно.
Паскаль Thivent

Добре бачити специфікацію, тому в сплячому режимі цілком коректно відображати (@Lob + байт []) на великий тип об'єкта, що підтримується. У Postgresql є 2 (bytea або oid). Однак, хоча сплячий режим 3.5 відображає на oid (за замовчуванням), він читає за допомогою JDBC getBytes (), який драйвер PGSQL повертає 6-байтовий оїд замість даних. Зауважимо також, що автор блогу відповів найбільш корисно (у своєму блозі) з моменту виникнення питання.
Джастін

@Justin Однак, хоч сплячий 3.5 мапи на oid (за замовчуванням) він читає за допомогою JDBC getBytes (), який драйвер PGSQL повертає 6-байтовий oid замість даних - це трапляється і при використанні hibernate.jdbc.use_streams_for_binary=false? (збирається перевірити, що Стів сказав зараз).
Паскаль Thivent

Я спробую вказати його у файлі властивостей, однак PostgreSQLDialect має useInputStreamToInsertBlob (), що повертає помилкове значення, тому я припускаю, що я є, оскільки я не встановлюю явно це властивість.
Джастін

Після встановлення цього властивості (або істинного, або хибного) я отримую виняток з виконання: ПОМИЛКА: стовпець "підпис" має тип bytea, але вираз має тип oid ". Я повинен зазначити, що я використовую сплячий 3.5.5. Фінал + PG 8.2 водіїв.
Justin

10

Ось що йдеться про те, що говорить 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() {

ОНОВЛЕННЯ

Тут було видобуто тут

У мене програма запущена в сплячому режимі 3.3.2, і програми відмінно працюють , в усіх полях блобування використовується oid (байт [] в java)

...

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

що можна пояснити тут

Це, як правило , не є помилкою в PG JDBC , а зміна впровадження Hibernate за замовчуванням у версії 3.5 . У моїй ситуації встановлення сумісної власності підключення не допомогло .

...

Набагато більше це те, що я бачив у 3.5 - бета-2, і я не знаю, чи це було виправлено - це сплячий режим - без анотації @Type - автоматично створить стовпець типу oid, але спробую прочитати це як bytea

Цікавим є те, що коли він відображає Types.BOLB як bytea (див. CustomPostgreSQLDialect), він отримує

Не вдалося виконати пакетне оновлення JDBC

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


Це рішення виглядає славно, я зараз його пробую.
Джастін

Це генерує правильний DDL, але не працює під час виконання: я отримую java.sql.BatchUpdateException при спробі об'єкта з властивістю blob.
Джастін

@Justin Спробуйте подібний сценарій, використовуючи Oracle замість PostgreSQL і подивіться, що ви отримаєте. BatchUpdateException пов'язаний з помилками, що виникають під час операції пакетного оновлення.
Артур Рональд

Насправді те, що я дуже хочу, - це не відображати BLOB на "bytea", а натомість поєднати (байт [] + @Lob) поєднання анотацій на Types.VARBINARY!
Джастін


7

Я використовую 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 див. У моєму блозі .


2
Для мене працювали Hibernate 4.3.6 та Postgresql 9.3 з розширенням Postgresql9Dialect. Дякую!
Андрес Ов'єдо

Працює зі сплячим режимом 5.3.7. Фінальний та Постгрес95Діалект. Thx
Бернхард Керн

6

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

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

+1 для ваших досліджень. Вітаю. Лише порада: волійте редагувати власне запитання / відповідь до 8 разів. В іншому випадку ваше питання / відповідь стане вікі спільноти, і ви не отримаєте репутації, і голосування за UP вже не буде обчислюватися
Артур Рональд

Я живу та навчаюсь, мабуть, у мене було стільки редагувань, що я забував робити те чи інше у своєму тестовому середовищі.
Джастін

Те ж саме тут, +1 для дослідження і в розчині для вашої ситуації.
Паскаль Thivent

будь-який шанс на рішення з 4.2.x сплячою версією? Внутрішня сплячка змінилася трохи (я коментував випущене питання: hibernate.atlassian.net/browse/HHH-5584 ).
Петро Буткович

2

Я виправив свою проблему, додавши анотацію @Lob, яка створить байт [] в oracle як blob, але ця анотація створить поле як oid, яке не працює належним чином, щоб зробити байт [], створений як 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

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

Примітка: нам просто потрібно перекрити один клас, тому це не є великою справою. Читайте більше з мого прикладу Приклад, щоб замінити анотацію з XML


0

У 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   

XYZ - назва пакету.

Зараз це працює добре. ПРИМІТКА - Моя спляча версія - 5.2.8. Фінальна версія Postgres - 9.6.3


0

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

На користь для тих, хто досі перебуває в режимі Hibernate 3.5 та використовує oid + байт [] + @LoB комбінація, наступне - що я зробив, щоб виправити проблему.

  1. Я створив користувацький BlobType, що розширює MaterializedBlobType і переосмислює набір та методи отримання з доступом до стилю 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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.