Blog

Developing a JPA application on JBoss AS 7

19 Jul, 2011
Xebia Background Header Wave

as7 logoIn my previous post we setup a Maven/Eclipse project for developing RESTful web applications on JBoss AS 7. A RESTful web service that is not using a database is some what of an oddity. Therefor in this blog we’ll extend the project with JPA.

Configuration: Adding a JBoss Module

I want to use Postgres as the underlying database. To install the required JDBC driver for Postgres in the JBoss AS 7 container, we need to add a module. As I mentioned in the previous post, the module structure of JBoss is reflected on the file system.

PostgresModule

The files in the ”main” directory that constitute the module are the folloing:

  • module.xml describes the module. The content follows below.
  • postgresql-9.0-801.jdbc4.jar is the postgres JDBC4 driver.
  • postgresql-9.0-801.jdbc4.jar.index is an index file generated by JBoss Annotation Indexer when the modules are scanned.

So in order to add a module to the JBoss container, we need to write an XML file:
[xml title="module.xml"]
<module xmlns="urn:jboss:module:1.0" name="org.postgres">
<resources>
<resource-root path="postgresql-9.0-801.jdbc4.jar"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.transaction.api"/>
</dependencies>
</module>
[/xml]
Now we also need to enlist the datasource in the standalone.xml: the main configuration for JBoss. You’ll find it in the ”standalone/configuration” subdirectory of the JBoss installation. The file already has a datasource and driver for h2. So we add a new node:
[xml title="standalone.xml" highlight="9,10-13"]
<?xml version=’1.0′ encoding=’UTF-8′?>
<server name="brandhout.local" xmlns="urn:jboss:domain:1.0">

<datasources>
<datasource>

</datasource>
<drivers>
<driver name="postgres" module="org.postgres">
<xa-datasource-class>
org.postgresql.xa.PGXADataSource
</xa-datasource-class>
</driver>
<driver name="h2" module="com.h2database.h2">
<xa-datasource-class>
org.h2.jdbcx.JdbcDataSource
</xa-datasource-class>
</driver>
</drivers>
</datasources>

</server>
[/xml]
Now, after restarting the server, we can finish the configuration using the admin console.

ConfigureDataSource

The ”New Datasource” button will open up a dialog that will guide you through a few simple steps to configure the datasource.
The changes will be persisted in the standalone.xml, which will now contain a fragment like this:
[xml title="standalone.xml"]
..
<datasource jndi-name="LibraryDS" pool-name="LibraryDS_Pool" enabled="true" jta="true" use-java-context="true" use-ccm="true">
<connection-url>
jdbc:postgresql://localhost:5432/library
</connection-url>
<driver>
postgres
</driver>
<security>
<user-name>
library
</user-name>
<password>
library
</password>
</security>
</datasource>

[/xml]

Reading from the database

Now we need to configure our application to read from the database. To configure the database as a persistence context in JPA, we add a persistence.xml to our project.
[xml language="src/main/resources/persistene"]
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="https://java.sun.com/xml/ns/persistence&quot; xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance&quot;
xsi:schemaLocation="https://java.sun.com/xml/ns/persistence https://java.sun.com/xml/ns/persistence/persistence_1_0.xsd&quot;
version="1.0">
<persistence-unit name="library" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/LibraryDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
</properties>
</persistence-unit>
</persistence>
[/xml]
The java code we have to write is quite trivial JPA code mixed in with some JAX-RS annotation. For completeness they are included here below (collapsed).
[java title="com/xebia/library/model/Book.java" collapse="true"]
package com.xebia.library.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.ws.rs.FormParam;
@Entity
@SequenceGenerator(name="BOOK_SEQ", sequenceName="BOOK_SEQ")
public class Book {
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="BOOK_SEQ")
@FormParam("id")
private Long id;
@FormParam("title")
private String title;
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
}
[/java]
[java title="com/xebia/library/BookRepository.java" collapse="true"]
package com.xebia.library;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import java.util.List;
import javax.ejb.Local;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import org.jboss.resteasy.annotations.Form;
import com.xebia.library.model.Book;
@Local
@Path("books")
@Produces(APPLICATION_JSON)
public interface BookRepository {
@GET
@Path("/")
List<Book> all();
@POST
@Path("/")
Book create(@Form Book entity);
@GET
@Path("/{id}/")
Book getById(@PathParam("id") long id);
@PUT
@Path("/{id}/")
Book update(@Form Book entity);
@DELETE
@Path("/{id}/")
void remove(@PathParam("id") long id);
}
[/java]
[java title="com/xebia/library/impl/BookRepositoryBean.java" collapse="true"]
package com.xebia.library.impl;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import com.xebia.library.BookRepository;
import com.xebia.library.model.Book;
@Stateless
public class BookRepositoryBean implements BookRepository {
@PersistenceContext
private EntityManager em;
@Override
public List<Book> all() {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Book> query = builder.createQuery(Book.class);
Root<Book> root = query.from(Book.class);
query.orderBy(builder.asc(root.get("id")));
return em.createQuery(query).getResultList();
}
@Override
public Book create(Book book) {
em.persist(book);
return book;
}
@Override
public Book byId(long id) {
return em.find(Book.class, id);
}
@Override
public Book update(long id, Book book) {
return em.merge(book);
}
@Override
public void delete(long id) {
em.remove(byId(id));
}
}
[/java]
A few things to notice:

  • The JPA entity is also used as a ”backing” object for Form posts. The @FormParam annotation is used to parameters posted to the fields of the object.
  • The Repository is now also used as a local interface for the stateless bean.

After deploying the application to the server, we can test it using this simple tool.
RestClient1

Conclusion

With just a few classes we were able to deploy a RESTful web service that enables users to maintain a (rudimentary) book list. The WAR that we build is really tiny: It only contains a few classes. The application relies on JEE specifications that are implemented in the JBoss container. A few remarks about this:

  1. Developing a JEE application becomes much simpler and more fun! It feels almost like RAD web development alla Grails.
  2. Testing an application like this is non-trivial: Much of the functionality is provided by the container. With a ‘thicker’ maven project with Hibernate and Spring/Seam as dependencies, we could mimic (parts of) the deployed application in an in-build testing environment. How do we do this now? One possible answer is to use Arquillian. A future blog will have to cover that…

Of course the application we have now is far from finished. To start with, it has no user interface…

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts