Правильная аннотация для спящего режима для байта []
У меня есть приложение, использующее аннотации 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