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) {
throw new Exception("Nenhum resultado!");
}
}
Na consulta acima buscamos pelo nome passado por parâmetro e retornamos um Usuario
.
Foi colocado um
rollback
para 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.
Quero agradecer a ajuda do Mário Peixoto, que sanou várias dúvidas.
Olá!
Parabéns pelo post, mesmo tendo alguns aninhos me ajudou muuuuuito!
Bom, minha dúvida é o seguinte: quando faço a busca por uma valor que não existe, aparece corretamente no navegador a mensagem nenhum resultado, porém no meu console aparece o erro: Set 18, 2014 6:46:55 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [default] in context with path [/jpa] threw exception
java.lang.IllegalStateException: Transaction not active
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:47)
at br.com.caelum.vraptor.util.jpa.JPATransactionInterceptor.intercept(JPATransactionInterceptor.java:49)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
at br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:44)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:81)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:67)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
at br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:70)
at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:56)
at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Está correto ou fiz algum coisa errada?
Muito bom! Era o que eu estava procurando :D
Também curti! :)
Olá Washington muito bom o tutorial, só uma dúvida, seria possível ter em uma mesma jsp tanto o formulário de pesquisa quanto a listagem? e no caso de eu ter mais de um campo de busca como por exemplo buscar uma pessoa pelo seu cpf ou nome.como passo isso para o controller?
Gostaria de parabenizalo pelo blog, apenas estou sentindo uma pequena inatividade no mesmo...
Agora quanto a minha duvida, criei os arquivos assim como demonstrou no exemplo, porém não está sendo gerada a tabela automaticamente. Gostaria de saber se tem mais alguma dica do motivo disto ocorrer.
Washington, primeiramente parabéns pelo blog. Fiquei com uma dúvida, nesses posts sobre o vraptor vc utiliza sempre o tomcat ? Pergunto isso, pois eu trabalho com o jboss e acho mais fácil usá-lo e tirar vantagem do hibernate/JPA já embutidos no AS, além de poder fazer uso do JTA. Como seria a configuração de um projeto com VRaptor + JPA no Jboss por exemplo?
Botelhos,
[ ]'s
Oi Adalto,
Neste caso creio que você teria de fazer um
SessionFactory
para poder trabalhar com uma ou com outra.Ainda não tenho um exemplo com tal implementação, mas pretendo fazer um post sobre para mostrar como trabalhar com JDBC de forma facilitada.
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
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.
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.
Oi Alexandre,
Você registrou o provider da JPA no web.xml?
Tambem obtive este erro.
Depois de algumas pesquisas descobri que faltava uma biblioteca do hibernate.
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!
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.
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!
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:
Baixa o projeto exemplo aqui no Github pra você entender um pouco melhor. (:
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
Oi Danilo e Epitácio,
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
.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
chamadadefault
por padrão.Como faço pra alterar?
Obrigado
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
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.
é 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
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.
O codigo fonte é livre??
Infelizmente não, porém todas as funcionalidades do mesmo serão postadas e explicadas aqui no blog.
Sugestões serão bem-vindas. (:
Cade a aplicação em vRaptor que voce disse no twiiter que ia colocar no ar??
[]'s
Ainda esta em versão de teste.
Com o passar do tempo irei implementando outras funcionalidades.
http://www.moviecollection.com.br
Muito bom tutorial me ajudo a fazer a migraçao aki na giran rsrsr vlww Kotó
Show de bola!
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 !
Excelente explicação, e, melhor ainda, passo a passo: antes criando o proprio
EntityManagerUtil
, e depois desacoplando para receber como argumento! Parabéns!Massa o exemplo!
E parabéns pelo blog!
:)