2010-07-02 7 views
14

Ich habe eine Spring-Anwendung, die Hibernate in einer PostgreSQL-Datenbank verwendet. Ich versuche, Dateien in einer Tabelle der Datenbank zu speichern. Es scheint es die Zeile mit der Datei speichert (ich verwende nur Methode auf EntityManager bestehen), aber wenn das Objekt aus der Datenbank, die ich die folgende Ausnahme erhalten geladen:Große Objekte dürfen nicht im automatischen Festschreibungsmodus verwendet werden

org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode. 

die Daten zu laden, ich bin mit Temporäres MultipartFile-Attribut und in seinem Setter setze ich die Information, die ich persistieren möchte (byte [], fileName, size). Das Unternehmen I aussieht wie dieser bin persistierende (ich habe den Rest von Getter/Setter weggelassen):

@Entity 
@Table(name="myobjects") 
public class MyClass { 

    @Id 
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequence") 
    @SequenceGenerator(name="sequence", sequenceName="myobjects_pk_seq", allocationSize=1) 
    @Column(name="id") 
    private Integer id; 

    @Lob 
    private String description; 

    @Temporal(TemporalType.TIMESTAMP) 
    private Date creationDate; 

    @Transient 
    private MultipartFile multipartFile; 

    @Lob 
    @Basic(fetch=FetchType.LAZY, optional=true) 
    byte[] file; 

    String fileName; 

    String fileContentType; 

    Integer fileSize; 

    public void setMultipartFile(MultipartFile multipartFile) { 
     this.multipartFile = multipartFile; 
     try { 
      this.file = this.multipartFile.getBytes(); 
      this.fileName = this.multipartFile.getOriginalFilename(); 
      this.fileContentType = this.multipartFile.getContentType(); 
      this.fileSize = ((Long) this.multipartFile.getSize()).intValue(); 
     } catch (IOException e) { 
      logger.error(e.getStackTrace()); 
     } 
    } 
} 

Ich kann sehen, dass, wenn es beibehalten wird ich die Daten in der Zeile haben, aber wenn ich rufe Diese Methode schlägt fehl:

public List<MyClass> findByDescription(String text) { 
    Query query = getEntityManager().createQuery("from MyClass WHERE UPPER(description) like :query ORDER BY creationDate DESC"); 
    query.setParameter("query", "%" + text.toUpperCase() + "%"); 
    return query.getResultList(); 
} 

Diese Methode schlägt nur fehl, wenn das Ergebnis Objekte mit Dateien enthält. Ich habe versucht, in meine persistence.xml

<property name="hibernate.connection.autocommit" value="false" /> 

setzen, aber es löst das Problem nicht.

Im Allgemeinen funktioniert die Anwendung gut es Commit nur die Daten, wenn die Transaktion abgeschlossen ist und es führt einen Rollback, wenn etwas fehlschlägt, so verstehe ich nicht, warum das passiert.

Irgendeine Idee?

Danke.

UPDATE

bei der Verbindung von Shekhar gegeben Suche wird vorgeschlagen, den Anruf in einem transation aufzunehmen, also hat er den Service-Aufruf innerhalb einer Transaktion setze ein es funktioniert (ich habe hinzugefügt @Transactional Anmerkung).

Das Problem ist, dass ich keine Daten persistieren will, also verstehe ich nicht, warum es in einer Transaktion enthalten sein sollte. Macht es Sinn, einen Commit zu machen, wenn ich nur einige Daten aus der Datenbank geladen habe?

Danke.

+0

Wenn Sie keine Daten persistieren möchten, warum haben Sie 'Dateien' gemappt? Sollte es nicht '@ Transient' sein? –

+0

@matt b Ich möchte die Dateien aber nur in der Sicherungsoperation beibehalten, nicht in der gelesenen. Deshalb habe ich nicht verstanden, dass das Lesen in einer Transaktion sein sollte. – Javi

+0

Sie verwenden @Transactional nicht nur, wenn Sie Objekte speichern möchten. In der Regel ist eine Transaktion erforderlich, um auf die Datenbank zugreifen zu können - egal ob Lesen oder Schreiben. – Volksman

Antwort

21

Ein großes Objekt kann in mehreren Datensätzen gespeichert werden, deshalb müssen Sie eine Transaktion verwenden. Alle Datensätze sind korrekt oder gar nicht.

https://www.postgresql.org/docs/current/static/largeobjects.html

+4

Ich bin mir nicht ganz sicher, wie das eine Antwort auf die ursprüngliche Frage ist? Das ursprüngliche Poster will keine LOBs ohne Transaktion schreiben, sondern ohne sie lesen. –

2

Sofern Sie als 1 GB große Dateien speichern müssen empfehle ich Ihnen bytea als Datentyp an Stelle von großen Objekt verwenden.

bytea ist im Grunde, was BLOB ist in anderen Datenbanken (z. B. Oracle) und seine Handhabung ist viel mehr kompatibel mit JDBC.

4

Wenn Sie können, erstellen Sie eine Zwischen-Entität zwischen MyClass und Datei-Eigenschaft für Beispiel. Etwas wie:

@Entity 
@Table(name="myobjects") 
public class MyClass { 
    @OneToOne(cascade = ALL, fetch = LAZY) 
    private File file; 
} 

@Entity 
@Table(name="file") 
public class File { 
    @Lob 
    byte[] file; 
} 

können Sie nicht @Lob verwenden und Art Faul holen. Es funktioniert nicht. Sie müssen eine Zwischenklasse haben.

+0

Wenn Sie den Lob nicht bei jeder Abfrage abrufen müssen, ist dies der beste Ansatz. Es wird ein gewisser Mehraufwand entstehen, da beim Zugriff auf das Objekt eine zusätzliche Abfrage ausgelöst werden muss, aber in manchen Situationen ist dies ideal. – Nicolas

Verwandte Themen