[RESOLVIDO] Persistir array de bytes com JPA

Olá amigos do GUJ,

Dei uma pesquisada aqui no fórum e achei algumas pessoas com esse problema… alguns até pareceram ter resolvido, mas eu não consegui resolver o meu. É o seguinte:

Estou desenvolvendo uma aplicação desktop para persistir imagens usando JPA pra uma disciplina da faculdade, recebo a imagem através de um JFileChooser, faço os passos para transformar em array de bytes, coloco no meu objeto persistente, mando persistir… mas dá erro ):

Estou recebendo a imagem nesse trecho de código:

[code] private void transferir() {
this.foto = new Imagem();
ImageIcon imagem = new ImageIcon(this.fchooser.getSelectedFile().getAbsolutePath());

    try {
        BufferedImage bfimg = ImageIO.read(this.fchooser.getSelectedFile());
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(bfimg, "jpg", baos);
        byte[] data = baos.toByteArray();

        this.foto.setImagem(data);
    } catch (IOException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
    }
    persistir();
}[/code]

E mandando persistir nesse:

[code] private void persistir() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(“BlobPU”);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

    em.persist(foto);
    em.getTransaction().commit();
    em.close();
}[/code]

As exceções lançadas são essas:

AVISO: SQL Error: 0, SQLState: null 23/04/2011 17:41:16 org.hibernate.util.JDBCExceptionReporter logExceptions GRAVE: Entrada em lote 0 insert into Imagem (imagem, id) values (17617, 6) foi abortada. Chame getNextException para ver a causa. 23/04/2011 17:41:16 org.hibernate.util.JDBCExceptionReporter logExceptions AVISO: SQL Error: 0, SQLState: 42804 23/04/2011 17:41:16 org.hibernate.util.JDBCExceptionReporter logExceptions GRAVE: ERRO: coluna "imagem" é do tipo bytea mas expressão é do tipo bigint 23/04/2011 17:41:16 org.hibernate.event.def.AbstractFlushingEventListener performExecutions GRAVE: Could not synchronize database state with session org.hibernate.exception.SQLGrammarException: Could not execute JDBC batch update at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67) at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43) at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:253) at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:237) at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:141) at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298) at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27) at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000) at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338) at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106) at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54) at dac.frame.Main.persistir(Main.java:131) at dac.frame.Main.transferir(Main.java:122) at dac.frame.Main.enviarActionPerformed(Main.java:100) at dac.frame.Main.access$000(Main.java:28) at dac.frame.Main$1.actionPerformed(Main.java:63) at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995) at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318) at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387) at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242) at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236) at java.awt.Component.processMouseEvent(Component.java:6267) at javax.swing.JComponent.processMouseEvent(JComponent.java:3267) at java.awt.Component.processEvent(Component.java:6032) at java.awt.Container.processEvent(Container.java:2041) at java.awt.Component.dispatchEventImpl(Component.java:4630) at java.awt.Container.dispatchEventImpl(Container.java:2099) at java.awt.Component.dispatchEvent(Component.java:4460) at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4577) at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4238) at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168) at java.awt.Container.dispatchEventImpl(Container.java:2085) at java.awt.Window.dispatchEventImpl(Window.java:2478) at java.awt.Component.dispatchEvent(Component.java:4460) at java.awt.EventQueue.dispatchEvent(EventQueue.java:599) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161) at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Vi que ele falou sobre a coluna ser do tipo “bytea” e eu estar passando “bigint”. Mas não é o caso, estou passando um array de bytes (byte[]).
Estou usando PostgreSQL… pode ser alguma incompatibilidade?

Desde já, agradeço a ajuda.

Arquivo binário eu crio como um campo de array de bytes com a anotação @Lob.

Os getters e setters são “normais” apenas chamando e “setando” para o campo.

A persistência basta adicionar o array no campo “setArquivo” e está tudo pronto.

Ás vezes pode ser necessário transformar InputStream para array de byte, você nesse caso pode usar a classe ByteArrayInputStream.

Meu mapeamento está sendo feito da seguinte maneira:

[code]package dac.frame;

import java.io.Serializable;
import java.util.Arrays;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;

/**

  • @author Diogo Moreira
    */
    @Entity
    public class Imagem implements Serializable{

    @Id
    @GeneratedValue
    private int id;
    @Lob @Basic(fetch= FetchType.EAGER) @Column(columnDefinition=“bytea”)
    private byte[] imagem;

    public int getId() {
    return id;
    }

    public void setId(int id) {
    this.id = id;
    }

    public byte[] getImagem() {
    return imagem;
    }

    public void setImagem(byte[] imagem) {
    this.imagem = imagem;
    }

    @Override
    public boolean equals(Object obj) {
    if (obj == null) {
    return false;
    }
    if (getClass() != obj.getClass()) {
    return false;
    }
    final Imagem other = (Imagem) obj;
    if (this.id != other.id) {
    return false;
    }
    if (!Arrays.equals(this.imagem, other.imagem)) {
    return false;
    }
    return true;
    }

    @Override
    public int hashCode() {
    int hash = 7;
    hash = 31 * hash + this.id;
    hash = 31 * hash + Arrays.hashCode(this.imagem);
    return hash;
    }

    public Imagem() {
    }

}[/code]

Acredito que no mapeamento não tenha nenhum problema, acho que o problema deva ser alguma incompatibilidade com o banco… visto que ele acusa que estou passando bigint ao invés de bytea. Mas não sei como resolver…

Resolvi meu problema!
Estava no mapeamento, de certo modo…

Estava assim:

@Lob @Basic(fetch= FetchType.EAGER) @Column(columnDefinition="bytea") private byte[] imagem;
Agora ficou assim:

@Lob @Basic(fetch= FetchType.EAGER) private byte[] imagem;
Ele guarda esse byte em um campo do tipo OID, lá no banco aparece apenas um número… mas enfim, deu certo! Vou dar uma estudada a mais como usar campos bytea com JPA, mas por enquanto isso resolveu meu problema.

Parece meio bobo resolver depois de tão pouco tempo, mas é que eu só tinha utilizado campos bytea no PostgreSQL e achei que fosse necessário mapear isso obrigatoriamente.