web-dev-qa-db-ja.com

byte []の適切な休止状態アノテーション

Hibernate 3.1およびJPAアノテーションを使用するアプリケーションがあります。 byte []属性(サイズが1k〜200k)のオブジェクトがいくつかあります。 JPA @Lobアノテーションを使用し、Hibernate 3.1はすべての主要なデータベースでこれらを正常に読み取ることができます。JDBCBlobベンダーの特性を隠しているようです(そうするべきです)。

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

Hibernate 3.5 壊れない(そして修正されない) postgresqlでのこの注釈の組み合わせ(回避策なし)を発見したときに、3.5にアップグレードする必要がありました。今のところ明確な修正は見つかりませんでしたが、@ 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 []プロパティに注釈を付けるポータブルな方法は何ですか?
  • これは、Hibernateの最近のバージョンで修正されていますか?

更新:読んだ後 このブログ JIRAの問題の元の回避策が何であるかがついにわかりました: @Lobを削除し、プロパティに次のように注釈を付けるには:

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

ただし、これは機能しませんfor me-私はまだbyteaではなくOIDを取得しています。ただし、Oirを必要とするJIRAの問題の作成者には有効でした。

A.ガルシアからの回答の後、このコンボを試しました。これは実際にはpostgresqlで機能しますが、Oracleでは機能しません。

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

本当に必要なのは、どの@ org.hibernate.annotations.Typeの組み合わせ(@Lob + byte []がマップされるか)を(postgresqlで)制御することです。


以下は、MaterializedBlobType(sqlタイプBlob)の3.5.5.Finalのスニペットです。 Steveのブログによると、postgresqlは、byteaにStreams(理由は聞かないでください)と、oidにはpostgresqlのカスタムBlob型を使用することを望んでいます。また、JDBCでsetBytes()を使用することはbyteaにも使用されることに注意してください(過去の経験から)。そのため、use-streamsが両方とも「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

Update次の論理的な質問は、「テーブル定義を手動でbyteaに変更するだけではない」と(@Lob + byte [])を保持することです。このは動作しますがヌルバイト[]を保存しようとするまで動作します。 postgreSQLドライバーがOID型式であり、列型がbyteaであると考えるのは、PGドライバーが予期するJDBC.setBytes(null)の代わりにhibernateがJDBC.setNull()を(正しく)呼び出すためです。

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

現在、休止状態の型システムは「進行中の作業」です(3.5.5非推奨コメントによる)。実際、3.5.5コードの多くは廃止されているため、PostgreSQLDialectをサブクラス化するときに何を見るべきかを知ることは困難です。

AFAKT、postgresqlのTypes.BLOB/'oid'は、OIDスタイルのJDBCアクセスを使用するカスタムタイプにマッピングする必要があります(つまり、PostgresqlBlobTypeオブジェクトとNOT MaterializedBlobType)。私は実際にpostgresqlでBlobsをうまく使用したことはありませんが、byteaは単に1つとして機能することを知っています/私は期待しています.

現在、BatchUpdateExceptionを確認しています。ドライバーがバッチ処理をサポートしていない可能性があります。


2004年からの素晴らしい引用:「私のとりとめをまとめると、Hibernateを変更する前にJDBCドライバーがLOBを適切に実行するのを待つべきだと思います。」

参照:

107
Justin

Byte []プロパティに注釈を付けるポータブルな方法は何ですか?

それはあなたが望むものに依存します。 JPAは、注釈のないbyte[]を永続化できます。 JPA 2.0仕様から:

11.1.6基本的な注釈

Basic注釈は、データベース列へのマッピングの最も単純なタイプです。 Basic注釈は、次のタイプの永続プロパティまたはインスタンス変数に適用できます。Javaプリミティブ、タイプ、プリミティブタイプのラッパー、Java.lang.StringJava.math.BigIntegerJava.math.BigDecimalJava.util.DateJava.util.CalendarJava.sql.DateJava.sql.TimeJava.sql.Timestampbyte[]Byte[]char[]Character[]、enum、およびSerializableを実装するその他の型。セクション2.8で説明したように、Basicアノテーションの使用は、これらのタイプの永続フィールドおよびプロパティではオプションです。このようなフィールドまたはプロパティに基本注釈が指定されていない場合、基本注釈のデフォルト値が適用されます。

また、Hibernateはitを「デフォルトで」PostgreSQLがVARBINARYで処理するSQL LONGVARBINARY(またはColumnサイズに応じてSQL bytea?)にマッピングします。

ただし、byte[]をラージオブジェクトに格納する場合は、@Lobを使用する必要があります。仕様から:

11.1.24ロブ注釈

Lob注釈は、永続プロパティまたはフィールドを、データベースでサポートされるラージオブジェクトタイプのラージオブジェクトとして永続化することを指定します。ポータブルアプリケーションは、データベースLob型へのマッピング時にLob注釈を使用する必要があります。 Lob注釈は、要素コレクションの値が基本型である場合、Basic注釈またはElementCollection注釈と共に使用できます。 Lobは、バイナリタイプまたは文字タイプのいずれかです。 Lob型は、永続フィールドまたはプロパティの型から推測され、文字列および文字型を除き、デフォルトはBlobです。

また、Hibernateは、PostgreSQLがBLOBで処理するSQL oidにマップします。

これは、Hibernateの最近のバージョンで修正されていますか?

問題は、問題が正確に何なのかわからないということです。しかし、少なくとも、3.5.xブランチの3.5.0-Beta-2(変更が導入された場所)から何も変わっていないと言うことができます。

しかし、 HHH-4876HHH-4617 、および PostgreSQLとBLOBPostgreSQLDialectのjavadocで言及)などの問題に対する私の理解は、次のプロパティを設定することになっています

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項「ロブ注釈」

資源

63
Pascal Thivent

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

ここが抽出されました here

hibernate 3.3.2で実行中のアプリケーションがあり、アプリケーションは正常に動作します。すべてのblobフィールドはoid(Javaのbyte [])を使用します

...

hibernate 3.5に移行すると、すべてのblobフィールドが機能しなくなり、サーバーログに次のエラーが表示されます。バイテア

説明できるここ

この一般的なはPG JDBCのバグではありませんが、3.5バージョンのHibernateのデフォルト実装の変更です。私の状況では接続で互換性のあるプロパティを設定しても助けにはなりませんでした

...

さらに多くのこれは私が3.5で見たもの-ベータ2、これ​​が修正されたかどうかはわかりません-@Typeアノテーションなし-タイプoidの列を自動作成しますが、試してみますこれをbyteaと読む

興味深いのは、Types.BOLBをbyteaとしてマップすると(CustomPostgreSQLDialectを参照)

JDBCバッチ更新を実行できませんでした

挿入または更新するとき

9
Arthur Ronald

私はついにこれが機能するようになりました。 A. Garciaのソリューションを拡張しますが、問題はBlob> byteaのマッピングだけでは十分ではないhibernateタイプのMaterializedBlobタイプにあるため、休止状態のBLOBサポートで動作するMaterializedBlobTypeの置換が必要です。この実装はbyteaでのみ動作しますが、おそらくOIDを望んでいたJIRAの問題の男がOIDの実装に貢献する可能性があります。

悲しいことに、これらの型は方言の一部であるはずなので、実行時にこれらの型を置き換えるのは苦痛です。 this JIRA enhanement のみが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

Postgres 9.3でHibernate 4.2.7.SP1を使用していますが、次のように動作します。

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

私が考えるこのソリューションの利点は、休止状態のjarファイルに手を付けないことです。

HibernateとのPostgres/Oracleの互換性の問題については、私の ブログ投稿 を参照してください。

5
Peter Butkovic

oracleでbyte []をblobとして作成する@Lobの注釈を追加することで問題を修正しましたが、この注釈は正しく動作しないoidとしてフィールドを作成します。以下の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

2

PostgresのXMLファイルでアノテーションをオーバーライドすることで機能しました。 Oracleの注釈は保持されます。私の意見では、この場合、この厄介なエンティティのマッピングをxmlマッピングでオーバーライドするのが最善でしょう。 XMLマッピングで単一/複数のエンティティをオーバーライドできます。そのため、主にサポートされているデータベースにはアノテーションを使用し、他のデータベースにはxmlファイルを使用します。

注:1つのクラスをオーバーライドする必要があるだけなので、大したことではありません。私の例からもっと読む XMLで注釈をオーバーライドする例

0
Vinh Vo

Postgresでは、@ Lobはbyte []をoidとして保存しようとするため壊れており、Stringでも同じ問題が発生します。以下のコードは、Oracleで正常に動作している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でカスタム方言を構成します

hibernate.dialect=X.Y.Z.PostgreSQLDialectCustom   

X.Y.Zはパッケージ名です。

今では正常に動作しています。注-My Hibernateバージョン-5.2.8.Final Postgresバージョン-9.6.3

0
gajendra kumar

正しい方向に導いてくれたパスカルのジャスティンに感謝します。 Hibernate 3.5.3でも同じ問題に直面していました。あなたの調査と適切なクラスへのポインタは、問題を特定して修正するのに役立ちました。

まだHibernate 3.5でスタックしていて、oid + byte [] + @LoBの組み合わせを使用しているユーザーのために、以下は問題を修正するために行ったことです。

  1. MaterializedBlobTypeを拡張し、oidスタイルのアクセス権を持つsetおよびgetメソッドをオーバーライドするカスタムBlobTypeを作成しました。

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