set

22

Spring Framework Parte 4 -> Integração com o Hibernate

By Felipe Saab

Antes de começarmos mais um post da série de tutoriais sobre o Spring Framework eu sugiro a leitura dos posts anteirores (caso você ainda não tenha conhecimento dos assuntos abordados):



Assumindo então que agora você já sabe sobre o Spring, vamos ver como ele facilita o uso do framework de mapeamento objeto-relacional Hibernate (versão 3.x).

OBS: Irei comentar detalhadamente apenas coisas relacionadas ao Spring, ou seja, espero que você já possua o conhecimento necessário do Hibernate.

Apenas para lembrarmos como o Spring tenta padronizar o acesso a dados em uma base de dados vamos dar uma olhada na imagem do post anterior (retirada do livro Spring in Action 2, Manning 2008):

Interpretando a figura a gente consegue chegar em algo assim: o nosso objeto responsável pelas operações de manipulação de dados irá utilizar um template (fornecido pelo Spring) que saiba interagir com o Hibernate (que conste no classpath e cuja versão seja 3.x) e que irá utilizar um DAO que saiba se comunicar com a base de dados (um data source).

Só com o parágrafo acima já deu pra ver que precisaremos criar e configurar alguns beans no Spring: um data source que se comunique com a base de dados e um template que se comunique com o Hibernate.

Vamos a eles então:

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost/hibernate_teste" />
        <property name="username" value="root" />
        <property name="password" value="123" />
    </bean>
 
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

Como você pode notar o template precisa de uma referência para um bean que seja uma implementação de org.hibernate.SessionFactory e aí depende de como você está acostumado a mapear os objetos para tabelas: utilizando XML ou Anotações.

Utilizando XML:

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mappingResources">
            <list>
                <value>Livro.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

Utilizando classes anotadas:

    <bean id="sessionFactory"
          class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
                <value>spring_hibernate.modelo.Livro</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

Os beans são bem parecidos, precisam de pelo menos três propriedades setadas via injeção de dependência:

dataSource -> referência para o data source
hibernateProperties -> uma lista (do tipo props, onde tanto a chave (key) quanto o valor são Strings) contendo as propriedades da session factory que queremos setar

E a terceira propriedade depende do bean que está sendo declarado:

– se for LocalSessionFactoryBean utiliza-se a propriedade mappingResources que recebe uma lista contendo todos os XMLs responsáveis pelo mapeamento
– se for AnnotationSessionFactoryBean utiliza-se a propriedade annotatedClasses que recebe uma lista contendo todas as classes que representam o mapeamento

OK. Estes são os beans que o Spring fornece para facilitar a nossa vida ao utilizar o Hibernate. Vamos então a um pouco de código para demonstrar como nós utilizamos tais beans nas nossas próprias classes.

Primeiro de tudo, no banco de dados vamos criar uma tabela para os testes:

CREATE TABLE  `Livros` (
    `liv_cod` INT NOT NULL ,
    `liv_titulo` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
    PRIMARY KEY (  `liv_cod` )
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Nossa tabela então será sobre livros, contendo apenas o código e o título do livro, vamos a classe que vai representar um livro:

public class Livro {
 
    private int cod;
    private String titulo;
 
    public Livro() {
    }
 
    public int getCod() {
        return cod;
    }
 
    public void setCod(int cod) {
        this.cod = cod;
    }
 
    public String getTitulo() {
        return titulo;
    }
 
    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }
}

Um simples POJO para representar o livro… :D

Neste tutorial eu irei fazer o mapeamento utilizando um XML, vamos a ele:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="spring_hibernate.modelo.Livro" table="Livros">
    <id name="cod" column="liv_cod" type="int">
      <generator class="increment"/>
    </id>
    <property name="titulo" column="liv_titulo" />
  </class>
</hibernate-mapping>

Como a tabela contém apenas dois campos o mapeamento fica bem simples: a chave primária que é a variável de instância cod (coluna liv_cod) e a variável de instância titulo ligada a coluna “liv_titulo”.

Só chamando a atenção para o generator do cod, que é increment, ou seja, não precisamos setar o código de um novo livro para adicioná-lo ao banco, o novo código será gerado sozinho pelo Hibernate.

Muito bem, agora que o mapeamento da classe Livro para a tabela Livros está feito vamos começar a escrever o nosso DAO. Primeiro vamos a interface (gosto de reforçar a idéia de que interfaces devem ser utilizadas):

public interface IBanco {
 
    public Livro InserirLivro(Livro livro);
 
    public void AtualizarLivro(Livro livro);
 
    public void ApagarLivro(Livro livro);
 
    public List<livro> SelecionarLivrosPorTitulo(String titulo);
 
    public Livro SelecionarLivroPorCodigo(int cod);
}

Acho que o único comentário que essa interface merece é no método InserirLivro porque ele recebe o Livro que será inserido e retorna outro Livro. Isso se deve ao fato da variável de instância cod do Livro ser nula quando o livro estiver sendo gravado no banco e após a sua gravação o Hibernate retorna qual valor foi atribuído como código, ou seja, depois de gravado no banco é que nós descobrimos o valor do código, então apenas atualizamos o objeto Livro com o seu novo código e retornamos um objeto consistente.

Vamos à implementação dessa interface para podermos ver como o template do Hibernate facilita a nossa vida:

public class BancoDados implements IBanco {
 
    HibernateTemplate hibernateTemplate;
 
    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }
 
    public Livro InserirLivro(Livro livro) {
        int cod = (Integer) hibernateTemplate.save(livro);
        livro.setCod(cod);
        return livro;
    }
 
    public void AtualizarLivro(Livro livro) {
        hibernateTemplate.update(livro);
    }
 
    public void ApagarLivro(Livro livro) {
        hibernateTemplate.delete(livro);
    }
 
    public List<livro> SelecionarLivrosPorTitulo(String titulo) {
        //utilizando criteria
        DetachedCriteria crit = DetachedCriteria.forClass(Livro.class)
                .add(Restrictions.like("titulo", titulo));
        return hibernateTemplate.findByCriteria(crit);
 
        //utilizando HQL
        //String hql = "from Livro where titulo like '%"+titulo+"%'";
        //return hibernateTemplate.find(hql);
    }
 
    public Livro SelecionarLivroPorCodigo(int cod) {
        Livro livro = (Livro) hibernateTemplate.get(Livro.class, cod);
        return livro;
    }
}

Hibernate já facilita a vida do desenvolvedor, adicionando o Spring na brincadeira então, tudo fica mais fácil como você acabou de ver.

Creio que os métodos save, update e delete do template são completamente auto explicativos.

Porém quando o assunto vai para o lado da busca e seleção de registros, temos alguns métodos que podem ajudar:

– método get que busca um objeto da classe passada por parâmetro que contenha a chave primária igual ao segundo parâmetro
– método find que realiza uma busca de acordo com uma query HQL
– método findByCriteria para quem está mais acostumado a fazer busca com criterias

Os métodos find e findByCriteria (e os outros find… que o template tem disponível) retornam um List tipado com objetos relacionados à consulta, no nosso caso então é retornado um List .

Mais um pequeno detalhe: o HibernateTemplate que usamos nesta classe deverá ser injetado pelo container, ou seja, essa classe será mais um bean para o Spring gerenciar. O arquivo de configuração completo deste tutorial será assim:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
 
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost/hibernate_teste" />
        <property name="username" value="root" />
        <property name="password" value="123" />
    </bean>
 
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mappingResources">
            <list>
                <value>Livro.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>
 
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
 
    <bean id="banco_dados" class="spring_hibernate.BancoDados">
        <property name="hibernateTemplate" ref="hibernateTemplate" />
    </bean>
 
</beans>

Como exemplo final eu só gostaria de deixar um código de como utilizar essa classe BancoDados:

public class Main {
 
    public static void main(String[] args) {
 
        ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/spring.xml");
        System.out.println("Spring inicializado");
 
        BancoDados banco = (BancoDados) ctx.getBean("banco_dados");
 
        Livro livro1 = new Livro();
        livro1.setTitulo("Spring + Hibernate");
        Livro livro2 = new Livro();
        livro2.setTitulo("Hibernate Avançado");
        System.out.println("Objetos Livro inicializados");
 
        livro1 = banco.InserirLivro(livro1);
        livro2 = banco.InserirLivro(livro2);
        System.out.println("Livros gravados no banco");
 
        //zerando os objetos Livro
        livro1 = null;
        livro2 = null;
 
 
        System.out.println("Selecionando livros por código");
        livro1 = banco.SelecionarLivroPorCodigo(1);
        exibe(livro1);
 
        livro2 = banco.SelecionarLivroPorCodigo(2);
        exibe(livro2);
 
        System.out.println("Selecionando livros que contenham 'Hibernate' no título");
        List<livro> lista = banco.SelecionarLivrosPorTitulo("Hibernate");
        for (Livro l : lista){
            exibe(l);
        }
 
        System.out.println("Atualizando livro");
        livro1.setTitulo(livro1.getTitulo()+" Segunda Edição");
        banco.AtualizarLivro(livro1);
 
        System.out.println("Apagando livro");
        banco.ApagarLivro(livro2);
    }
 
    static void exibe(Livro livro) {
        if (livro == null)
            System.out.println("Livro não encontrado");
        else
            System.out.println("Livro -> cod: " + livro.getCod() + " | Título: " + livro.getTitulo());
    }
}

Muito bem leitor, se você chegou até aqui você vai desenvolver com uma produtividade bem alta porque agora que você já sabe Hibernate E Spring você pode se concentrar muito mais na camada de negócios, ou seja, não precisa perder muito tempo codificando acesso a banco de dados e operações necessárias para manipulação dos dados.

Caso você queira os arquivos que eu desenvolvi neste tutorial basta pegá-los aqui (o arquivo está meio pesado por causa das bibliotecas).

Dúvidas, críticas e etc fique a vontade para usar os comentários.. :D

Até a próxima,
Saab.

9 Responses so far

Olá!
Adorei sua série de posts sobre Spring! Agora você poderia fazer um sobre Spring MVC ;)
Estou precisando entender de um jeito prático como funciona!

Obrigada!

Obrigado pelo retorno positivo Carol, fico feliz que tenha gostado. :D
Sobre o Spring MVC, este será um dos próximos tópicos abordados aqui, pode ficar tranquila.
=]

Cara parabens ensinou muito bem de uma forma muito simples fazia tempo que eu tava pesquizando algo do genero estou comentando só este post mais vale para os 4 inclusive a iniciativa de usar o spring-jdbc, bem explicado valeu mesmo
agora vou ver o post de spring-security

Bom como já falei antes seu posts sobre spring estão muito bons, já peguei diversos livros antes, mas em pouco tempo vc conseguiu passar muito melhor.

Obrigado.

Muito bom o post, porém, ficou devendo a parte anotada (AnnotationSessionFactoryBean).
Poderia complementar.

Opa, ótima sugestão Adriano!
Já anotei aqui na minha lista de posts a fazer e assim que der vc verá esse assunto por aqui! :D
Sinceramente acho que tal assunto merece um post próprio, falando de Hibernate Annotations.
[]s e obrigado pela sugestão,
Saab.

Muito boa a série de posts, esclareceu muitas dúvidas :)

Cara, muito bom este post. Poderia tem entrado tb alguns explicações sobre o hibernate e como juntar isso no padrao MVC.continue sempre assim.
valeu !!

Saab, como vai meu amigo?

Parabéns pela iniciativa. Agora me tira uma dúvida, pelo que pude observar me parece que o hibernateTemplate faz o gerenciamento das transações, é isso mesmo? Se for, tem como eu usar o hibernateTemplate e passar o controle das transações para o Spring via anotação @Transaction?

vlw aí

Leave a comment