Showing posts with label jersey. Show all posts
Showing posts with label jersey. Show all posts

Friday, February 19, 2010

JSON RESTfull Service Using Jersey with Hibernate Persistence on Glassfish

Tips:
  • to setup Hibernate to use a datasource defined under Glassfish, see my other post.
  • usefull Jersey annotations:
    • for the resource class: @Path("/person/")
    • for a method: @GET @Path("{personId}/") @Produces("application/json"). Then, you can use public Person getUser(@PathParam("personId") int personId) for method declaration.
    • add @Consumes("application/json") with public Person post(Person person) method declaration. Jersey takes care of unmarshaling the Json into Person object.
  • to return an error code from the restfull webservice, throw a new WebApplicationException(Response.Status.NOT_FOUND), for example. This one would return 404 error. No Need to declare the jersey handler method as "throws ...".
  • to unit test the service, use com.sun.jersey.api.client.Client:
    Person result = Client.create().resource("http://localhost:8080/myapp/person")
       .type("application/json")
       .post(Person.class,new Person("Jon Smith",133));
    Assert.assertEquals("Jon Smith", result.getName()); 
  • To pass an object retrieved via Hibernate as a response from a webservice, it needs to be serializable. But objects retrieved this way have additional members and so they need to be converted to a pure entity object (Person, in my case). I have used the Dozer library for this purpose (see the code below).
  • The entity classes (Person and PersonCollection, in my example)need to be annotated with @XmlRootElement.

Code:
Complete web service implementation class code (in RESTfull, call a resource):
package olcc.jersey.service;

import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import olcc.entity.HibernateUtil;
import olcc.entity.Person;
import olcc.entity.PersonCollection;
import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;

@Path("/person/")
public class PersonResource {
 
 @GET @Path("{personId}/") @Produces("application/json")
 public Person getPerson(@PathParam("personId") int personId) {
  Session session = HibernateUtil.getSessionFactory().getCurrentSession();
  Transaction tx = null;
  Person personClean;
  
  try{
   tx = session.beginTransaction();
   Person person = (Person) session.load(Person.class,personId);
   
   Mapper mapper = new DozerBeanMapper();
   personClean = mapper.map(person,Person.class);

   session.getTransaction().commit();

  } catch( Exception ex) {
   if( tx != null ) tx.rollback();
         throw new WebApplicationException(Response.Status.NOT_FOUND);
  }
  return personClean;
 }
 
 @GET @Produces("application/json")
 public PersonCollection get() {
  Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();
  List<person> result = session.createQuery("from Person").list();

  Mapper mapper = new DozerBeanMapper();
  PersonCollection persons = new PersonCollection();
        
  for( Person person : result ) {
    persons.add(mapper.map(person,Person.class));
  }
  session.getTransaction().commit();
        
  return persons;
 }
 
 @POST @Consumes("application/json")
 @Produces("application/json")
 public Person post(Person person) {
  Session session = HibernateUtil.getSessionFactory().getCurrentSession();
  session.beginTransaction();
  
  session.save(person);
  
  session.getTransaction().commit();
  return person;
 }

 @GET @Path("delete/{personId}/") @Produces("application/json")
 public Person deleteOne(@PathParam("personId") int personId) {
  Session session = HibernateUtil.getSessionFactory().getCurrentSession();
  session.beginTransaction();
  Transaction tx = null;
        Person personClean;
  
  try{
   tx = session.beginTransaction();
   Person person = (Person) session.load(Person.class,personId);
   session.delete(person);
   
   Mapper mapper = new DozerBeanMapper();
   personClean = mapper.map(person,Person.class);

   session.getTransaction().commit();

  } catch( Exception ex) {
   if( tx != null ) tx.rollback();
         throw new WebApplicationException(Response.Status.NOT_FOUND);
  }

  return personClean;
 }
}

Open Questions:
  • I'm not sure where the Hibernate session should be closed. I don't think this can be done at the end of each handler method.
  • How to use JTA for transaction management instead of direct JDBC transaction management?
  • Most of the Hibernate-specific code should go into a DAO, with most of it into a generic DAO. But the recommended by Hibernate documentation generic DAO is defined in terms of state-oriented data access API (makePersistent() and makeTransient() methods), which are less intuitive for me. What is the advantage of using them and how to use them from CRUD operations?