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


120

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

@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() {...

しかし、これは私にとっては機能しません -私はまだbyteaの代わりにOIDを取得しています。ただし、oidを必要としているように思われたJIRAの問題の作成者には役立ちました。

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

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

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


以下は、MaterializedBlobType(SQLタイプBlob)の3.5.5.Finalのスニペットです。Steveのブログによると、postgresqlではbytea(理由は聞かないでください)にはStreamsを、oidsにはpostgresqlのカスタムBlobタイプを使用することを求めています。また、JDBCでのsetBytes()の使用はbytea(過去の経験から)にも使用できることに注意してください。したがって、これは、use-streamが両方とも「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であると考えるのは、PGドライバーが予期するJDBC.setBytes(null)の代わりにhibernateが(正しく)JDBC.setNull()を呼び出すためです。

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

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

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

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


2004年の素晴らしい引用:「とりとめのない話をまとめると、Hibernateを変更する前に、JDBCドライバーがLOBを適切に実行するのを待つ必要があると私は言います。」

参照:


これは3.6で修正されたようですが、3.5.6については不明です。MaterializedBlobTypeクラスは3.5.5> 3.6から完全に書き直されました。OIDタイプは、実装を変更したため機能するようになりました。
Justin

いいね!もしあれば、Jiraの問題がこの書き換えを追跡しているのではないかと思います(おそらく、書き換えはより深い変更の結果です)。可能であれば、3.5での変更をバックポートするとよいでしょう。それが不可能な場合の悪いニュース。
Pascal Thivent、2010

実際、私のテストで初めて誤検知が発生しました(待っていたはずですが!)。それでもまだ修正されていません。バグはBlobTypeDescriptorに移動しました。
Justin

ありがとう。@Type(type = "org.hibernate.type.BinaryType")は、PDFファイルを格納するテーブルで私に役立ちました。インテリジェントコンバーターからOracle-To-PostgreSQLを使用してデータベースをOracleからPostgresに移行し、BLOBからBYTEAに自動的に変換および挿入しましたが、BlobTypeは機能しませんでした。
jmoran

回答:


68

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[]、列挙、および実装する任意の他のタイプSerializable。セクション2.8で説明されているように、Basicアノテーションの使用は、これらのタイプの永続フィールドおよびプロパティではオプションです。そのようなフィールドまたはプロパティにBasicアノテーションが指定されていない場合、Basicアノテーションのデフォルト値が適用されます。

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

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

11.1.24ロブ注釈

Lob永続プロパティまたはフィールドがデータベース・サポートラージ・オブジェクト・タイプに大きいオブジェクトとして永続化されなければならない注釈を指定します。ポータブルアプリケーションではLob、データベースLobタイプにマッピングするときにアノテーションを使用する必要 があります。Lob注釈は、基本的な注釈付きかと組み合わせて使用することができる ElementCollection要素のコレクション値が基本型である注釈。A Lobは、バイナリ型または文字型のいずれかです。Lobタイプは、文字列と文字の種類、ブロブデフォルト以外の、永続フィールドまたはプロパティのタイプから推測とされています。

そしてHibernateはそれをBLOBPostgreSQLがで処理するSQLにマップしますoid

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

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

しかし、HHH-4876HHH-4617のような問題、およびPostgreSQLとBLOB(のjavadocに記載 PostgreSQLDialect)についての私の理解は、次のプロパティを設定することになっているということです。

hibernate.jdbc.use_streams_for_binary=false

oidie byte[]と一緒に使用したい場合@Lob(これは私の理解VARBINARYですが、Oracleで使用するものではありません)やってみましたか?

別の方法として、HHH-4876は非推奨のものPrimitiveByteArrayBlobTypeを使用して古い動作(Hibernate 3.5より前)を取得することを推奨しています。

参考文献

  • JPA 2.0仕様
    • 2.8項「非関係フィールドまたはプロパティのデフォルトのマッピング」
    • 11.1.6項「基本的な注釈」
    • 11.1.24項「ロブ注釈」

資源


OMG、答え始めてからこの質問は大きく変わったと思います。後ですべての変更を読み取り、必要に応じて変更を消化した後、私の回答を更新します。
Pascal Thivent、2010

仕様を確認するのは良いので、hibernateは(@Lob + byte [])をラージオブジェクトがサポートする型にマップするのが完全に正しいです。Postgresqlには2(byteaまたはoid)があります。ただし、Hibernate 3.5はoid(デフォルト)にマップしますが、PGSQLドライバーがデータの代わりに6バイトのoidを返すJDBC getBytes()を使用して読み取ります。また、質問が出されて以来、ブログの作成者が(彼のブログで)最も役に立ったと回答しています。
ジャスティン

@Justin ただし、Hibernate 3.5はoid(デフォルト)にマップされますが、 PGSQL ドライバーがデータの代わりに6バイトのoidを返すJDBC getBytes()を使用して読み取ります。これは、使用時にhibernate.jdbc.use_streams_for_binary=falseも発生しますか?(スティーブが今言ったことをチェックするつもりです)。
Pascal Thivent、2010

プロパティファイルで指定しようとしていますが、PostgreSQLDialectはuseInputStreamToInsertBlob()がfalseを返すため、このプロパティを明示的に設定していないので、私はそうだと思います。
ジャスティン

このプロパティを(trueまたはfalseに)設定すると、実行時例外が発生します。 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() {

更新

ここはここで抽出されました

私はhibernate 3.3.2でアプリケーションを実行していますが、アプリケーションはOID(Javaのbyte [])を使用してすべてのBLOBフィールドで正常動作します

...

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

ここで説明できます

これはgeneraly PG JDBCのバグではありませんしかし、3.5バージョンで休止状態の既定の実装の変更。私の状況では、接続に互換性のあるプロパティを設定しても役に立たなかった

...

さらにはるかに私が3.5で見たもの-ベータ2、これ​​が修正されたかどうかはわかりません-Hibernate-@Type注釈なし- タイプoidの列を自動作成しますが、これをbyteaとして読み込もうとします

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

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

挿入または更新時


この解決策は見栄えがよく、私は今それを試しています。
Justin

これは正しいDDLを生成しますが、実行時に失敗します。blobプロパティを持つオブジェクトにしようとすると、java.sql.BatchUpdateExceptionが発生します。
ジャスティン

@Justin PostgreSQLの代わりにOracleを使用して同様のシナリオを試して、得られる結果を確認してください。BatchUpdateExceptionは、バッチ更新操作中に発生するエラーに関係しています。
Arthur Ronald

実際に私が本当に望んでいるのは、BLOBを「bytea」にマップするのではなく、(byte [] + @Lob)アノテーションの組み合わせをTypes.VARBINARYにマップすることです。
ジャスティン


7

私は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互換性の問題の詳細については、私のブログ投稿を参照してください。


2
Hibernate 4.3.6とPostgresql 9.3を使用し、Postgresql9Dialectを拡張して動作しました。ありがとうございました!
アンドレス・オビエド

Hibernate 5.3.7.FinalおよびPostgres95Dialectで動作します。Thx
Bernhard Kern、

6

これでようやく動作しました。A. Garciaのソリューションを拡張したものですが、問題は休止型のMaterializedBlob型にあるため、Blob> byteaをマッピングするだけでは不十分であり、休止状態の壊れたBlobサポートで動作するMaterializedBlobTypeの代替が必要です。この実装はbyteaでのみ機能しますが、OIDを望んでいたJIRAの問題の人が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投票が計算されなくなります
Arthur Ronald

テスト環境で何かをするのを忘れていたので、私はライブで学習したと思います。
Justin

ここでは同じ、研究のための+1 あなたの状況のためのソリューション。
Pascal Thivent、2010

4.2.x Hibernateバージョンでの解決策はありますか?Hibernateの内部構造が少し変更されました(コメントされた問題にコメントしました:hibernate.atlassian.net/browse/HHH-5584)。
Peter Butkovic

2

oracleでblobとしてbyte []を作成する@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


0

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

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


0

Postgresでは@Lobはoidとして保存しようとするため、byte []で破損し、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.dialect=X.Y.Z.PostgreSQLDialectCustom   

XYZはパッケージ名です。

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


0

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

まだHibernate 3.5で立ち往生していて、oid + byte [] + @LoBの組み合わせを使用している人のために、以下は問題を修正するために私が行ったことです。

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

    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);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.