Skip to main content

In this article, we are going to learn how to create a REST API using Java EE 8, MicroProfile, Hibernate, PostgreSQL and the TomEE application server. It’s really easy and fun to develop Java EE 8 applications.

Tools You Will Need:

  • Maven 3.3+
  • JDK 1.8
  • PostgreSQL

Note: We will not go through the process of how to install the needed tools in this tutorial.

Generate the Project

We will use the MicroProfile starter to generate our application. Go to start.microprofile.io and enter the details as follows:

groupId: org.superbiz
artifactId: blog
MicroProfile Version: 2.0.1
MicroProfile Server: Apache TomEE 8.0.0-M2
Examples for specifications: Uncheck all examples

Open pom.xml and add the following dependencies to the <dependencies> section:

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>8.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-agroal</artifactId>
    <version>5.4.3.Final</version>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.5</version>
</dependency>

We added Java EE 8, Hibernate and PostgreSQL dependencies. In the <properties> section find the TomEE version and update it to 8.0.0-M3 and while we are at it change the tomeeClassifier to plus instead of microprofile.

TomEE Config

It’s time to start the real coding, the first thing we will do is to configure the DataSource. Create the resources.xml file inside src/main/webapp/WEB-INF/ and make it look like this:

<?xml version="1.0" encoding="UTF-8"?>
<tomee>
    <Resource id="jdbc/my_blog_datasource" type="javax.sql.DataSource">
        JdbcDriver = org.postgresql.Driver
        JdbcUrl = jdbc:postgresql://localhost/blog
        UserName = mybloguser
        Password = mypass
        jtaManaged = true
    </Resource>
</tomee>

This is the configuration for the database connection. Now create the persistence.xml inside src/main/resources/META-INF/ and make it look like this:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
             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">
    <persistence-unit name="myBlog_PU" transaction-type="JTA">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>jdbc/my_blog_datasource</jta-data-source>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="tomee.jpa.factory.lazy" value="true" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL10Dialect" />
            <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.format_sql" value="true" />
            <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
        </properties>
    </persistence-unit>
</persistence>

The persistence.xml is the standard configuration file for JPA and it has to be included in the META-INF directory.

The persistence.xml file defines what provider to be used, the name of the persistence unit, and how classes should be mapped to database tables.

We are done with the configurations and it’s time to start coding our API.

Creating Entities

Entities are POJOs (Plain Old Java Objects) class which are annotated so it can be easily persisted.

Create a new file called Post.java inside entities package and add the following:

package org.superbiz.blog.entities;

import javax.persistence.*;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Date;

@Entity
@Table(name = "posts")
@NamedQueries({
        @NamedQuery(name = "Post.findAll", query = "SELECT p FROM Post p")
})
public class Post implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(name = "title", length = 255)
    private String title;
    @Column(name = "body", length = 1000)
    @Size(min=10, max=1000)
    private String body;
    private Date createdAt;
    private Date updatedAt;
    private String author;

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @PrePersist
    private void onCreate() {
        createdAt = new Date();
    }

    @PreUpdate
    private void onUpdate() {
        updatedAt = new Date();
    }
}
  • @Entity annotation indicates that it is a JPA entity.
  • @Table annotation is used to name the table in the database.
  • @NamedQueries annotation is used to add multiple queries.
  • @NamedQuery annotation defines query with a name.
  • @Id annotation is used to define the primary key and the Id property is also annotated with @GeneratedValue to indicate that the Id should be generated automatically.
  • @Size annotation is a bean validation that validates that the value is between the attribute min and max.
  • @Column annotation is used to specify the mapped column for a persistent property.

Business Logic

Create a new file called PostRepository inside repositories package and add the following:

package org.superbiz.blog.repositories;

import org.superbiz.blog.entities.Post;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;

@Stateless
public class PostRepository {

    @PersistenceContext(unitName = "myBlog_PU")
    EntityManager em;

    public List getAllPosts() {
        return em.createNamedQuery("Post.findAll", Post.class).getResultList();
    }

    public Post findById(Long id) {
        return em.find(Post.class, id);
    }

    public void create(Post post) {
        em.persist(post);
    }

    public void update(Post post) {
        em.merge(post);
    }

    public void delete(Post post) {
        if (!em.contains(post)) {
            post = em.merge(post);
        }

        em.remove(post);
    }
}
  • @PersistenceContext annotation injects the EntityManager to be used at runtime.
  • getAll() method retrieves all the posts from the database and return the entire list.
  • findById() method finds one Post object from the database with ID and returns it.
  • update() method will update existing Post object in the database.
  • create() method will create Post object in the database.
  • delete() method will find the Post bject in the database and delete it.

Resource

The next step is to create a resource class. Create a new file called PostResource.java inside resources package and move the BlogRestApplication.java inside that package also and remove the HelloController.java because we don’t need that.


package org.superbiz.blog.resources;

import org.superbiz.blog.entities.Post;
import org.superbiz.blog.repositories.PostRepository;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.*;

@RequestScoped
@Path("posts")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PostResource {

    @Inject
    PostRepository postRepository;

    @GET
    public Response getAllPosts() {
        return Response.ok().entity(postRepository.getAllPosts()).build();
    }

    @GET
    @Path("{id}")
    public Response getPostById(@PathParam("id") Long id) {
        return Response.ok().entity(postRepository.findById(id)).build();
    }

    @POST
    public Response create(Post post, @Context UriInfo uriInfo) {
        Post postId = postRepository.create(post);
        UriBuilder builder = uriInfo.getAbsolutePathBuilder();
        builder.path(Long.toString(postId.getId()));
        return Response.created(builder.build()).build();
    }

    @PUT
    @Path("{id}")
    public Response update(@PathParam("id") Long id, Post post) {
        Post updatePost = postRepository.findById(id);

        updatePost.setTitle(post.getTitle());
        updatePost.setBody(post.getBody());
        updatePost.setAuthor(post.getAuthor());

        return Response.ok().entity(post).build();
    }

    @DELETE
    @Path("{id}")
    public Response delete(@PathParam("{id}") Long id) {
        Post post = postRepository.findById(id);
        postRepository.delete(post);
        return Response.noContent().build();
    }
}
  • @RequestScoped annotation indicates that this class will be created once every request.
  • @Path annotation identifies the URI path to which the resource responds.
  • @Produces annotation will automatically convert the response to JSON format.
  • @Consumes annotation will automatically convert the posted JSON string here into Post object.
  • We inject the PostRepository with the @Inject annotation.
  • @GET annotation maps /posts HTTP GET request to getAll() method, which will retrieve all the posts from the database and return the entire list.
  • Parameters are accessed with the @PathParam annotation.
  • @PUT annotation is for handling HTTP PUT request and it is meant to update an existing resource.
  • @POST annotation is for handling HTTP POST request and it is meant to create a new resource.
  • @DELETE annotation is for handling HTTP DELETE request.

Your directory structure should look like this:

$ tree
.
├── pom.xml
├── readme.md
└── src
    ├── main
    │   ├── java
    │   │   └── org
    │   │       └── superbiz
    │   │           └── blog
    │   │               ├── entities
    │   │               │   └── Post.java
    │   │               ├── repositories
    │   │               │   └── PostRepository.java
    │   │               └── resources
    │   │                   ├── BlogRestApplication.java
    │   │                   └── PostResource.java
    │   ├── resources
    │   │   ├── META-INF
    │   │   │   ├── microprofile-config.properties
    │   │   │   └── persistence.xml
    │   │   └── publicKey.pem
    │   └── webapp
    │       ├── WEB-INF
    │       │   ├── beans.xml
    │       │   └── resources.xml
    │       └── index.html
    └── test
        └── java

Test

Time to test our application. Open your terminal and navigate to the directory where you have the application and run the following command to build and start the TomEE application server:

$ mvn clean package && java -jar target/blog-exec.jar

Open a new terminal window and use curl to test that everything works.

GET Request

$ curl -v http://localhost:8080/data/posts

Single GET Request

$ curl -v http://localhost:8080/data/posts/3

POST Request

$ curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"title":"My First Blog Post","body":"Welcome to my first blog post, this blog runs on TomEE Application Server",
        "author": "Hayri Cicek"}' \
        http://localhost:8080/data/posts

PUT Request

$ curl -X PUT -H "Content-Type: application/json" -d '{"title":"My Updated Blog Post","body":"Welcome to my Updated blog post, this blog runs on TomEE Application Server", "author": "Hayri Cicek"}' http://localhost:8080/data/posts/3

DELETE Request

$ curl -X "DELETE" http://localhost:8080/data/posts/3

As you can see, it’s really easy and fun to create REST API’s with Java EE 8 and running on TomEE Application Server.

Hayri Cicek

Hayri Cicek

Hayri Cicek is author to the upcoming ebook Building an API Backend with MicroProfile. A passionate open source contributor and software developer from Sweden, Hayri's book is open source and hosted on Github and everybody can contribute.

3 Comments

Leave a Reply