Combining JPA and JAX-RS to create RESTful service

Combining JPA and JAX-RS to create RESTful service

Submitted by Christian Crawford on Sun, 11/30/2014 - 19:45
hibernate logo

Java Persistence API (JPA) abstracts the database interactions and treats them as objects rather than simply queries. Since these queries are now objects, the principles of Object-Oriented Programming (OOP) can come into play including, encapsulation, abstraction, inheritance, and polymorphism. Persistence in the java language is actually not a new concept at all. Many implementations have been created in the past including, EDJ, JDO, Hibernate, and Toplink to name a few. JPA was designed with the idea of simplifying the Enterprise JavaBean (EJB) programming model. JPA has taken the best ideas from the different persistence providors and combines them into one stanardized specification that helps you build a layer that is independent of the providor. One of the largest features of JPA are the annotations, these annotations provide simple, yet powerful tools which help you create an application with persistence.

When creating a JPA application three things are required to build your foundation. First you need an entity class, which simply contains a method that has your fields (variables) as well as your getter and setter methods. Above each of the variables you can add annotations for keys, column names, not null attributes, and relationships. The entity object is simply a POJO class which has a @Entity annotation.

 
@Entity
@Table(name="tix_event")
public class Event {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @NotNull
  @Column(name = "id")
  private int id;
  @Column(name = "title")
  private String title;
  @Column(name = "uid")
  private int uid;
  @Column(name = "created")
  private String created;
  @Column(name="description")
  private String desc;
  
  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getTitle() {
    return title;
  }
  public void setTitle(String title) {
    this.title = title;
  }
  public int getUid() {
    return uid;
  }
  public void setUid(int uid) {
    this.uid = uid;
  }
  public String getCreated() {
    return created;
  }
  public void setCreated(String created) {
    this.created = created;
  }
  public String getDesc() {
    return desc;
  }
  public void setDesc(String desc) {
    this.desc = desc;
  }
}
  • @Entity signifies that the class is an entity class. If the table's name is different the @Table annotation is needed.
  • @Id tells the program that this field is the primary key.   
  • @Generated Value specifies a stratagey to assign a unique value to the fields tagged with it. There are four options, IDENTITY, SEQUENCE, TABLE, and AUTO.
  • The @Column is not neccesary if the field name is the same as the column in the table.

Secondly you will need a persistence.xml file. This file is what tells your program how to connect and interact with your database.

<?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_2_0.xsd"
             version="2.0">
   <persistence-unit name="Tix">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <class>com.spinspire.hibernate.entity.Event</class>
      <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=""/>
        <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/rest_test?zeroDateTimeBehavior=convertToNull"/>
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
      </properties>
   </persistence-unit>
</persistence>

 The final file needed will be the class where you implement your CRUD operations, generally referred to as the resource file. Examples of these files can be found in the related articles here and here. Below is a sample containing the GET and POST methods.

@Produces("application/json")
public abstract class ResourceBase<T> {
 
/* Methods declarations here */
 
  protected EntityManager getEntityManager() throws NamingException {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("Tix");
    return emf.createEntityManager();
  }

  @GET
  public List<T> getList() throws SQLException, NamingException { 
    List records = getTixQuery();
    return records;
  }

  @GET 
  @Path("{id}")
  public List<T> getSingle(@PathParam("id") int id) throws NamingException {
    List records = getSingleQuery(id);
    return records; 
  }

  @POST 
  public void insertTix(T t) throws NamingException, SQLException {
    createQuery(t); 
  }
}

This class has been abstracted into a base class in order to facilitate the handling of data from many different types of objects. The getEntityManager() method creates an EntityManagerFactory which is in turn used to create Entity Mangers on demand. You can ignore the annotations for now, I will explain them soon.

The specific implementation of the above methods are contained in separate files which then extend this base class.

@Path("/event")
@Produces("application/json")
public class EventResource extends ResourceBase<Event> {
 
  private EntityManager em;
  private List<Event> listEvents;
  
  @Override
  protected List<Event> getTixQuery() throws NamingException {
    em = getEntityManager();
    em.getTransaction().begin();
    listEvents = em.createQuery("SELECT e FROM Event e").getResultList();
    em.getTransaction().commit();
    em.close();
    return listEvents;
  }
 
  @Override
  protected List getSingleQuery(int id) throws NamingException {
    em = getEntityManager();
    em.getTransaction().begin();
    listEvents = singletonList(em.find(Event.class, id));
    em.getTransaction().commit();
    em.close();
    return listEvents;
  }
 
  @Override
  protected void createQuery(Event t) throws SQLException, NamingException {
    Event event = new Event();
    em = getEntityManager();
    em.getTransaction().begin();
    event.setTitle(t.getTitle());
    event.setDesc("abcd");
    event.setUid(t.getUid());
    event.setCreated("1410876904");
    em.persist(event);
    em.getTransaction().commit();
    em.close();
  }
.
.
.
}

One of the keys to persistence is the EntityManager which is associated with a persistence context. The persistence context is a set of entity instances in which, for any persistent entity identity, there is a unique instance. The entity manager API can create and remove persistence entity instances. The set of entities managed by the Entity Manger is called the persistence unit. The entity manager facilitates the interactions between your database and your program. Each method that talks to the database should get its own entity manager. JPA has its own query language known as Java Persistence Query Language (JPQL), which can be used to when custom queries are needed. For simple CRUD operations however, you can generally rely on the built in functions persist (create), find (read), and remove (delete).  More information on JPQL can be found here. Interactions with the database are called transactions and are managed by, you guessed it, the Entity Manager. Each time a transactions is called, it must be begun (getTransaction().begin()), and then after the actions have been specified the transaction must be committed (getTrasnaction.commit()). If data is being added, post and put methods, to the database then the persist(ObjName) command is called before the commit.

Since you’ve now gotten the JPA portion of your application set up you can now begin implementing the JAX-RS portion so you can use the data you retrieved in your app. As mentioned here, JAX-RS is a Java programming language API that provides support in creating web services in the REST architectural pattern. Several implementations exists including, Apache CXF, Jersey, RESTeasy, and WebSphere Application Server. In our case we will be using Oracle’s Jersey implementation. Like JPA, JAX-RS uses annotations. Some of the most commonly used ones include @Path, @GET, @POST, @PUT, @DELETE, and @Produces/Consumes The @Path annotation specifies the end point for the rest call URL. GET, PUT, POST, DELETE annotations are used to determine when each method is to be called, i.e. If a HTTP GET request is made, then the method with the @GET annotation will be called. @Produces/Consumes tells the program what type of data is expected to be either produced or consumed in that method. One more important annotation is @PathParam, this annotations allows URL parameters to be passed into the method. The code for the base resource contains several examples of the annotations.

The final project should have a structure similar to the one shown in the image below.

[[{"type":"media","view_mode":"media_original","fid":"169","attributes":{"alt":"","class":"media-image","height":"441","style":"width: 200px; height: 298px;","width":"296"}}]]

Now that you’ve gotten all of the code written you can now make sure that your code is returning the correct data. If you are using NetBeans then you can click the run button to fire up jetty. Once jetty is fully set up open your browser and navigate to http://localhost:8080/api/event. If everything is working correctly you should see the data from your database in a JSON format.

[[{"type":"media","view_mode":"media_original","fid":"168","attributes":{"alt":"","class":"media-image","height":"781","style":"line-height: 1.538em; width: 1280px; height: 720px;","width":"1631"}}]]

In order to test the other crud operations open your preferred terminal shell and try the following curl commands

curl -X POST -H "Content-Type:application/json" -d '{"title":"Event123","uid":42}' -i localhost:8080/api/event
curl -X PUT -H 'Content-Type:application/json' -d '{"title":"Event123","description":"testDesc","uid":2,"created":1417381350}' -i localhost:8080/api/event/7
curl -X DELETE -H -i localhost:8080/api/event/16

The completed project can be downloaded from here.

Christian Crawford

Profile picture for user Christian Crawford
Senior Engineering Manager & Lead Software Developer
  • Drupal site building, module development, theming (since Drupal 7)
  • Cloud Infrastructure (AWS, Azure, Google)
  • Docker & Kubernetes
  • SQL (MySQL and Oracle), NoSQL (MongoDB)
  • ReactJS, Svelte, jQuery, NodeJS
  • HTML, CSS, SASS/LESS
  • Nginx and Apache Stacks