it-swarm.com.de

angemessene Annotation im Ruhezustand für Byte []

Ich habe eine Anwendung, die Anmerkungen zu Hibernate 3.1 und JPA verwendet. Es hat einige Objekte mit Attributen byte [] (1k - 200k groß). Es verwendet die JPA @ Lob-Annotation, und Hibernate 3.1 kann diese problemlos in allen wichtigen Datenbanken lesen - es scheint die Besonderheiten des JDBC-Blob-Anbieters zu verbergen (wie es sollte).

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

Wir mussten auf 3.5 upgraden, als wir entdeckten, dass der Ruhezustand 3.5 bricht (und nicht repariert) diese Annotationskombination in postgresql (ohne Problemumgehung). Ich habe noch keine eindeutige Lösung gefunden, aber ich habe festgestellt, dass beim Entfernen von @Lob der postgresql-Typ bytea verwendet wird (was funktioniert, aber nur für 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.

Ich suche nach einer Möglichkeit, eine einzelne mit Anmerkungen versehene Klasse (mit einer Blob-Eigenschaft) zu haben, die für alle wichtigen Datenbanken portierbar ist.

  • Wie kann eine byte [] -Eigenschaft portabel mit Anmerkungen versehen werden?
  • Ist dies in einer neueren Version von Hibernate behoben?

Update: Nach dem Lesen von diesem Blog habe ich endlich herausgefunden, wie die ursprüngliche Problemumgehung in der JIRA-Ausgabe lautete: Anscheinend sollten Sie @Lob löschen und die Eigenschaft mit folgenden Anmerkungen versehen:

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

Dies funktioniert jedoch nicht für mich - Ich erhalte weiterhin OIDs anstelle von bytea. Es funktionierte jedoch für den Autor der JIRA-Ausgabe, der offenbar nichts wissen wollte.

Nach der Antwort von A. Garcia habe ich dann diese Combo ausprobiert, die tatsächlich auf postgresql funktioniert, aber nicht auf Oracle.

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

Was ich wirklich tun muss, ist zu steuern, welche @ org.hibernate.annotations.Type die Kombination (@ Lob + Byte [] wird zugeordnet) (auf postgresql).


Hier ist das Snippet aus 3.5.5.Final von MaterializedBlobType (SQL-Typ Blob). Laut Steves Blog möchte postgresql, dass Sie Streams für bytea (fragen Sie mich nicht warum) und postgresqls benutzerdefinierten Blob-Typ für oids verwenden. Beachten Sie auch, dass die Verwendung von setBytes () in JDBC auch für bytea gilt (aus früheren Erfahrungen). Dies erklärt, warum Use-Streams keinen Einfluss darauf haben, dass beide von "bytea" ausgehen.

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

Das führt zu:

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

Update Die nächste logische Frage lautet: "Warum nicht einfach die Tabellendefinitionen manuell in bytea ändern" und das (@Lob + byte []) beibehalten? Dieses does funktioniert,BISSie versuchen, ein Null-Byte [] zu speichern. Was der postgreSQL-Treiber für einen Ausdruck vom Typ OID hält und der Spaltentyp bytea ist - dies liegt daran, dass der Ruhezustand (mit Recht) JDBC.setNull () anstelle von JDBC.setBytes (null) aufruft, was der PG-Treiber erwartet.

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

Das im Ruhezustand befindliche Typsystem ist derzeit in Bearbeitung (gemäß 3.5.5 Verfallserklärung). Tatsächlich ist ein Großteil des 3.5.5-Codes veraltet, und es ist schwer zu erkennen, worauf man bei der Unterklassifizierung von PostgreSQLDialect achten muss.

AFAKT, Types.BLOB/'oid' in postgresql sollte einem benutzerdefinierten Typ zugeordnet werden, der einen JDBC-Zugriff im OID-Stil verwendet (d. H. PostgresqlBlobType-Objekt und NOT MaterializedBlobType). Ich habe Blobs noch nie erfolgreich mit postgresql verwendet, aber ich weiß, dass bytea einfach so funktioniert, wie ich es erwartet hätte.

Ich schaue gerade auf die BatchUpdateException - es ist möglich, dass der Treiber das Batching nicht unterstützt.


Tolles Zitat aus dem Jahr 2004: "Um mein Geschwätz zusammenzufassen, ich würde sagen, sie sollten warten, bis der JDBC-Treiber die LOBs korrekt ausführt, bevor sie den Ruhezustand ändern."

Referenzen:

102
Justin

Wie kann man eine Byte-Eigenschaft [] auf tragbare Weise annotieren?

Es hängt davon ab, was du willst. JPA kann einen nicht kommentierten byte[] beibehalten. Aus der JPA 2.0-Spezifikation: 

11.1.6 Grundlegende Anmerkungen

Die Basic-Annotation ist die einfachste Art der Zuordnung zu einer Datenbankspalte . Die Basic-Annotation kann angewendet werden zu einer persistenten Eigenschaft oder Instanz Variable einer der folgenden Typen: Java-Grundelement, Typen, Wrapper der primitiven Typen, 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[], Aufzählungszeichen und beliebige andere Typ, der Serializable..__ implementiert. Wie in Abschnitt 2.8 beschrieben, wird die Verwendung von der Basic-Annotation ist optional für persistente Felder und Eigenschaften dieser Arten. Wenn das Basic Anmerkung ist für ein solches .__ nicht angegeben. Feld oder Eigenschaft die Standardwerte der Basic-Anmerkung wird zutreffen.

Und Hibernate wird "standardmäßig" einer SQL VARBINARY (oder einer SQL LONGVARBINARY abhängig von der Column-Größe?) Zugeordnet, die PostgreSQL mit einer bytea verarbeitet.

Wenn Sie jedoch möchten, dass der byte[] in einem großen Objekt gespeichert wird, sollten Sie einen @Lob verwenden. Aus der Spezifikation:

11.1.24 Lob-Anmerkung

Eine Lob-Annotation gibt an, dass eine persistente Eigenschaft oder Feld sollte .__ sein. behielt als großes Objekt eine datenbankgestützter großer Objekttyp . Portable Anwendungen sollten die .__ verwenden. Lob-Anmerkung beim Zuordnen zu einem Datenbank Lob Typ. Die Lob-Anmerkung kann in Verbindung mit der .__ verwendet werden. Grundlegende Anmerkung oder mit der ElementCollection-Anmerkung, wenn die Der Elementauflistungswert ist grundlegend Art. Ein Lob kann entweder ein Binärcode oder ein .__ sein. Zeichentyp. Der Lob-Typ ist abgeleitet vom Typ des persistentes Feld oder Eigenschaft und, mit Ausnahme von Zeichenfolgen- und Zeichentypen Der Standardwert ist Blob.

Und Hibernate wird es einer SQL-Variablen BLOB zuordnen, die PostgreSQL mit einer oid.__ behandelt.

Ist dies in einer aktuellen Version von Hibernate behoben?

Nun, das Problem ist, dass ich nicht genau weiß, was das Problem ist. Aber ich kann zumindest sagen, dass sich seit 3.5.0-Beta-2 (wo eine Änderung eingeführt wurde) im 3.5.x-Zweig nichts geändert hat.

Aber mein Verständnis von Problemen wie HHH-4876 , HHH-4617 und von PostgreSQL und BLOBs (erwähnt im Javadoc des PostgreSQLDialect) ist, dass Sie die folgende Eigenschaft festlegen sollen

hibernate.jdbc.use_streams_for_binary=false

wenn Sie oid verwenden möchten, d. h. byte[] mit @Lob (dies ist mein Verständnis, da VARBINARY nicht das ist, was Sie mit Oracle möchten). Hast du das probiert?

Alternativ schlägt HHH-4876 vor, das veraltete PrimitiveByteArrayBlobType zu verwenden, um das alte Verhalten zu erhalten (vor Hibernate 3.5).

Verweise

  • JPA 2.0-Spezifikation
    • Abschnitt 2.8 "Standardwerte für Felder oder Eigenschaften ohne Beziehung zuordnen"
    • Abschnitt 11.1.6 "Grundlegende Anmerkungen"
    • Abschnitt 11.1.24 "Lob Annotation"

Ressourcen

59
Pascal Thivent

Hier ist, was O'reilly Enterprise JavaBeans 3.0 sagt

JDBC hat spezielle Typen für diese sehr großen Objekte. Der Java.sql.Blob-Typ repräsentiert Binärdaten und Java.sql.Clob repräsentiert Zeichendaten.

Hier geht es zum PostgreSQLDialect-Quellcode

public PostgreSQLDialect() {
    super();
    ...
    registerColumnType(Types.VARBINARY, "bytea");
    /**
      * Notice it maps Java.sql.Types.BLOB as oid
      */
    registerColumnType(Types.BLOB, "oid");
}

Also, was können Sie tun?

Überschreiben Sie PostgreSQLDialect wie folgt

public class CustomPostgreSQLDialect extends PostgreSQLDialect {

    public CustomPostgreSQLDialect() {
        super();

        registerColumnType(Types.BLOB, "bytea");
    }
}

Definieren Sie jetzt einfach Ihren benutzerdefinierten Dialekt

<property name="hibernate.dialect" value="br.com.ar.dialect.CustomPostgreSQLDialect"/>

Und verwenden Sie Ihre tragbare JPA @ Lob-Annotation

@Lob
public byte[] getValueBuffer() {

UPDATE

Hier wurde extrahiert hier

Ich habe eine Anwendung, die im Ruhezustand 3.3.2 ausgeführt wird, und die Anwendungen funktionieren einwandfrei , wobei alle Blob-Felder oid (byte [] in Java) verwenden.

...

Bei der Migration auf Hibernate 3.5 funktionieren alle Blob-Felder nicht mehr , und im Serverprotokoll wird Folgendes angezeigt: ERROR org.hibernate.util.JDBCExceptionReporter - ERROR: Die Spalte hat den Typ oid, der Ausdruck jedoch den Typ bytea

was erklärt werden kann hier

Diese Regel ist kein Fehler in PG JDBC , , sondern eine Änderung der Standardimplementierung von Hibernate in Version 3.5 . In meiner Situation Einstellung kompatiblen Eigenschaft auf Verbindung hat nicht geholfen .

...

Viel mehr das, was ich in 3.5 - Beta 2 gesehen habe, und ich weiß nicht, ob dies behoben wurde. Hibernate - ohne @Type-Annotation - erstellt automatisch eine Spalte vom Typ oid, versucht es aber um dies als bytea zu lesen

Interessant ist, weil er, wenn er Types.BOLB als bytea abbildet (siehe CustomPostgreSQLDialect), erhält

Konnte nicht das JDBC batch Update ausführen

beim Einfügen oder Aktualisieren

9
Arthur Ronald

Ich habe es endlich geschafft. Sie erweitert die Lösung von A. Garcia. Da das Problem jedoch im Hibernate-Typ MaterializedBlob-Typ liegt, reicht die Zuordnung von Blob> bytea nicht aus. Daher benötigen wir einen Ersatz für MaterializedBlobType, der mit Hibernaten Unterstützung für defekte Blob funktioniert. Diese Implementierung funktioniert nur mit bytea, aber vielleicht könnte der Typ aus der JIRA-Ausgabe, der OID wollte - eine OID Implementierung beitragen.

Das Ersetzen dieser Typen zur Laufzeit ist leider ein Schmerz, da sie Teil des Dialekts sein sollten ..__ Wenn nur diese JIRA-Erweiterung in 3.6 kommt, wäre dies möglich.

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

Vieles davon könnte wahrscheinlich statisch sein (benötigt getBinder () wirklich eine neue Instanz?), Aber ich kann den internen Ruhezustand nicht wirklich verstehen, daher ist dies meistens Kopieren + Einfügen + Ändern.

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

Ich verwende den Hibernate 4.2.7.SP1 mit Postgres 9.3 und folgenden Arbeiten für mich:

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

da Oracle damit keine Probleme hat, und für Postgres verwende ich benutzerdefinierten Dialekt:

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

der Vorteil dieser Lösung ist meiner Meinung nach, dass ich die Gläser im Winterschlaf unberührt lassen kann.

Weitere Postgres/Oracle-Kompatibilitätsprobleme mit Hibernate finden Sie in meinem Blogbeitrag .

4
Peter Butkovic

mein Problem wurde durch Hinzufügen der Annotation von @Lob behoben, durch die das Byte [] in Oracle als Blob erstellt wird. Diese Annotation erstellt jedoch das Feld als OID, das nicht ordnungsgemäß funktioniert. Um Byte [] als Bytea erstellt zu machen, habe ich den Kunden Dialect für gemacht Postgres wie unten 

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

Müssen auch Parameter für den Dialekt überschreiben

spring.jpa.properties.hibernate.dialect = com.ntg.common.DBCompatibilityHelper.PostgreSQLDialectCustom

weitere Hinweise finden Sie hier: https://dzone.com/articles/postgres-and-Oracle

1

Ich habe es funktioniert, indem ich die Annotation mit der XML-Datei für Postgres überschrieb. Anmerkung wird für Oracle beibehalten. Meiner Meinung nach ist es in diesem Fall am besten, das Mapping dieser Problematik mit dem XML-Mapping zu überschreiben. Wir können einzelne/mehrere Entitäten mit XML-Mapping überschreiben. Daher würden wir Annotation für unsere hauptsächlich unterstützte Datenbank und eine XML-Datei für jede andere Datenbank verwenden.

Hinweis: Wir müssen nur eine einzelne Klasse überschreiben, es ist also keine große Sache. Lesen Sie mehr aus meinem Beispiel Beispiel zum Überschreiben von Annotationen mit XML

0
Vinh Vo

Auf Postgres bricht @Lob für Byte [], wenn es versucht, es als oid zu speichern, und für String tritt dasselbe Problem auf. Der folgende Code bricht bei Postgres, was bei Oracle einwandfrei funktioniert.

@Lob
private String stringField;

und 

@Lob
private byte[]   someByteStream;

Um oben auf postgres zu beheben, haben Sie die benutzerdefinierte hibernate.dialect geschrieben 

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

Konfigurieren Sie jetzt den benutzerdefinierten Dialekt im Ruhezustand 

hibernate.dialect=X.Y.Z.PostgreSQLDialectCustom   

X.Y.Z ist der Paketname. 

Jetzt funktioniert es gut. HINWEIS- Meine Version des Ruhezustands - 5.2.8.Final Postgres-Version 9.6.3

0
gajendra kumar

Danke Justin, Pascal, dass du mich in die richtige Richtung gelenkt hast. Ich hatte auch mit Hibernate 3.5.3 das gleiche Problem. Ihre Nachforschungen und Hinweise auf die richtigen Klassen hatten mir geholfen, das Problem zu identifizieren und eine Korrektur durchzuführen.

Für diejenigen, die immer noch mit Hibernate 3.5 hängen geblieben sind und die Kombination von oid + byte [] + @LoB verwenden, habe ich folgendes getan, um das Problem zu beheben.

  1. Ich habe einen benutzerdefinierten BlobType erstellt, der MaterializedBlobType erweitert und den Satz und die Get-Methoden mit dem OID-Stilzugriff überschreibt.

    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. Registrieren Sie den CustomBlobType bei Hibernate. Folgendes habe ich getan, um das zu erreichen.

      hibernateConfiguration= new AnnotationConfiguration();
      Mappings mappings = hibernateConfiguration.createMappings();
      mappings.addTypeDef("materialized_blob", "x.y.z.BlobType", null);