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プロパティを持つ)を持つ方法を探しています。
更新:読んだ後 このブログ 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を適切に実行するのを待つべきだと思います。」
参照:
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[]
、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-4876 、 HHH-4617 、および PostgreSQLとBLOB (PostgreSQLDialect
のjavadocで言及)などの問題に対する私の理解は、次のプロパティを設定することになっています
hibernate.jdbc.use_streams_for_binary=false
oid
を使用する場合、つまりbyte[]
を@Lob
と一緒に使用する場合(VARBINARY
はOracleで必要なものではないため、これは私の理解です)。これを試しましたか?
別の方法として、 HHH-4876 は、非推奨のPrimitiveByteArrayBlobType
を使用して古い動作を取得することを提案します(Hibernate 3.5より前)。
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バッチ更新を実行できませんでした
挿入または更新するとき
私はついにこれが機能するようになりました。 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);
}
}
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の互換性の問題については、私の ブログ投稿 を参照してください。
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
PostgresのXMLファイルでアノテーションをオーバーライドすることで機能しました。 Oracleの注釈は保持されます。私の意見では、この場合、この厄介なエンティティのマッピングをxmlマッピングでオーバーライドするのが最善でしょう。 XMLマッピングで単一/複数のエンティティをオーバーライドできます。そのため、主にサポートされているデータベースにはアノテーションを使用し、他のデータベースにはxmlファイルを使用します。
注:1つのクラスをオーバーライドする必要があるだけなので、大したことではありません。私の例からもっと読む XMLで注釈をオーバーライドする例
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
正しい方向に導いてくれたパスカルのジャスティンに感謝します。 Hibernate 3.5.3でも同じ問題に直面していました。あなたの調査と適切なクラスへのポインタは、問題を特定して修正するのに役立ちました。
まだHibernate 3.5でスタックしていて、oid + byte [] + @LoBの組み合わせを使用しているユーザーのために、以下は問題を修正するために行ったことです。
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));
}
}
CustomBlobTypeをHibernateに登録します。以下は私がそれを達成するためにしたことです。
hibernateConfiguration= new AnnotationConfiguration();
Mappings mappings = hibernateConfiguration.createMappings();
mappings.addTypeDef("materialized_blob", "x.y.z.BlobType", null);