Spring Boot + Spring Data REST Example

In this articles we’ll go through some of the basics of Spring Data REST and see an example of using Spring Boot with Spring Data REST.

Spring Data REST

Spring Data REST builds on top of the Spring Data repositories and automatically exports those as REST endpoints. It takes the features of Spring HATEOAS and Spring Data and automatically combines them together.

In the Spring Boot + Spring Data JPA + MySQL + Spring RESTful example you can see how using Spring Data JPA requires that you just create a Repository interface. No need to write DAO implementation classes Spring takes care of automatically implementing this repository interface.

Spring Data REST goes one step further, you don’t even need to create a Controller class with mappings (GET, POST, PUT etc.). By using the domain class used with in the Repository interface Data REST automatically exposes the methods provided by Spring Data JPA as REST end points. Let’s see it in action with the help of an example.

Maven Dependencies

In the pom.xml file add the following starter dependencies.

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
  </dependency>
  <!-- MySQL Driver -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
  </dependency>
</dependencies>

This example uses MySQL DB as backend so driver for that is added.

DB table Query

MySQL DB table used for this Spring Boot and Spring data REST example can be created using the following query.

CREATE TABLE `employee` ( `id` int(11) NOT NULL AUTO_INCREMENT, `first_name` varchar(45) DEFAULT NULL, `last_name` varchar(45) DEFAULT NULL, `department` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Domain class (Entity class)

Entity class which maps to the employee table in DB.

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="employee")
public class Employee {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private int id;
  @Column(name="first_name")
  private String firstName;
  @Column(name="last_name")
  private String lastName;
  @Column(name="department")
  private String dept;
  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public String getLastName() {
    return lastName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public String getDept() {
    return dept;
  }
  public void setDept(String dept) {
    this.dept = dept;
  }

  @Override
  public String toString() {
    return "Id= " + getId() + " First Name= " + 
           getFirstName() + " Last Name= " + getLastName() + 
           " Dept= "+ getDept();
  }
}

Data JPA Repository Class (Employee Repository)

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import com.knpcode.model.Employee;

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
  List<Employee> findByLastName(@Param("name") String lastName);
}

This repository is an interface that lets you perform various operations involving Employee objects. It gets these operations by extending the JPARepository interface that is defined in Spring Data Commons.

At runtime, Spring Data REST automatically creates an implementation of this interface.

For this EmployeeRepository interface, Spring Data REST exposes a collection resource at "/employees" by default. This path is derived from the uncapitalized, pluralized, simple class name of the domain class being managed. It also exposes an item resource for each of the items managed by the repository under the URI template "/employees/{id}".

DB Configuration

By default Spring boot reads properties file at this location src/main/resources/application.properties. You must define the DB connection attributes and Hibernate related properties in the application.properties file.

spring.datasource.url=jdbc:mysql://localhost:3306/knpcode
spring.datasource.username=admin
spring.datasource.password=password

spring.jpa.properties.hibernate.sqldialect=org.hibernate.dialect.MySQLInnoDBDialect
spring.jpa.properties.hibernate.showsql=true

Create Spring Boot Application Class

Here is an application class with the main method which is the entry point for Spring Boot application.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DataRestApp {
  public static void main(String[] args) {
    SpringApplication.run(DataRestApp.class, args);
  }
}

That’s all you need to have a fully functional REST API. Run it as a stand alone Java application by running the class with the main method (DataRestApp.java) from Eclipse IDE itself.

Right click DataRestApp.java – Run As – Java Application

To discover what resources are available at the root of the application, issue an HTTP GET to the root URL (http://localhost:8080/)

Spring data REST

As you can see “/employees” endpoint is available to get all the employees. There are also ?page,size,sort options available.

A profile endpoint “/profile”, is a place to include application-level details.

Adding an Employee-

Using Postman client you can send a POST request to add employee record.

Data REST endpoint

If you look at the response headers in the Postman client, you will notice that the content-type is 'application/hal+JSON'

By default, Spring Data REST uses HAL to render responses. Hypertext Application Language (HAL) is a simple language that gives a consistent and easy way to hyperlink between resources in API.

Getting all employees-

Getting employee by ID-

Using @RepositoryRestResource to customize REST endpoints

If you want to change this default path you can use @RepositoryRestResource annotation to do that. Both the name of the resource and the path can be customized by using @RepositoryRestResource on the repository interface.

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import com.knpcode.model.Employee;

@RepositoryRestResource(collectionResourceRel = "employee", path = "employee")
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
  List<Employee> findByLastName(@Param("name") String lastName);
}

With this change you can get the list of all employees by using the following URL.

http://localhost:8080/employee

For getting employee detail by ID-

http://localhost:8080/employee/23

You can also issue PUT, PATCH, and DELETE REST calls to replace, update, or delete existing records respectively. The following example uses a PATCH call to update a subset of items.

REST patch request

Changing the Base URI

By default, Spring Data REST serves up REST resources at the root URI, '/'. You can change the base URI by setting a single property in application.properties, as follows:

spring.data.rest.basePath=/app

Now you can use the following URL to access all employees.

http://localhost:8080/app/employee

Accessing Data JPA custom query

In the EmployeeRepository interface there is a custom method findByLastName(). You can find all query methods exposed by a repository by using search resource which returns links for all query methods.

Data REST Search resource

You can see the URL for the query which by default matches the custom method name. The HTTP query parameter, name, matches the @Param("name") annotation used in the method in repository interface.

Spring Data query method

Using @RestResource annotation to change query method path

To change the segment of the URL under which this query method is exposed, you can use the @RestResource annotation as the following example shows.

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
  @RestResource(path = "lastname")
  List<Employee> findByLastName(@Param("name") String lastName);
}

Then you can use following URL to access query method.

http://localhost:8080/employees/search/lastname?name=Callahan

That's all for the topic Spring Boot + Spring Data REST Example. If something is missing or you have something to share about the topic please write a comment.


You may also like

Tic-Tac-Toe Game in Python

In this article we’ll see how to develop tic-tac-toe game in Python.

The tic-tac-toe game we are going to develop is a two player game played in the command line. Steps in the program are as follows-

  1. Give an option to the player 1 to choose symbol (‘X’ or ‘O’).
  2. Create a tic-tac-toe board with index numbers making it easy for the players to input the index where they want their symbol to be placed.
  3. Give each player a chance to place the symbol alternatively until one of the players has won the game or the game is drawn.
tic-tac-toe Python
tic-tac-toe board

Tic-tac-toe game – Python code

def user_choice():
  choice = ' '
  flag = False
  while not choice.isdigit() or not flag:
    choice = input('Please input a number (0-10): ')
    if choice.isdigit() and int(choice) in range(0, 10):
      flag = True
      return int(choice)
    else:
      print('Enter number between 0 and 10')
      flag = False


def display_board(board):
    print('\n')
    print(' ' + board[1] + ' | ' + board[2] + ' | ' + board[3])
    print('-----------')
    print(' ' + board[4] + ' | ' + board[5] + ' | ' + board[6])
    print('-----------')
    print(' ' + board[7] + ' | ' + board[8] + ' | ' + board[9])


def player_input():
  marker = ''
  while not (marker == 'X' or marker == 'O'):
      marker = input('Player 1: Do you want to be X or O? ').upper()
  if marker == 'X':
      return ('X', 'O')
  else:
      return ('O', 'X')


def place_symbol(board, symbol, position):
  if board[position] in ('X', 'O'):
    print('Position already marked')
  board[position] = symbol


def check_marked_position(board, position):
  if board[position] in ('X', 'O'):
    print('Position already marked')
    return False
  return True


def select_player_position(board, player):
  position = 0
  while position not in range(1, 10) or not check_marked_position(board, position):
    position = int(input(player + ' Choose your next position: (1-9) '))
  return position


def is_winner(board, symbol):
  return ((board[1] == symbol and board[2] == symbol and board[3] == symbol) or  # top row
          (board[4] == symbol and board[5] == symbol and board[6] == symbol) or  # middle row
          (board[7] == symbol and board[8] == symbol and board[9] == symbol) or  # bottom row
          (board[1] == symbol and board[4] == symbol and board[7] == symbol) or  # first column
          (board[2] == symbol and board[5] == symbol and board[8] == symbol) or  # second column
          (board[3] == symbol and board[6] == symbol and board[9] == symbol) or  # third column
          (board[1] == symbol and board[5] == symbol and board[9] == symbol) or  # diagonal
          (board[3] == symbol and board[5] == symbol and board[7] == symbol))  # diagonal


def is_board_full(board):
  full_flag = True
  for i in range(1, 10):
    if board[i] not in ('X', 'O'):
      full_flag = False
  return full_flag


def start_play():
  while True:
    player1_symbol, player2_symbol = player_input()
    tictac_board = ['#', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    turn = 'Player1'
    play_again_flag = 'Y'
    symbol = ''
    display_board(tictac_board)
    while True:
      if turn == 'Player1':
        symbol = player1_symbol
      else:
        symbol = player2_symbol

      position = select_player_position(tictac_board, turn)
      place_symbol(tictac_board, symbol, position)
      display_board(tictac_board)
      if is_winner(tictac_board, symbol):
        print('Yeey! ' + turn + ' won!')
        break
      if is_board_full(tictac_board):
        print('It''s a draw!')
        break
      else:
        turn = 'Player2' if turn is 'Player1' else 'Player1'


      play_again_flag = input('Do you want to play again? Enter Y or N: ')
      print(play_again_flag.lower())
      if play_again_flag.lower() != 'y':
        break

start_play()
  1. start_play() function is the main function where first task is to ask player 1 to choose preferred symbol out of ‘X’ or ‘O’. This is done by calling player_input() function.
  2. To display tic-tac-toe board display_board() function is called passing list tictac_board = ['#', '1', '2', '3', '4', '5', '6', '7', '8', '9'] as an argument. Since index 0 is not required so that has a placeholder value ‘#’.
  3. In a while loop each player gets a chance to place their respective symbol. Functions used for this task are select_player_position() and place_symbol().
  4. While placing a symbol it is required to check if the selected position is already marked or not that is done using check_marked_position() function.
  5. After each turn it is also required to check if there is a winner that is done using is_winner() function. Another thing to check is whether the board is full that is done using is_board_full() function.

That's all for the topic Tic-Tac-Toe Game in Python. If something is missing or you have something to share about the topic please write a comment.


You may also like