Spring Framework Parte 4 -> Integração com o Hibernate
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):
- Spring Framework Parte 1 -> Dependency Injection
- Spring Framework Parte 2 -> Aspect Oriented Programming
- Spring Framework Parte 3 -> Java Database Connectivity (JDBC)
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…
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..
Até a próxima,
Saab.

9 Responses so far
Carol
outubro 11th, 2010
17:01
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!
Saab
outubro 12th, 2010
19:33
Obrigado pelo retorno positivo Carol, fico feliz que tenha gostado.
Sobre o Spring MVC, este será um dos próximos tópicos abordados aqui, pode ficar tranquila.
=]
Robson
janeiro 31st, 2011
7:49
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
Renato
fevereiro 24th, 2011
22:22
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.
Adriano
abril 14th, 2011
14:22
Muito bom o post, porém, ficou devendo a parte anotada (AnnotationSessionFactoryBean).
Poderia complementar.
Saab
abril 19th, 2011
21:52
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!
Sinceramente acho que tal assunto merece um post próprio, falando de Hibernate Annotations.
[]s e obrigado pela sugestão,
Saab.
Fredy
setembro 21st, 2011
9:32
Muito boa a série de posts, esclareceu muitas dúvidas
Anderson
outubro 21st, 2011
12:07
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 !!
William
janeiro 20th, 2012
10:04
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