Através deste plugin, o VRaptor2 gerencia o ciclo de vida do EntityManager, principal componente da especificação de persistência do Java EE 5: a Java Persistence API, ou simplesmente JPA. Isto significa que o VRaptor2 será responsável por decidir quando criá-lo e quando destruí-lo.
O plugin ainda resolve os problemas apontados no excelente artigo do hibernate hibernate open-session-in-view, deixando o seu javax.persistence.EntityManager ativo até que a página seja renderizada e evitando o famoso LazyInitializationException.
Se a sua lógica jogar uma exceção, qualquer transação em execução será cancelada (rollback).
Se você desejar, o plugin pode gerenciar as transações também. Logo antes da lógica ser executada, uma transação será iniciada (transaction.begin()) e logo após a lógica terminar será feito o commit da transação (transaction.commit()).
Passos simples para o sucesso:
Abra o seu arquivo vraptor.xml e registre o plugin:
<vraptor> ... <plugin>org.vraptor.plugin.jpa.JavaPersistencePlugin</plugin> .... </vraptor>
Mais detalhes sobre mapeamento com JPA podem ser encontrados na excelente documentação do hibernate annotations. O mapeamento através de arquivos xml, definido na especificação JPA também pode ser usado.
Como exemplo, segue o mapeamento através de anotações de uma simples classe:
@Entity public class User { @Id @GeneratedValue private Long id; private String name; private String email; private String password; //getters and setters }
Este é o exemplo mais simples possível.
@Entity diz ao mecanismo de persistência que esta é uma classe persistente. @Id marca a chave primária e @GeneratedValue diz ao mecanismo de persistência para gerar as chaves primárias automaticamente (geralmente delegando esta tarefa ao banco de dados).
Existem muitas outras anotações. Para mais detalhes leia a documentação da JPA no tutorial oficial do Java EE 5.
Existem duas principais implementações da JPA (mecanismo de persistência):
Ambos são opensource e largamente adotados. Existem outras alternativas como BEA Kodo e Apache OpenJPA. O VRaptor2 com este plugin funcionará com qualquer implementação compatível com a especificação JPA.
Você deve escolher apenas uma implementação e colocar os jars necessários no seu diretório WEB-INF/lib.
A especificação Java Persistence API requer um arquivo META-INF/persistence.xml para configuração.
Você precisa ter o arquivo persistence.xml dentro do diretório META-INF, que deve estar no seu classpath. A forma mais fácil é deixar o diretório META-INF na pasta dos seus fontes e criar o arquivo persistence.xml dentro dele.
Detalhes sobre o persistence.xml podem ser encontrados na documentação da JPA.
O plugin de persistência para o VRaptor2 requer apenas que a sua persistence unit seja chamada default.
Aí vai um simples exemplo de configuração para hibernate com o banco de dados HSQLDB:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="default"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/> <property name="hibernate.connection.username" value="sa"/> <property name="hibernate.connection.password" value=""/> <property name="hibernate.connection.url" value="jdbc:hsqldb:."/> <property name="hibernate.max_fetch_depth" value="3"/> </properties> </persistence-unit> </persistence>
E outro simples exemplo para o Oracle Toplink Essentials com o banco de dados MySQL:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd" version="1.0"> <persistence-unit name="default"> <provider>oracle.toplink.essentials.PersistenceProvider</provider> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <property name="toplink.ddl-generation" value="drop-and-create-tables" /> <property name="toplink.logging.level" value="FINE" /> <property name="toplink.jdbc.url" value="jdbc:mysql://localhost/myDB" /> <property name="toplink.jdbc.driver" value="com.mysql.jdbc.Driver" /> <property name="toplink.jdbc.user" value="root" /> <property name="toplink.jdbc.password" value="" /> </properties> </persistence-unit> </persistence>
Basta dizer ao VRaptor2 para injetar um EntityManager nos seus componentes.
Através da anotação da JPA @PersistenceContext:
import org.vraptor.annotations.Component; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Component public class UserLogic { @PersistenceContext private EntityManager entityManager; public void save(User user) { this.entityManager.getTransaction().begin(); this.entityManager.persist(user); this.entityManager.getTransaction().commit(); } public void update(User user) { this.entityManager.getTransaction().begin(); this.entityManager.merge(user); this.entityManager.getTransaction().commit(); } // ... }
Ou através da injeção de dependências pelo construtor:
import org.vraptor.annotations.Component; import javax.persistence.EntityManager; @Component public class UserLogic { private EntityManager entityManager; public UserLogic(EntityManager entityManager) { this.entityManager = entityManager; } public void save(User user) { this.entityManager.getTransaction().begin(); this.entityManager.persist(user); this.entityManager.getTransaction().commit(); } public void update(User user) { this.entityManager.getTransaction().begin(); this.entityManager.merge(user); this.entityManager.getTransaction().commit(); } // ... }
Simples, não?
Estes são apenas exemplos simples. A JPA é muito mais poderosa e você ainda deveria encapsular a lógica de acesso aos dados dentro dos seus DAOs.
Esta é apenas uma convenção adotada pelo plugin, seguindo o princípio do VRaptor2 de favorecer as convenções ao invés de configurações (conventions over configuration).
Você ainda pode usar outro nome para a sua persistence unit, e injetar um EntityManager desta persistence unit através da anotação @PersistenceContext:
@Component public class UserLogic { @PersistenceContext(unitName="otherName") private EntityManager entityManager; public void save(User user) { this.entityManager.getTransaction().begin(); this.entityManager.persist(user); this.entityManager.getTransaction().commit(); } // ... }
Você só precisa dizer ao VRaptor2 que o seu método de lógica precisa de uma transação, @Transaction(required=true). Caso nada seja especificado, o comportamento padrão será assumido: "a transação não é necessária".
Uma nova transação será iniciada logo antes da lógica começar a ser executada (transaction.begin()). Logo após o término da execução da lógica será feito o commit da transação (transaction.commit()).
Este é o caso mais comum para aplicações web. Se ele não serve a você, simplesmente não peça ao plugin para gerenciar a sua transação e gerencie-a da forma mais adequada ao seu caso com entityManager.getTransaction().
Se a sua lógica lançar uma exceção, qualquer transação em execução (aberta pelo VRaptor2 ou não) será cancelada (rolback).
Exemplo:
import org.vraptor.annotations.Component; import org.vraptor.plugin.jpa.Transaction; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Component public class UserLogic { @PersistenceContext private EntityManager entityManager; @Transaction(required=true) public void save(User user) { this.entityManager.persist(user); } }