Archive for the ‘Hibernate’ Category

JPA and VRaptor 3

Updated at January 5th 2011.

We know that nowadays is essential to have a persistence framework to avoid the hard work of to create tables, key exchange and so one. If you want productivity and incredibles features, I can say that Hibernate is a great choice.

Objective:

Create a project in which we configure and use the API JPA with automated transaction control with the VRaptor 3.

We’ll start by creating a configuration file for our database.

Creating the configuration file (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>

Line 04: have a persistence unit called default;
Line 05: we use the Hibernate provider;
Line 08: we use the MySQL database and its driver;
Line 09-10: the database use the username and password as “root“;
Line 11: connection localhost with default port 3306 in the schema “banco“;
Line 12: the dialect used is the MySQL’s InnoDB;
Line 14: we using the HBM to generate the tables automatically according to the entities annotated with @Entity. The value update add or update the columns in the database when you modifies the entity, but it’s not remove the columns.

Importantly, you must use InnoDB because it is transactional unlike the MyISAM.

Our setup is ready, but we need a class that use these settings and do our application to connect to the database.

In principle we would create a EntityManagerUtil like this:

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

}

And get the DAOs in our connection as following:

@Component
public class UserDao {

    private EntityManager manager = EntityManagerUtil.getEntityManager();
    ...

}

Through our utility class would be possible to receive the EntityManager and the Transaction either, but we’ll have something undesirable, the coupling.

Our DAO is worrying about how to get it and this code is tied to the class itself. The ideal would be to receive this object through a set method, constructor or dependency injection and int this point VRaptor comes to help us. (:

In the place of the class EntityManagerUtil we’ll register a optional component.

Creating the CustomProvider component:

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

}

Our class extends SpringProvider and override the method registerCustomComponents to register the EntityManagerCreator, FactoryCreator and JPATransaction providing us the EntityManager in a transparent way.

After created this class, we’ll register it as a provider in web.xml:

<context-param>
   <param-name>com.caelum.vraptor.provider</param-name>
   <param-value>com.wbotelhos.provider.CustomProvider</param-value>
</context-param>

Using our provider we can decide if we’ll register the transaction control component or not. But the VRaptor still makes more for us, and as expected we would want all these components, VRaptor already included in his util other package called jpa which already register these three components that replaces the CustomProvider just changing the configuration of the web.xml to point it:

<context-param>
    <param-name>br.com.caelum.vraptor.packages</param-name>
    <param-value>br.com.caelum.vraptor.util.jpa</param-value>
</context-param>

Now you can remove all begin and commit of the code because VRaptor will worry about it for you. As for rollback you need to keep it, because when an error is throwed on any operation, all of other will be reverted, since VRaptor keep this actions set in a single transaction.

Ok! Now VRaptor already know should seek and use our persistence.xml, which must be located in classpath within src/META-INF.

VRaptor by default searchs for a persistence-unit called “default“. Make sure you have created it with this name: <persistence-unit name="default"

Then the DAO receives by injection the EntityManager avoiding the high coupling:

@Component
public class UsuarioDao {

    private EntityManager manager;

    UsuarioDao (EntityManager manager) {
        this.manager = manager;
    }

    ...

}

Try to use a generic DAO, avoiding duplication of code.

Creating the database schema and populating it:

There is a file, banco.sql, within the project that have the SQL code to create and populate the database, but if you prefer, just run the application and Hibernate will create the entire database due to SchemaExport (hbm2ddl), leaving for you only the task of populating it.

Let’s create the entity Usuario:

@Entity
public class Usuario {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String nome;
    private String email;

    // getters and setters

}

We said that Usuario is a entity and the id is a key incremented by the database.

Now we can create a method to put the Hibernate in action:

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) {
        manager.getTransaction().rollback();
        throw new Exception("Nenhum resultado!");
    } catch (Exception e) {
        manager.getTransaction().rollback();
        throw new Exception("Ocorreu um erro na busca!");
    }
}

In the above query we searched for the name passed as parameter and was returned an Usuario.

Were placed a rollback just to illustrate its use, although it is not necessary in this case.

Let’s create the controller to call this method and provide the result on screen:

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

}

We injected the resources we need and called the method that return a user with the same name of the parameter.

Finally we create the query and result pages.

Query page (index/index.jsp):

<form action="<c:url value='/usuario/consultar'/>" method="get">
    Consultar: <input type="text" name="entity.nome"/>
    <input type="submit"/>
</form>

Result page (user/result.jsp):

${message}

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

The utility components of the VRaptor also has other features such as Hibernate Session and SessionFactory, and I believe it will increasingly make our lives easier.

See this code running on: http://moviecollection.com.br

Thanks to Mário Peixoto that explained several doubts.

Project link:

http://github.com/wbotelhos/jpa-vraptor-3

Share