quinta-feira, 12 de junho de 2008

Salvando fotos no banco de dados com Hibernate

Olá amiguinhos,

Vi nos fóruns por ae que muita gente tem dúvida quanto a isso. Eu particularmente, prefiro salvar no banco de dados, se forem fotos simples de um cadastro. Acho que evita muitos problemas futuramente.

O arquivo com todos os arquivos do projeto pode ser baixado nesse link: Download

Nesse exemplo que vou ensinar é bem simples, nem me dei ao luxo de fazer uma mega interface com o Swing, usei o FlowLayout como padrão mesmo e taquei os componentes lá. A beleza do negócio está no Hibernate hehe. Bom, vamos lá, vou ensinar passo a passo. Estou usando o Maven como gerenciador de projetos, que na minha opinião, é perfeito. Mas se você não quiser usar o Maven, basta adicionar no Classpath as bibliotecas do Hibernate e MySQL. Antes de começar, verifique se você possui o MySQL instalado na sua máquina, e então crie um database com o nome de hibernate_object:

CREATE DATABASE 'hibernate_object';


Crie um usuário para esse database agora com o seguinte comando:

GRANT ALL ON hibernate_object.* TO 
'hibernate'@localhost IDENTIFIED BY 'hibernate123';


Perfeito!!! Agora pode fechar o MySQL que agora vamos só trabalhar com Java. Se você não for usar o Maven, crie um projeto com o nome de HibernateObject. Se estiver usando Maven, crie um archtype padrão e adicione as dependências do Hibernate e MySQL:

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>br.org.hibernateobject</groupId>
<artifactId>HibernateObject</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>HibernateObject</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.3.1.GA</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
</project>


Para criar a estrutura de diretórios padrão do Maven, basta usar esse comando (Se você estiver usando Linux é claro ;) ):

$ mkdir -p src/{main,test}/{java,resources}


Agora basta dar o comando do Maven para atualiza o projeto para o Eclipse:

$ mvn eclipse:eclipse


Vamos botar a mão na massa agora. Vamos precisar da seguinte estrutura de classes:

Cadastro
CadastroDao
Hibernateinitialize
Principal


E o arquivo XML do Hibernate:

hibernate.cfg.xml


Primeiro, vamos criar a classe Cadastro, que terá a seguinte estrutura:

package br.org.hibernateobject;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;

@Entity
@Table(name = "cadastro")
public class Cadastro {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id_cadastro")
private int id;

@Column(length = 100)
private String nome;

@Lob
private byte[] foto;

public int getId() {
return id;
}

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

public byte[] getFoto() {
return foto;
}

public void setFoto(byte[] foto) {
this.foto = foto;
}

public String getNome() {
return nome;
}

public void setNome(String nome) {
this.nome = nome;
}
}


O segredo pra funcionar esse esquema é o annotation @Lob, que cria um campo no banco de dados do tipo Blob ou Clob. Eu não testei em outros bancos, somente no MySQL, mas creio que essa mesma annotation dá pra ser usada em outros tipos.

Agora vamos criar a classe CadastroDao, onde fará toda a mágica do negócio:

package br.org.hibernateobject;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.swing.JOptionPane;

import org.hibernate.HibernateException;

public class CadastroDao {

public void save(String txt) {
if (txt.equals("") || txt == null) {
JOptionPane.showMessageDialog(null, "Campo obrigatório!");
} else {
try {
String nome = null;
File f = new File(txt);

// Converte o arquivo em um array de bytes
byte[] buffer = new byte[(int) f.length()];
DataInputStream ds = new DataInputStream(new FileInputStream(f));
ds.readFully(buffer);
ds.close();

// Pega o nome do arquivo
nome = f.getName();

Cadastro c = new Cadastro();
c.setNome(nome);
c.setFoto(buffer);

// Finaliza a transacao com o hibernate
Principal.getHi().getSession().save(c);
Principal.getHi().getTx().commit();

JOptionPane.showMessageDialog(null,
"Arquivo cadastrado com sucesso!");
} catch (HibernateException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}


public void get(int id) {
try {
Cadastro c;
c = (Cadastro)Principal.getHi().getSession()
.get(Cadastro.class, id);

String arquivo = "/tmp/" + c.getNome();

FileOutputStream fos = new FileOutputStream(arquivo);
byte[] buffer = c.getFoto();

fos.write(buffer);
fos.flush();
fos.close();

JOptionPane.showMessageDialog(null,
"Arquivo resgatado com sucesso em: " + arquivo);
} catch (HibernateException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}


O método save(String txt) pega o caminho absoluto de onde está o arquivo da foto, depois passa para um DataInputStream que passa para um array de bytes. Feito isso, basta apenas passar para nosso método lá da classe Cadastro setFoto(byte[] foto). O método get(int id) faz o processo contrário, salvando a foto do banco para o diretório /tmp conforme foi setado na variável arquivo.

Vamos criar a classe HibernateInitialize, que vai ser responsável em criar a fábrica de sessão do Hibernate e iniciar as transações:

package br.org.hibernateobject;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;

public class HibernateInitialize {

private Session session;
private Transaction tx;


public HibernateInitialize() {
Configuration cfg = new AnnotationConfiguration()
.addAnnotatedClass(Cadastro.class)
.setProperty(Environment.HBM2DDL_AUTO, "update");

cfg.configure("/br/org/hibernateobject/hibernate.cfg.xml");

SessionFactory sf = cfg.buildSessionFactory();
session = sf.openSession();
tx = session.beginTransaction();
}


public Session getSession() {
return session;
}

public void setSession(Session session) {
this.session = session;
}

public Transaction getTx() {
return tx;
}

public void setTx(Transaction tx) {
this.tx = tx;
}

}


Dispensa comentários. Eu ainda uso XML para configurações, se você usa annotations, pode fazer essa classe do jeito que preferir. O arquivo hibernate.cfg.xml tem a seguinte estrutura:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
<!-- properties -->
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="connection.url">
jdbc:mysql://localhost:3306/hibernate_object
</property>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="show_sql">false</property>
<property name="connection.username">hibernate</property>
<property name="connection.password">hibernate123</property>
<property name="connection.pool_size">10</property>
<!-- mapping classes -->
<mapping class="br.org.hibernateobject.Cadastro" />
</session-factory>
</hibernate-configuration>


Agora vamos fazer a classe Principal, que vai ser a interface gráfica e dispensa comentário também. Com uma interface simples usando Swing e chamando os métodos do CadastroDao:

package br.org.hibernateobject;

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class Principal extends JFrame {

private static final long serialVersionUID = 1L;
private static HibernateInitialize hibernate;

public Principal() {
super("Hibernate Salvando Objetos");

setSize(400,100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);

// Centraliza a janela
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int w = getSize().width;
int h = getSize().height;
int x = (dim.width-w) / 2;
int y = (dim.height-h) / 2;
setLocation(x, y);

final JTextField txtFoto = new JTextField(30);
final JButton btOK = new JButton("Salvar");
final JButton btGet = new JButton("Resgatar");

// Acao do click do botao
btOK.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {
String txt = txtFoto.getText();
CadastroDao cdao = new CadastroDao();
cdao.save(txt);
}

});

btGet.addActionListener(new ActionListener(){

public void actionPerformed(ActionEvent e) {
int id = Integer.parseInt(JOptionPane
.showInputDialog("Informe o ID do cadastro:"));

CadastroDao cdao = new CadastroDao();
cdao.get(id);
}

});

JPanel p = new JPanel();
p.add(txtFoto);
p.add(btOK);
p.add(btGet);

getContentPane().add(p);
}

public static void main(String[] args) {
new Principal().setVisible(true);
hibernate = new HibernateInitialize();
}


public static HibernateInitialize getHi() {
return hibernate;
}


public static void setHi(HibernateInitialize hi) {
Principal.hibernate = hi;
}
}


Execute o seu programa e digite o caminho completo de algum arquivo de foto e clique no botão Salvar, se aparecer a mensagem 'Arquivo cadastrado com sucesso!', parabéns, senão, verifique seu código.

É isso, espero ter ajudado com esse pequeno conhecimento. O Hibernate é muito poderoso, dá pra fazer muita coisa com ele sem ao menos mexer no banco.

Enjoy...

0 comentários: