2010-07-02 7 views
14

Ho un'applicazione Spring che utilizza Hibernate su un database PostgreSQL. Sto cercando di memorizzare i file in una tabella del database. Sembra che memorizza la riga con il file (mi basta usare metodo su EntityManager persistono), ma quando l'oggetto viene caricato dal database ottengo la seguente eccezione:Non è possibile utilizzare oggetti grandi in modalità di commit automatico

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

per caricare i dati che sto utilizzando un MultipartFile transient atribute e nel suo setter sto impostando l'informazione che voglio persistere (byte [], fileName, size). L'entità che sto persistente aspetto come questo (ho omesso il resto dei getter/setter):

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

posso vedere che quando è persistito ho i dati nella fila ma quando chiamo questo metodo non riesce:

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

Questo metodo ha esito negativo solo quando il risultato contiene oggetti con file. Ho provato a impostare nel mio persistence.xml

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

ma non risolve il problema.

In generale, l'applicazione funziona bene, si limita a impegnare i dati solo quando la transazione è terminata e esegue un rollback se qualcosa non funziona, quindi non capisco perché sta succedendo.

Qualche idea?

Grazie.

UPDATE

Guardando il link indicato da Shekhar si suggerisce di includere la chiamata in un transation, così ho impostato la chiamata di servizio all'interno di una transazione di un funziona (ho aggiunto @Transactional annotazione).

@Transactional 
public List<myClass> find(String text) { 
    return myClassDAO.findByDescription(text); 
} 

il problema è che io non voglio a persistere i dati in modo da non capisco perché dovrebbe essere includere all'interno di una transazione. Ha senso fare un commit quando ho caricato solo alcuni dati dal database?

Grazie.

+0

se non si desidera mantenere i dati perché si esegue il mapping di 'file'? Non dovrebbe essere '@ Transient'? –

+0

@matt b Voglio mantenere i file ma solo nell'operazione di salvataggio non in quella di lettura. Questo è il motivo per cui non ho capito che la lettura dovrebbe essere in una transazione. – Javi

+0

Non si utilizza @Transactional quando si desidera salvare oggetti. In genere è necessaria una transazione per accedere al database, sia che si tratti di leggere o scrivere. – Volksman

risposta

21

Un oggetto di grandi dimensioni può essere memorizzato in più record, ecco perché è necessario utilizzare una transazione. Tutti i record sono corretti o nulla del tutto.

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

+4

Non sono sicuro di quale sia la risposta alla domanda originale? Il poster originale non vuole * scrivere * LOB senza una transazione, vuole leggerli senza uno. –

2

A meno che non è necessario memorizzare file di grandi dimensioni di 1 GB vi consiglio di utilizzare bytea come il tipo di dati invece che oggetto di grandi dimensioni.

bytea è fondamentalmente quello che BLOB è in altri database (ad esempio Oracle) e la sua gestione è molto più compatibile con JDBC.

4

Se è possibile, creare un'entità intermedia tra MyClass e la proprietà del file, ad esempio. Qualcosa di simile:

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

Non è possibile utilizzare @Lob a prendere il tipo pigro. Non funziona. Devi avere una classe intermedia.

+0

Se non è necessario recuperare il Lob su ogni query, questo è l'approccio migliore. Ciò comporterà un sovraccarico, poiché è necessario inviare una query aggiuntiva quando si accede all'oggetto, ma in alcune situazioni questo è l'ideale. – Nicolas

Problemi correlati