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);
}
}