Creating an API with Spring Boot and MySQL: Part 2

Adam Zink
6 min readJul 17, 2018

--

In Part 1, we used Spring Initializr to create a skeleton Spring Boot project and added a few details to make it work with MySQL and Flyway.

Next, we are going to start a simple API with functionality to POST a new User.

Audience

Anyone who wants to learn how to create and run an API using Spring Boot with MySQL and Flyway.

Experience with Java programming and relational databases is helpful but not required.

Requirements

Create the database model

This demo uses Hibernate as an ORM (Object Relational Mapper) tool to convert database rows in the USER table to Java UserModel objects. Hibernate is a popular implementation of JPA (Java Persistence API).

The first thing to do is create the UserModel class to mirror the table structure:

@Entity
@Table(name="USER")
public class UserModel {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column
private Long id;

@Column
private String firstName;

@Column
private String lastName;

@Column
private Date addTs;

public UserModel() {
}
...
}
  • The @ signs are annotations that tell the Spring Boot framework how to interpret the model
  • @Entity and @Table indicate this class represents a database table
  • All JPA model classes have an @Id to be able to refer to a specific row in the table
  • Each field in the table needs a corresponding variable with the @Columnannotation
  • Since the USER table has AUTO_INCREMENT property on the ID column, JPA matches it with GenerationType.IDENTITY strategy to let MySQL keep creating the IDs
  • ... stands for the standard getters, setters, equals(), and hashcode()methods common in POJO (Plain Old Java Object) classes. With IntelliJ (and most other IDEs), you can generate these methods by right clicking and selecting “Generate…”

Create the request object

To add a new User, the REST client will need to send details about the User to our resource. However, there are often cases where the details sent by the client are different than what is stored in the database. For example, UserModel has a timestamp that is generated by the service. Also, the ID is generated by the database. Therefore, the only two values that are needed for a request are First Name and Last Name.

public class UserRequest {    private String firstName;
private String lastName;
public UserRequest() {
}
...
}
  • UserRequest does NOT have any JPA annotations. Since it is only a partial representation of the table, we need to write some code later to convertbetween the database model and request object.

Create the response object

After the User is saved, the details are typically returned to the client as confirmation. It is advantageous to have a third POJO with formatted values for the client to use. For example, instead of making the client convert the Add timestamp, the service can do it and pass along the formatted date string to the client.

public class User {    private Long id;
private String firstName;
private String lastName;
private String addDate;
public User() {
}

...
}

Create the converter

In order to translate between the client and database representations of a User, we can set up an interface with generic method signatures, and then implement the interface specifically for User.

public interface ModelConverter<Q, M, S> {    M requestToModel(Q request);    S modelToResponse (M model);}

The interface declares two methods:

  1. requestToModel to take the request from the client and translate into the database model (in preparation for saving to the database)
  2. modelToResponse to take an object from the database and translate into the client format (in preparation for returning confirmation to the client)

Next, implement ModelConverter for Users:

@Component
public class UserConverter implements ModelConverter<UserRequest, UserModel, User> {
@Override
public UserModel requestToModel(UserRequest request) {
UserModel model = new UserModel();
model.setFirstName(request.getFirstName());
model.setLastName(request.getLastName());
return model;
}
@Override
public User modelToResponse(UserModel model) {
User response = new User();
response.setId(model.getId());
response.setFirstName(model.getFirstName());
response.setLastName(model.getLastName());
response.setAddDate(new SimpleDateFormat("MMM d, yyyy").format(model.getAddTs()));
return response;
}
}
  • A new JPA annotation, @Component, indicates Spring Boot should manage this class and make it available to other classes
  • The interface parameters <Q, M, S> are replaced with User-specific objects <UserRequest, UserModel, User>
  • requestToModel fills in the details it knows for UserModel and leaves the rest null
  • modelToResponse fills in the required details for User. It also formats the timestamp, showing how the converter can have some of its own logic instead of always passing the exact database values to the client

How do I save a User?

We have set up the dependent classes for our API, and now we can implement the actual repository and service methods.

First, start with UserRepository:

@Repository
public interface UserRepository extends JpaRepository<UserModel, Long> {
}
  • All of the standard repository methods our API needs are inherited from JpaRepository
  • @Repository indicates Spring Boot should also manage this class

What calls the repository method?

For the client to access the repository method, we need to create the UserService class:

@Service
@Transactional
public class UserService {
@Autowired
UserConverter userConverter;
@Autowired
UserRepository userRepository;
public User save(final UserRequest userRequest) {
UserModel userModel = userConverter.requestToModel(userRequest);
userModel.setAddTs(new Date()); return userConverter.modelToResponse(userRepository.save(userModel));
}
}
  • @Service indicates Spring Boot should manage this service layer component. @Service is functionally similar to @Component, but convention encourages using the most specific annotation for code readability and compatibility with future releases (see Spring documentation for a more complete explanation)
  • @Transactional directs Spring Boot to use a single session to interact with the database. Transactions are commonly started at the service layer and then used by the repository methods
  • @Autowired is a special annotation that allows the current Spring Boot managed class to use an instance of other managed classes. For example, since UserRepository was annotated with @Repository, Spring Boot can find it and there is no need to explicitly declare an instance of UserRepository
  • The save method takes a UserRequest from the client and returns a Userresponse to the client

How does the client call the API?

The client needs to know several things in order to call the API:

  1. URL
  2. HTTP action verb
  3. Request data

All of these can be described by a Jersey resource class. Let’s create one called UserResource:

@Path("/users")
@Component
public class UserResource {
@Autowired
UserService userService;
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public User save(final UserRequest userRequest) {
return userService.save(userRequest);
}
}
  • The @Path annotation specifies the relative root of the the URL
  • UserService is autowired so service methods can be mapped to the URLs
  • @POST indicates the save method requires data from the client to create the new User
  • @Consumes and @Produces specify the type of the incoming data from the client and outgoing data to the client, respectively. Many APIs, including this one, send data in JSON (JavaScript Object Notation) format

A specific API path is often called an endpoint. Also, many REST APIs have a common path prefix of /api for all endpoints. One way to use a path prefix is by adding this line to application.properties:

# Path properties
server.servlet.contextPath=/api

Including the prefix, the endpoint for a new User becomes /api/users

However, if you try to use the endpoint now, it will give a 404 Not Found error. For Spring Boot and Jersey to work properly, one more step is required. You must register the resource class in a new class that extends ResourceConfig. We can call it JerseyConfiguration:

@Component
public class JerseyConfiguration extends ResourceConfig {
public JerseyConfiguration() {
register(UserResource.class);
}
}

POST a User

The API is ready to add the first User!

With a REST client like Postman, choose POST and enter URL as http://localhost:8080/api/users

The request body is required in the following format:

{
"firstName": "Adam",
"lastName": "Zink"
}

Then, click Send to submit the request:

The User object is returned as confirmation. It includes both the ID generated by the database and the date formatted by the service.

Congratulations! Your Spring Boot REST endpoint is now operational.

--

--