[RESOLVIDO] Persistir array de bytes com JPA

3 respostas
D

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:
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();
    }
E mandando persistir nesse:
private void persistir() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("BlobPU");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        
        em.persist(foto);
        em.getTransaction().commit();
        em.close();
    }
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.

3 Respostas

G

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.

D

Meu mapeamento está sendo feito da seguinte maneira:

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

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...

D

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.

Criado 23 de abril de 2011
Ultima resposta 23 de abr. de 2011
Respostas 3
Participantes 2