Build REST API

As stated in before posts, we are going to build a Blog system.

To demonstrate REST API, we use a simple Post entity to persist blog entries, and expose the CRUD operations via REST APIs to client applications. As a REST API consumer, the client applications could be a website, a desktop application, or a mobile application.

Following the REST API convention and HTTP protocol specification, the post APIs can be designed as the following table.

Uri

Http Method

Request

Response

Description

/posts

GET

200, [{'id':1, 'title'},{}]

Get all posts

/posts

POST

{'title':'test title','content':'test content'}

201

Create a new post

/posts/{id}

GET

200, {'id':1, 'title'}

Get a post by id

/posts/{id}

PUT

{'title':'test title','content':'test content'}

204

Update a post

/posts/{id}

DELETE

204

Delete a post

Next, we begin to create the domain models: Post.

Modeling the blog application

As planned in Overview, there are some domain objects should be created for this blog sample application.

A Post model to store the blog entries posted by users. A Comment model to store the comments on a certain post. A User model to store users will user this blog application.

Every domain object should be identified. JPA entities satisfy this requirement. Every JPA entity has an @Id field as identifier.

A simple Post entity can be designated as the following. Besides id, it includes a title field , a content field, and createdDate timestamp, etc.

@Entity
@Table(name = "posts")
public class Post implements Serializable {

    @Id()
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "title")
    private String title;

    @Column(name = "content")
    @Size(max = 2000)
    private String content;

    @Column(name = "created_date")
    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;

    //getters and setters, hashcode, equals, toString etc. are omitted
}

It is a standard JPA entity.

  • An entity class must be annotated with @Entity

  • An entity should implement Serializable interface

  • An entity must include an identifier field(annotated with @Id), such as id of Post.

  • An entity should have a default none-arguments constructor. By default, if there is no explicit constructor declaration, there is an implicit none-argument constructor. If there is a constructor accepts more than one arguments, you have to add another none-argument explicitly.

    For example.

Optionally, it is recommended to implement your own equals and hashCode methods for every entities, if there is a requirement to identify them in a collection.

For example, there are a post existed in a collection, adding another Post into the same collection should check the post existance firstly.

The title field can be used to identify two posts in a collection, because they are not persisted in a persistent storage at the moment, id value are same--null.

Implements equals and hashCode with title field of Post.

When an entity instance is being persisted into a database table, the id will be filled.

In JPA specification, there is a sort of standard id generation strategies available.

By default, it is AUTO, which uses the database built-in id generation approache to assign an primary key to the inserted record.

WARNING: Every databases has its specific generation strategy, if you are building an application which will run across databases. AUTO is recommended.

Other id generation strategies include TABLE, IDENTITY. And JPA providers have their extensions, such as with Hibernate, you can use uuid2 for PostgresSQL.

Lombok

Lombok is a greate helper every Java developer should use in projects. Utilize Java annotation processor, it can generate getters, setters, equals, hashCode, toString and class constructor at compile runtime with some Lombok annotations.

Add @Data to Post class, you can remove all getters and setters, and equals, hashcode, toString methods. The code now looks more clean.

There are some annotations provided in Lombok to archive this purpose.

@Data is a composite annotation which includes @Getter, @Setter, @EqualsAndHashCode,@ToString etc. @Builder will generate an inner Builder class in the hosted class which provides a fluent API to build an object.

Add lombok dependency in pom.xml.

org.projectlomboklombok${lombok.version}

If there are several JAP(Java annotation processor) exist in the project, such as JPA metadata generator, it is better to add Lombok processor to maven compiler plugin.

For example.

org.apache.maven.pluginsmaven-compiler-plugin3.5.1-Xlintlombok.launch.AnnotationProcessorHider$AnnotationProcessororg.hibernate.jpamodelgen.JPAMetaModelEntityProcessor

NOTE: If you are using Eclipse based IDE, such as Spring Tool Suite, or Intellij IDEA, you could have to install the Lombok plugin manually, check the Lombok download page for installation information. Luckily, NetBeans IDE can recognize the Lombok facilities automatically.

Unlike JPA metadata generator which generates metedata source for JPA entities. Lombok modifies target classes directly.

Execute javap Post.class in command line, you can get the follwing info.

It prints all signatures of members of Post.class. As you see all essential methods(getters, setters, equals, hashCode, toString) have been added into the Post.class, and there is a Post$Builder.class file existed in the same folder, which is an inner class in the Post and implements the Builder pattern.

You can create a Post object using Post builder like this.

Compare to following legacy new an object, the Builder pattern is more friendly to developers, and codes become more readable.

Model associations

Let's create other related models, Comment and User.

Comment class is associated with Post and User. Every comment should be belong to a post, and has an author(User).

User class contains fields of a user account, including username and password which used for authentication.

A Post should have an author.

Data persistence with JPA

Generally, in Spring application, in order to make JPA work, you have to configure a DataSource, EntityManagerFactory, TransactionManager.

JPA overview

JPA standardized Hibernate and it is part of Java EE specification since Java EE 5. Currently there are some popular JPA providers, such as Hibernate, OpenJPA, EclipseLink etc. EclipseLink is shipped with Glassfish, and Hibernate is included JBoss Wildfly/Redhat EAP.

In the above Modeling section, we have created models, which are JPA entities. In this section, let's see how to make it work.

Configure DataSources

Like other ORM frameworks, you have to configure a DataSource.

The DataSourceConfig defines a series of DataSource for differnt profiles.

In development stage("dev" profile is activated), using an embedded database is more easy to write tests, and speeds up development progress. In an integration server, it is recommended to run the application and integration tests on an environment close to production deployment. In a production environment,most of case, using a container managed DataSource is effective.

The Spring profile can be activated by an environment variable: spring.profiles.active. In our case, I used maven to set spring.profiles.active at compile time.

  1. In the maven profile section, there is spring.profiles.active property defined. eg.

    devtrueDEBUGdevcreatetruetrue //...

  2. Then used maven resource filter to replaced the placeholder defined in app.properties. Every maven profile could have a specific folder to hold the profiled based files. eg.

    dev //...src/main/resources-devtrue

  3. After it is compiled, content of app.properties is filtered and replaced with the defined property.

    Becomes:

  4. In configuration class, add PropertySource to load the properties file.

NOTE: Read the Spring official document about Spring profile and Environment.

An example of JPA usage could like.

@Repository is an alias of @Component, Transactional is used to enable transaction on this bean. @PersistenceContext to inject an EntityManager to this bean. EntityManager provides a plent of methods to operate database.

For example,

em.persist(Post) to persist new entity. em.merge(Post) to merge the passed data into the entity existed and return a copy of the updated entity.

If you want to explore all methods provided in EntityManager, check EntityManager javadoc.

Spring Data JPA

Spring Data JPA simplifies JPA, please read an early post I wrote to discuss this topic.

Use a EnableJpaRepositories to activiate Spring Data JPA. basePackages specifies packages will be scanned by Spring Data.

EnableJpaAuditing enable a simple auditing features provided in Spring Data JPA. There is some annotations are designated for it. Such as:

  • @CreatedBy

  • @CreatedDate

  • @LastModifiedBy

  • @LastModifiedDate

When AuditingEntityListener is activated globally in /META-INF/orm.xml. Any fields annotated with above annotaitions will be filled automatically when the hosted entity is created and updated.

CreatedBy and LastModifiedBy try to find a AuditAware bean and inject it into these fields at runtime. A simple implementation is getting the current principal from the Spring Security context.

Repository

You can imagine a repository as a domain object collection, allow you retreive data from it or save change state back.

Spring Data Commons project defines a series of interfaces for common data operations for different storages, including NoSQL and RDBMS.

The top-level repository representation is the Repository interface.

CrudRepository subclasses from Repository and includes extra creating, retrieving, updating and deleting operations, aka CRUD.

A simple CRUD operations just need to create an interface extends CrudRepository.

Create repository for Post.

JpaRepository is a JPA specific repository and derived from PagingAndSortingRepository which also subclasses from CrudRepository and provides pagination and sort capability. It accepts a Pageable object as method arguments, and can return a paged result with Page class.

JpaSpecificationExecutor is designated for JPA Criteria Query API, and provides type safe query instead of literal based JPQL.

For example, to search post by input keyword.

And you want to get a pageable result, you can use like this, just add a Pageable argument.

page argument is a Pageable object which can transfer pagination parameters from client request, and return result is a typed Page object, it includes the items of the current page and page navigation meta, such as total items, etc.

Application Service

A service can delegate CRUD operations to repository, also act as gateway to other bound context, such as messaging, sending email, fire events etc.

In this service there are some POJOs created for input request, response presentation etc.

PostForm gathers the user input data from client.

Validation annotations can be applied on it.

PostDetails represents the return result of a post details. Usually, it includes some info that should not include in the PostForm, such as id, timestamp etc.

Some exceptions are threw in the service if the input data can not satisfy the requirements. In further posts, I will focus on exception handling topic.

There is a DTOUtils which is responsible for data copy from one class to another class.

It used the effort of ModelMapper project.

Produces REST APIs with Spring MVC

Like other traditional action based framework, such as Apache Struts, etc, Spring MVC implements the standard MVC pattern. But against the benefit of IOC container, each parts of Spring MVC are not coupled, esp. view and view resolver are pluggable and can be configured.

For RESTful applications, JSON and XML are the commonly used exchange format, we do not need a template engine(such as Freemarker, Apache Velocity) for view, Spring MVC will detect HTTP headers, such as Content Type, Accept Type, etc. to determine how to produce corresponding view result. Most of the time, we do not need to configure the view/view resolver explicitly. This is called Content negotiation. There is a ContentNegotiationManager bean which is responsible for Content negotiation and enabled by default in the latest version.

The configuration details are motioned in before posts. We are jumping to write @Controller to produce REST APIs.

Follows the REST design convention, create PostController to produce REST APIs.

@RestController is a REST ready annotation, it is combined with @Controller and @ResponseBody.

@RequestMapping defines URL, HTTP methods etc are matched, the annotated method will handle the request.

/api/posts stands for a collection of Post, and /api/posts/{id} is navigating to a specific Post which identified by id.

A POST method on /api/posts is use for creating a new post, return HTTP status 201, and set HTTP header Location value to the new created post url if the creation is completed successfully.

GET, PUT, DELETE on /api/posts/{id} performs retrieve, update, delete action on the certain post. GET will return a 200 HTTP status code, and PUT, DELETE return 204 if the operations are done as expected and there is no content body needs to be sent back to clients.

Run

For none Spring Boot application, run it as a general web application in IDE.

Tomcat also provides maven plugin for those maven users.

org.apache.tomcat.maventomcat7-maven-plugin2.2/angularjs-springmvc-sample

Execute this in the command line to start this application.

NOTE: The tomcat maven plugin development is not active, if you are using Servlet 3.1 features, you could have to use other plugins instead.

Jetty is the fastest embedded Servlet container and wildly used in development community.

org.eclipse.jettyjetty-maven-plugin9.3.7.v20160115108005STOP/angularjs-springmvc-sample

Execute the following command to run the application on an embedded Jetty server.

Another frequently used is Cargo which provides support for all popular application servers, and ready for all hot build tools, such as Ant, Maven, Gradle etc.

org.codehaus.cargocargo-maven2-plugintomcat8xembedded9000high

Execute the following command to run the application on an embedded Tomcat 8 server with the help of Cargo.

For Spring boot application, it is simple, just run the application like this.

By default, it uses Tomcat embedded server, but you can switch to Jetty and JBoss Undertow if you like. Check the Spring boot docs for details.

Source Code

Check out sample codes from my github account.

Or the Spring Boot version:

Read the live version of thess posts from Gitbook:Building RESTful APIs with Spring MVC.

Last updated

Was this helpful?