Developing a JPA application on JBoss AS 7

Maarten Winkels

In 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.

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:

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

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

Now, after restarting the server, we can finish the configuration using the admin console.

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:

..
                <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>
...

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 version="1.0" encoding="UTF-8" ?>
<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="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>

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).

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

}

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.

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...

Comments (7)

  1. Nikola - Reply

    July 30, 2011 at 11:32 am

    Great article. Very useful, thanks.
    One thing might worth mentioning is that one should do the postgre configuration, e.g. creating /postgres/main and the files in it on a stopped server, because if you are doing it on a running instance of JBoss 7, the folders are going to be deleted.

  2. Frédéric Chopin - Reply

    October 23, 2011 at 5:23 pm

    Thank you!... I change "postgres" for "postgresql" for not confusion.

  3. pieter - Reply

    January 7, 2012 at 2:35 am

    Dag Maarten,

    Interessant artikel. Ik heb een vraag en kan het antwoord niet vinden in diverse Hibernate en JBoss groepen. Misschien heb jij een tip?

    Ik ben met een migratie van JBoss 4.2 naar JBoss 7.0.2 bezig en heb mijn oorspronkelijke opzet van een EAR met een WAR en EJB-JAR gewijzigd in een WAR met alleen een EJB-JAR.

    Ik krijg nu, als ik vanuit een servlet de entitymanager aanroep en een JPA "find" doe op een Entity die in de EJB-JAR zit, de volgende exception:

    "org.hibernate.HibernateException: proxy handle is no longer valid".

    De Entity die ik aanroep, linkt naar andere Entities maar ik doe niets anders dan de EJB laden en doe geen query op gelinke entities. Heb jij dit wellicht bij de hand gehad?

    Alvast bedankt,
    Pieter

  4. Charles Moulliard - Reply

    February 15, 2012 at 7:54 pm

    Hi Maarteen,

    This is an really and great blog that you have published about JPA on JBoss AS7. Just a quick question, have you published your project in a GitHub repo ? Have you added a beans.xml file to allow the CDI container to inject dependencies ? I do not see in your explanations how the persistence manager is injected into the BookRepositoryBean class ? Is it done automatically by JBoss AS7 or do we have to install something for EJB 3.0 spec ?

    Regards,

    Charles M.

  5. Sandy Noa Cabrera - Reply

    September 12, 2012 at 6:51 pm

    Hi, what about the JBossAS7 configuration for Oracle ?

  6. vani - Reply

    March 21, 2013 at 8:46 pm

    I tried it but something is still wrong,
    i keep getting this error:

    java.lang.NoClassDefFoundError: org/postgresql/copy/CopyManager

    any idea?

    Anyway, thank for the contribution

Add a Comment