Java Persistence API (JPA) Plugin

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:

  1. Registrar o plugin no arquivo de configuração vraptor.xml
  2. Mapear suas classes persistentes com as anotações da Java Persistence API
  3. Escolher uma das implementações da JPA
  4. Configurar o mecanismo de persistência escolhido no arquivo META-INF/persistence.xml
  5. Gerenciar os seus objetos persistentes (criar, remover, buscar, etc) nas suas lógicas.

vraptor.xml

Abra o seu arquivo vraptor.xml e registre o plugin:

<vraptor>
        ...
        <plugin>org.vraptor.plugin.jpa.JavaPersistencePlugin</plugin>
        ....
</vraptor>

Mapeando suas classes persistentes

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.

Escolhendo a Implementação da JPA

Existem duas principais implementações da JPA (mecanismo de persistência):

  1. Hibernate implementa a JPA através de três subprojetos: hibernate-core, hibernate-annotations e hibernate-entitymanager.
  2. Oracle Toplink Essentials

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.

Configurando: o arquivo persistence.xml

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>

Gerenciando seus objetos persistentes

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.

Por que minha persistence unit precisa chamar "default"?

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();
    }
    // ...
}

Gerenciando automaticamente as transações

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