2010-07-02 5 views
14

Tengo una aplicación Spring que usa Hibernate en una base de datos PostgreSQL. Intento almacenar archivos en una tabla de la base de datos. Parece que almacena la fila con el archivo (sólo tiene que utilizar el método persistir en EntityManager), pero cuando el objeto se carga desde la base de datos me sale el siguiente excepción:Los objetos grandes no se pueden utilizar en el modo de confirmación automática

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

Para cargar los datos que estoy usando una MultipartFile atributo transitorio y en su setter estoy configurando la información que quiero persistir (byte [], fileName, tamaño). La entidad estoy persistiendo miradas como éste (he omitido el resto de getters/setters):

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

puedo ver que cuando se persistió tengo los datos en la fila, pero cuando llamo este método falla:

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

Este método solo falla cuando el resultado tiene objetos con archivos. He intentado establecer en mi persistence.xml

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

pero no resuelve el problema.

En general, la aplicación funciona bien, solo compromete los datos cuando la transacción finaliza y realiza una reversión si algo falla, por lo que no entiendo por qué sucede esto.

¿Alguna idea?

Gracias.

ACTUALIZACIÓN

Mirando el enlace dado por Shekhar se sugiere incluir la llamada en un transation, por lo que ha fijado la llamada de servicio dentro de una transacción una que funciona (He añadido @Transactional anotación).

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

el problema es que no quiero que persistan los datos, así que no entiendo por qué se debe incluir dentro de una transacción. ¿Tiene algún sentido hacer una confirmación cuando solo he cargado algunos datos de la base de datos?

Gracias.

+0

si no desea conservar los datos ¿por qué tiene 'archivos' mapeados? ¿No debería ser '@ Transient'? –

+0

@matt b Deseo conservar los archivos pero solo en la operación de guardar, no en la de lectura. Por eso no entendí que la lectura debería estar en una transacción. – Javi

+0

No solo usa @Transactional cuando quiere guardar objetos. Por lo general, se requiere una transacción para acceder a la base de datos, ya sea leyendo o escribiendo. – Volksman

Respuesta

21

Un objeto grande se puede almacenar en varios registros, es por eso que debe usar una transacción. Todos los registros son correctos o nada en absoluto.

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

+4

No estoy muy seguro de cómo esa es una respuesta a la pregunta original? El póster original no quiere * escribir * LOB sin una transacción, quiere leerlos sin uno. –

2

A menos que necesite para almacenar archivos de gran tamaño de 1 GB le sugiero que utilice bytea como el tipo de datos en lugar de objeto grande.

bytea es básicamente lo que BLOB es en otras bases de datos (por ejemplo, Oracle) y su manejo es mucho más compatible con JDBC.

4

Si puede, cree una entidad intermedia entre MyClass y la propiedad de archivo, por ejemplo. Algo así como:

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

No puede utilizar @Lob a buscar tipo perezoso. No funciona. Debes tener una clase intermedia.

+0

Si no necesita recuperar el Lob en cada consulta, este es el mejor enfoque. Se incurrirá en una sobrecarga, ya que se necesita despachar una consulta adicional cuando se accede al objeto, pero en algunas situaciones, esto es ideal. – Nicolas

Cuestiones relacionadas