This article further takes Part 1 of this article series to the realm of RESTful Web Services with Jersey. It contains almost no theoretical/conceptual background and delves directly into implementing one. Programmers often find implementing RESTful CRUD implementations that use EJB and ORM tools a daunting task. Here, I have tried to depict in pictorial detail the steps needed to create one. So, without further ado, let’s get our hands dirty.
Some Prerequisites
We’ll explore RESTful Web services with EJB and an ORM tool such an EclipseLink to represent data persistence. Because Tomcat 7/8 is not JEE compliant, we shall be using Glassfish 4.1 as the Application Server. The backend database would be MySQL with a JDBC type 4 driver. And, for all the required jar files, we shall depend on Maven (pom.xml). To sum up, the requirements list is as follows:
- Java SDK 8
- Netbeans 8.0.2 (bundled with Glassfish 4.1 + all we need for the project)
- MySQL 5
1. Creating a Maven Project from the List of Archetypes
Open the NetBeans IDE. Create a New Project from the File menu.
Figure 1: Creating a new project
Select Jersey-quickstart-webapp from the list of known archetypes.
Figure 2: Selecting Jersey-quickstart-webapp
Next, provide the Project Name, Group ID, and Package. Finish.
Figure 3: Providing more information
A file named pom.xml will be created automatically; it contains a list of dependent jars. Maven will download a list of dependent jars behind the scenes. We need to add a few more dependencies to use EclipseLink and EJB in our project. So, the exact pom.xml file will as follows.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance" xsi_schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.mano.bookstore</groupId> <artifactId>bookstorerestapp</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>bookstorerestapp</name> <build> <finalName>bookstorerestapp</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <inherited>true</inherited> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> <dependencyManagement> <dependencies> <dependency> <groupId>org.glassfish.jersey</groupId> <artifactId>jersey-bom</artifactId> <version>${jersey.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet-core</artifactId> <!-- use the following artifactId if you don't need --> <!-- servlet 2.x compatibility --> <!-- artifactId>jersey-container-servlet</artifactId --> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-moxy</artifactId> </dependency> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>eclipselink</artifactId> <version>2.5.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.jpa. modelgen.processor</artifactId> <version>2.5.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.1-api</artifactId> <version>1.0.0.Final</version> <type>jar</type> </dependency> <dependency> <groupId>org.jboss.spec.javax.ejb</groupId> <artifactId>jboss-ejb-api_3.2_spec</artifactId> <version>1.0.0.Final</version> <type>jar</type> </dependency> </dependencies> <properties> <jersey.version>2.21.1</jersey.version> <project.build.sourceEncoding>UTF-8 </project.build.sourceEncoding> </properties> </project>
2. Table Structure for a MySQL Database
Create a rudimentary database and table SQL DDL. For more information on how to create a database and tables in MySQL, refer to the MySQL documentation.
CREATE DATABASE bookstore2; CREATE TABLE books ( ID INT NOT NULL, COPYRIGHT VARCHAR(255), EDITIONNUMBER INT, ISBN VARCHAR(255), PRICE DOUBLE, PUBLISHER VARCHAR(255), TITLE VARCHAR(255), PRIMARY KEY (ID) ) ENGINE=InnoDB;
3. Creating a Connection Pool and JNDI Resource in Glassfish
A Java EE application grants access to databases through the JDBC API. Creating a connection pool means that the data source is registered with the application server for use by the application running within the container. There may more than one data source available; hence the name connection pool. JNDI provides the name through which the JEE application gets hold of the data source in the container at runtime.
First, create a connection pool for the MySQL datasource as follows:s
- Start Glassfish server from NetBeans.
- Open the Administrative Console in the browser with the URL http://localhost:4848/common/index.jsf.
- Select JDBC, JDBC Connection Pools. and New….
Figure 4: The JDBC Connection Pools
In the General Settings, give a Pool Name with any name, such as MySQLPool, the Resource Type as javax.sql.DataSource, and choose Database Driver Vendor as MySql. Next.
Figure 5: Choosing a database driver
Now, provide the following additional properties name value pairs and then Save.
Encoding = UTF-8
User = user1
Password = password1
ServerName = mysql
DatabaseName = bookstore2
URL = jdbc:mysql://:3306/bookstore2
Url = jdbc:mysql://:3306/bookstore2
Once the JDBC Connection Pool settings are complete, go to JDBC Connection Pools, MySQLPool, and ping to check whether the setting are all right.
Figure 6: A ping was successful
Now to create the JDBC Resource, go to JDBC, JDBC Resource, New….
Figure 7: Creating a JDBC Resource
JNDI Name = bookstore2_jndi
Pool Name = MySQLPool (The pool you have created a while ago)
In the additional property section, add a new property:
DatabaseName = bookstore2
Save and close the browser. We are done creating the JDBC Connection Pool and JDBC Resources for our MySQL database.
4. Creating a Model Class
We primarily have one model class, one Book.java. Once the table has been created in the MySQL database, the NetBeans IDE provides a feature to automatically generate entity classes from database tables. Otherwise, they can be manually coded as follows.
Book.java
package org.mano.bookstore.bookstorerestapp.model; import java.io.Serializable; import java.util.Collection; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.xml.bind.annotation.XmlRootElement; @Entity @Table(name = "books") @XmlRootElement @NamedQueries({ @NamedQuery(name = "Book.findAll", query = "SELECT b FROM Book b") }) public class Book implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private String copyright; private Integer editionnumber; private String isbn; private Double price; private String publisher; private String title; public Book() { } public Book(Integer id) { this.id = id; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCopyright() { return copyright; } public void setCopyright(String copyright) { this.copyright = copyright; } public Integer getEditionnumber() { return editionnumber; } public void setEditionnumber(Integer editionnumber) { this.editionnumber = editionnumber; } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public String getPublisher() { return publisher; } public void setPublisher(String publisher) { this.publisher = publisher; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
5. Creating EJB Classes
This class represents the business logic of the application and, in our case, the CRUD operations.
BookEJB.java
package org.mano.bookstore.bookstorerestapp.ejb; import java.util.List; importjavax.ejb.Stateless; importjavax.persistence.EntityManager; import javax.persistence.PersistenceContext; importjavax.persistence.TypedQuery; importorg.mano.bookstore.bookstorerestapp.model.Book; @Stateless public class BookEJB { @PersistenceContext(unitName = "bookstore2PU") private EntityManager entityManager; public Book getBook(int id) { return entityManager.find(Book.class, id); } public List<Book> getBooks() { TypedQuery<Book> query = entityManager.createNamedQuery ("Book.findAll", Book.class); return query.getResultList(); } public Book addNew(Book book) { entityManager.persist(book); return book; } public Book update(Book book) { Book b = entityManager.find(Book.class, book.getId()); if (b != null) { entityManager.merge(book); } return book; } public Book delete(int id) { Book b = entityManager.find(Book.class, id); if (b != null) { entityManager.remove(b); } return b; } }
6. Creating a RESTful Web Services Class
Let’s make a REST service class that exposes the CRUD APIs to the world.
BookWS.java
package org.mano.bookstore.bookstorerestapp.services; import java.util.List; import javax.ejb.EJB; import javax.ejb.Stateless; import javax.ws.rs.Consumes; 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 vjavax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.mano.bookstore.bookstorerestapp.ejb.BookEJB; import org.mano.bookstore.bookstorerestapp.model.Book; @Stateless @Path("books") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class BookWS { @EJB private BookEJB bookEJB; @GET @Path("/{id}" public Book getBook(@PathParam("id") int id){ return bookEJB.getBook(id); } @GET public List<Book> getBooks(){ return bookEJB.getBooks(); } @POST public Book addNew(Book book){ return bookEJB.addNew(book); } @PUT @Path("/{id}") public Book update(@PathParam("id") int id, Book book){ book.setId(id); return bookEJB.update(book); } @DELETE @Path("/{id}") public void delete(@PathParam("id") int id){ bookEJB.delete(id); } }
7. Using Postman as a RESTFul Client to Test CRUD APIs
Figure 8: Checking the GET request output that returns the list of Books in the database in JSON format
Now, let’s check the parametrized GET request that returns the Book with id = 2.
Figure 9: Checking the parameterized GET request
Figure 10: Inserting a new record into the database with POST
Figure 11: Updating an existing record with PUT
Figure 12: Deleting a record with DELETE
Figure 13: Confirming whether the record has been deleted with GET
Conclusion
These are some of the basic functionalities to implement CRUD operations in RESTful Web Services leveraging persistence with the help of an ORM tool and EJB. The main idea is to go hands-on rather than describing the underlying concepts of RESTful Web services. However, in doing so, I have only scratched the surface; interested readers may get started from here and explore further into the depth of APIs in the EE framework and RESTful Web Services.