JPA e VRaptor 3

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`:

```java
@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:

```java
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:

```java
@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):

```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):**

```jsp
${message}

<c:if test="${usuario != null}">
    ID: ${usuario.id}
    Nome: ${usuario.nome}
    E-mail: ${usuario.email}
</c:if>
```

Os [componentes utilitários](http://vraptor.caelum.com.br/documentacao/componentes-utilitarios-opcionais) 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](http://mariopeixoto.wordpress.com), que sanou várias dúvidas.

### Link do projeto:

[http://github.com/wbotelhos/jpa-vraptor](http://github.com/wbotelhos/jpa-vraptor-3)