Atualizado em 4 de Janeiro de 2011.
Sabemos que hoje em dia é fundamental termos uma framework de persistência nos poupando o trabalho de criação de tabelas, trocas de chave e afins. Se você quer produtividade e um conjunto incrível de funcionalidades, posso dizer que o Hibernate é uma ótima escolha.
Objetivo:
Criar um projeto no qual iremos configurar e utilizar a API JPA com controle de transação automatizada junto ao VRaptor 3.
Começaremos criando um arquivo de configuração para o nosso banco de dados.
Criando o arquivo de configuração (persistence.xml):
<?xml version="1.0" encoding="UTF-8"?>
<persistence>
<persistence-unit name="default">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="root"/>
<property name="hibernate.connection.url" value="jdbc:mysql://127.0.0.1:3306/banco"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
Linha 4: temos uma unidade de persistência chamada default;
Linha 5: usamos o provider do Hibernate;
Linha 8: utilizamos o banco de dados MySQL e seu driver;
Linha 9-10: o banco esta com nome de usuário e senha “root“;
Linha 11: conexão localhost, porta padrão 3306 no schema “banco“;
Linha 2: o dialeto usado é o InnoDB do MySQL;
Linha 14: usamos o HBM para gerar as tabelas de automáticamente de acordo com as entidades anotadas com @Entity. O valor update adiciona colunas no banco à medida que você adiciona ou altera a entidade, porém não as retira caso deixem de existir.
É importante ressaltar que deverá ser usado o InnoDB, pois ele é transacional, ou seja, é possível trabalhar com trasações ao contrário do MyISAM.
Nossa configuração esta pronta, porém precisamos de uma classe que utilize estas configurações e possibilite nossa aplicação conectar ao banco.
À princípio criaríamos um EntityManagerUtil desta forma:
public class EntityManagerUtil {
private static EntityManagerFactory emf;
public static EntityManager getEntityManager() {
if (emf == null) {
emf = Persistence.createEntityManagerFactory("default");
}
return emf.createEntityManager();
}
public static void close() {
emf.close();
}
}
E em nossos DAOs pegaríamos a conexão da seguinte forma:
@Component
public class UsuarioDao {
private EntityManager manager = EntityManagerUtil.getEntityManager();
...
}
Através da nossa classe utilitária seria possível obter o EntityManager e assim a Transaction, mas teríamos algo indesejado, o acoplamento.
Nosso DAO esta se preocupando com a forma de obtê-lo e este código esta amarrado à própria classe. O ideal seria receber este objeto, através de um método set, construtor ou injeção de dependência e é ai que o VRaptor entra para nos ajudar. (:
No lugar dessa classe EntityManagerUtil vamos registrar um componente opcional do VRaptor.
Criando o componente CustomProvider:
public class CustomProvider extends SpringProvider {
@Override
protected void registerCustomComponents(ComponentRegistry registry) {
registry.register(EntityManagerCreator.class, EntityManagerCreator.class);
registry.register(EntityManagerFactoryCreator.class, EntityManagerFactoryCreator.class);
registry.register(JPATransactionInterceptor.class, JPATransactionInterceptor.class);
}
}
Nossa classe estende SpringProvider e sobrescreve o método registerCustomComponents para registrar o EntityManagerCreator, FactoryCreator e JPATransaction nos disponibilizando assim o EntityManager de forma transparente.
Depois de criar esta classe, devemos registrá-la como um provider no web.xml:
<context-param> <param-name>br.com.caelum.vraptor.provider</param-name> <param-value>br.com.wbotelhos.provider.CustomProvider</param-value> </context-param>
Usando o nosso provider podemos decidir se iremos registrar o componente de controle de transação ou não. Mas o VRaptor ainda faz mais por nós e como já era esperado que iríamos querer todo estes componentes, o VRaptor já incluiu em seu pacote util um outro pacote chamado jpa que já registra estes três componentes e que por sua vez substituirá a nossa CustomProvider bastando trocar o registro do web.xml para apontar para o mesmo:
<context-param>
<param-name>br.com.caelum.vraptor.packages</param-name>
<param-value>br.com.caelum.vraptor.util.jpa</param-value>
</context-param>
Agora você pode retirar qualquer begin e commit do seu código, pois o VRaptor irá se preocupar com isso por você. Quanto ao rollback você precisa de mantê-lo, pois se der um erro em alguma operação, todas as anteriores serão desfeita, já que o VRaptor irá manter este conjunto de ações em uma única transação de forma transparente.
Pronto! Agora o VRaptor já sabe que deve buscar e utilizar o nosso persistence.xml, que deve estar localizado no classpath dentro de src/META-INF.
O VRaptor por padrão procura por uma persistence-unit chamada “default“. Certifique-se de ter criado com este nome:
<persistence-unit name="default"…
Com isso o DAO recebe por injeção o EntityManager evitando o alto acoplamento:
@Component
public class UsuarioDao {
private EntityManager manager;
UsuarioDao (EntityManager manager) {
this.manager = manager;
}
...
}
Procure se possível utilizar um DAO genérico, evitando repetições de código.
Criando o schema e populando o banco:
Existe um arquivo, banco.sql, dentro do projeto já com o código SQL para criar e popular o banco, mas se preferir poderá rodar a aplicação e o Hibernate tratará de criar todo o banco devido ao SchemaExport (hbm2ddl), ficando-lhe apenas a tarefa de populá-lo.
Vamos criar uma entidade Usuario:
@Entity
public class Usuario {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nome;
private String email;
// getters and setters
}
Dissemos que Usuario é uma entidade e que id é a chave, sendo esta incrementada pelo banco.
Com a entidade criada, podemos criar um método para colocarmos o Hibernate em ação:
public Usuario loadByNome(String nome) throws Exception {
try {
Query query = manager.createQuery("from Usuario where nome = :nome");
query.setParameter("nome", nome);
Usuario usuario = (Usuario) query.getSingleResult();
return usuario;
} catch (NoResultException e) {
manager.getTransaction().rollback();
throw new Exception("Nenhum resultado!");
} catch (Exception e) {
manager.getTransaction().rollback();
throw new Exception("Ocorreu um erro na busca!");
}
}
Na consulta acima buscamos pelo nome passado por parâmetro e retornamos um Usuario.
Foi colocado um
rollbackpara ilustrar o uso, apesar de não ser necessário neste caso.
Vamos criar o controller para fazer a chamada desse método e disponibilizar o resultado em tela:
@Resource
public class UsuarioController {
private Result result;
private UsuarioDao usuarioDao;
public UsuarioController(Result result, UsuarioDao usuarioDao) {
this.result = result;
this.usuarioDao = usuarioDao;
}
@Get
@Path("/usuario/consultar")
public Usuario consulta(Usuario entity) {
try {
return usuarioDao.loadByNome(entity.getNome());
} catch (Exception e) {
result.include("message", e.getMessage());
}
return null;
}
}
Fizemos a injeção dos recursos que necessitamos e chamamos o método que nos retorna um usuário de acordo com o nome passado.
Por fim iremos criar as páginas de consulta e resultado.
Página de consulta (index/index.jsp):
<form action="<c:url value='/usuario/consultar'/>" method="get">
Consultar: <input type="text" name="entity.nome"/>
<input type="submit"/>
</form>
Página de resultado (usuario/resultado.jsp):
${message}
<c:if test="${usuario != null}">
ID: ${usuario.id}
Nome: ${usuario.nome}
E-mail: ${usuario.email}
</c:if>
Os componentes utilitários do VRaptor ainda possui outros recursos como Hibernate Session e SessionFactory, e creio que cada vez mais irá facilitar nossas vidas.
Veja este código funcionando em: http://MovieCollection.com.br
Quero agradecer a ajuda do Mário Peixoto, que sanou várias dúvidas.
Link do projeto:
Kleber Pinel
novembro 21st, 2011
Antes de tudo, parabens pelo Post.. mt bom.
Eu consegui fazer 100% do que você propos no post, mas agora estou tentando fazer a mesma coisa mas usando o servlet 3 (jee6) mas ai nao sei como configurar pra subir a JPA… eu até reabri um topico parecido que tinha no GUJ
http://www.guj.com.br/java/196361-resolvido-vraptor-31–jee6–hibernate#1349459
se puder me ajudar, agradeço.
Obrigado
wbotelhos
novembro 26th, 2011
Oi Kleber,
Como esta usando Hibernate e não JPA, é necessário o hibernate.cfg.xml e não o persistence.xml.
Sendo assim o Session será injetado em vez do EntityManager.
Pessoalmente prefiro usar JPA, até porque a JPA 2 já tem Criteria, além disso, muito das vezes um HQL é mais simples de escrever e ender que um Criteria.
wbotelhos
novembro 26th, 2011
Oi Kleber,
Como esta usando Hibernate e não JPA, é necessário o hibernate.cfg.xml e não o persistence.xml.
Sendo assim o Session será injetado em vez do EntityManager.
Pessoalmente prefiro usar JPA, até porque a JPA 2 já tem Criteria, além disso, muito das vezes um HQL é mais simples de escrever e entender que um Criteria.
Alexandre Coelho
novembro 10th, 2011
Botelhos,
Segui seu tutorial e criei a aplicação no Eclipse. Criei uma pasta META-INF dentro da pasta src e coloquei o persistence.xml dentro dessa pasta META-INF. Meu persistence-unit name é “default”, conforme o padrão do Vraptor 3. Quando compilo, a seguinte mensagem de erro é exibida: Caused by: javax.persistence.PersistenceException: No Persistence provider for EntityManager named default.
Não sei o que pode estar errado, pois coloquei o persistence.xml no local certo com o persistence-unit name com o nome correto.
wbotelhos
novembro 16th, 2011
Oi Alexandre,
Você registrou o provider da JPA no web.xml?
Germano Fronza
março 28th, 2011
Muito bom o seu artigo, parabéns.
Queria te perguntar se você já fez deploy de uma aplicação assim no glassfish 3.1 (versão recentemente liberada). Quando a minha aplicação estava no glassfish 3.0.x eu não tinha problema com o JPATransactionInterceptor, mas agora nesta versão nova, a cada requisição para uma lógica do VRaptor eu vejo a exceção SQLException: Can’t call commit when autocommit=true.
Já setei o autocommit do hibernate (estou usando JPA 2) para false, mas ele ignora, pois “hibernate.connection.autocommit = false break the EJB3 specification”. Estou a ponto de desabilitar logs de erro no hibernate, mas não é nada legal fazer isso… Thnks!
wbotelhos
abril 1st, 2011
Oi Germano,
Infelizmente não sei te falar, pois nunca utilizei o Glassfish.
Estranho ele não aceitar o autocommit = false.
Tenta implementar o seu próprio OpenSessionInView para testar: http://vraptor.caelum.com.br/documentacao/interceptadores
E desabilitar o log de longe não é algo legal.
Amanda
janeiro 5th, 2011
Parabéns pelo blog.
Adorei achar exemplos utilizando vRaptor, pois preciso fazer um projeto e não sei nem por onde começar.
Muito bom seu post, mas ainda não ficou claro pra mim. Você diz criar isso ou configura aquilo, mas não sei onde fica cada um. Não sei como é a estrutura de um projeto com vRaptor.
Por exemplo: criando o componente CustomProvider. Não sei onde tenho que criar esse componente.
Estou meio perdida, ficaria grata se pudesse me ajudar.
Obrigada!
wbotelhos
janeiro 5th, 2011
Oi Amanda,
Criar manualmente o CustomProvider é algo que não precisamos fazer mais, porém você poderia deixá-lo em qualquer lugar da sua aplicação, já que o local do mesmo é indicado posteriormente no web.xml. Hoje basta registrar no web.xml o pacote do VRaptor que já tem esses provider:
<context-param> <param-name>br.com.caelum.vraptor.packages</param-name> <param-value>br.com.caelum.vraptor.util.jpa</param-value> </context-param>Baixa o projeto exemplo aqui no Github pra você entender um pouco melhor. (:
Danilo Akamine
novembro 19th, 2010
Muito bom o artigo, parabéns.
Fiquei com a mesma dúvida do Epitácio Jr.
Inicialmente também não achei essa resposta na documentação do VRaptor.
Obrigado
wbotelhos
novembro 26th, 2010
Oi Epitácio e Júnior,
Não existe isso de forma configurável e fácil no VRaptor, infelizmente.
Para isso ser possível você precisaria implementar o seu próprio
EntityManagerFactoryCreator.Acho que não vale o trabalho, sendo que seria mais fácil trocar o nome do seu
persistence-unit.Epitácio Júnior
novembro 9th, 2010
Muito bom o artigo!
era o que eu tava procurando para trabalhar com o VRaptor.
Mas me surgiu uma dúvida, vc disse que por padrão o VRaptor procura o persistence-unit chamada “default“ por padrão.
Como faço pra alterar?
Obrigado
Washington Machado
junho 20th, 2010
Muito bom o artigo.
Como estou começando ainda surgem algumas dúvidas bobas.
O Hibernate é um framework de persistência ORM que surgiu antes mesmo da JPA, e quando esta surgiu o hibernate a implementou. Correto isso?
Há também pessoas que utilizam o hibernate.cfg.xml ou hibernate.properties como arquivos de configuração. Quando eu trabalho com o persistence.xml significa que estou usando o hibernate como implementação da JPA e quando uso as outras configurações que sitei significa que estou usando o hibernate “isoladamente”? Ou não existe diferença alguma? (Não sei se defini muito bem) Há vantagens claras entre uma forma e outra de se trabalhar?
Uma sugestão de post seria a criação de uma aplicação com o Flexigrid (baseado no seu comentario acima hehe).
Obrigado! []s
wbotelhos
junho 20th, 2010
Fala @Washington,
O que você disse esta correto, o hibernate veio primeiro e hoje implementa a JPA.
Sim, o persistence.xml é utilizado quando estamos utilizando JPA e o hibernate.cfg.xml quando estamos utilizando a implementação do Hibernate isoladamente.
Até um tempo atrás eu defendia o Hibernate por ter a Criteria, porém hoje temo isso na JPA 2.0, apesar do Tomcat ainda não ter uma versão de suporte para a mesma. Mas ainda sim creio que utilizar a JPA é mais seguro por ser a especificação padrão e com isso nos garantir compatibilidade sempre.
A aplicação com Flexigrid estava em rascunho, mas passei a bola para o meu amigo Makoto.
Aprendi muito com ele e garanto que irá aprender também. (:
Obrigado pelas leituras e feedback.
Bruno
março 26th, 2010
é uma ajuda também.
gostaria de saber como fazer a parte visual com vRaptor, ele não tem aquelas tags do JSF com componentes visuais correto?
Não sou design como faço o visual da minha aplicação com vRaptor
É htlm puro?
Como ganho produtividade com vRaptor?
Uma sugestão é fazer uma serie de post sobre a integração de spring, jpa/hibernate e vraptor e Visão.
[]’s
wbotelhos
março 26th, 2010
O VRaptor não tem os componentes visuais como JSF, porém você pode utilizar o jQueryUI. Outros componentes baseados no jQuery são achados aos montes na net, como o Flexigrid entre outros.
A produtividade do VRaptor esta no código java, no trabalho com as camadas MVC através das convenções, injeção de dependência, casting automáticos, interceptors entre outras funcionalidades que estão melhores explicadas no site do VRaptor.
A parte de JPA/Hibernate e Visão utilizando jQuery e algumas outras frameworks irão aparecer por aqui sempre. Apenas o Spring que por enquanto não terá exemplos por eu não estar trabalhando muito com ele.
Obrigado pelo feedback Bruno.
Bruno
março 24th, 2010
O codigo fonte é livre??
wbotelhos
março 24th, 2010
Infelizmente não, porém todas as funcionalidades do mesmo serão postadas e explicadas aqui no blog.
Sugestões serão bem-vindas. (:
Bruno
março 17th, 2010
Cade a aplicação em vRaptor que voce disse no twiiter que ia colocar no ar??
[]‘s
wbotelhos
março 17th, 2010
Ainda esta em versão de teste.
Com o passar do tempo irei implementando outras funcionalidades.
http://www.moviecollection.com.br
Junior
fevereiro 25th, 2010
Muito bom tutorial me ajudo a fazer a migraçao aki na giran rsrsr vlww Kotó
Fernando
fevereiro 24th, 2010
Show de bola!
mauriciovoto
fevereiro 24th, 2010
Parabéns pelo post !
Qualquer divulgação de VRaptor integrado com outros frameworks é muito útil, pois ainda é um pouco difícil encontrar material de VRaptor 3 !
Paulo Silveira
fevereiro 23rd, 2010
Excelente explicação,e, melhor ainda, passo a passo: antes criando o proprio EntityManagerUtil, e depois desacoplando para receber como argumento! Parabens!
Loiane
fevereiro 23rd, 2010
Massa o exemplo!
E parabéns pelo blog!
:)