In this tutorial, we explain how to run a minimal Spring Boot CRUD REST application, with Hibernate ORM and a MySQL database. We also write unit tests, to check the CRUD operations with the API. We do all this in less than 200 lines of code. This tutorial is useful to get started for any more complex larger Java SaaS project, that relies on a classical MVC design pattern.
1) Java project folder
On your local machine, create a Java project folder, called for instance:
hibernate
In the rest of this tutorial, we will explain where to place files inside this folder.
2) Customer DB model
In this demo app, we will have only one entity called Customer (with only 2 fields: id and name). We will persist the entity in database using JPA Hibernate. Create the file hibernate/src/main/java/com/Customer.java with content:
package com;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
@Entity
@Table(name = "customers")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@GeneratedValue(strategy = GenerationType.IDENTITY) means that the id primary key will be auto-incremented.
3) The customer repository
The Hibernate repository is a layer where we perform CRUD operations on the backend database. To create it with Spring Boot, it is very easy. Simply, create the file hibernate/src/main/java/com/CustomerRepository.java with content:
package com;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}
4) The web controller
Create a file hibernate/src/main/java/com/CustomerController.java with content:
package com;
import javax.validation.Valid;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("")
public class CustomerController {
@Autowired
CustomerRepository customerRepository;
@GetMapping("/customers")
public List<Customer> getCustomers() {
return customerRepository.findAll();
}
@PostMapping("/customers")
public Customer createCustomer(@Valid @RequestBody Customer customer) {
return customerRepository.save(customer);
}
@GetMapping("/customers/{id}")
public Customer getCustomerById(@PathVariable(value = "id") Long customerId) {
return customerRepository.findById(customerId).orElseThrow(NotFoundException::new);
}
@DeleteMapping("/customers/{id}")
public ResponseEntity<?> deleteCustomer(@PathVariable(value = "id") Long customerId) {
Customer customer = customerRepository.findById(customerId).orElseThrow(NotFoundException::new);
customerRepository.delete(customer);
return ResponseEntity.ok().build();
}
@ResponseStatus(value = HttpStatus.NOT_FOUND)
private static class NotFoundException extends RuntimeException {
}
}
This is the usual controller code, with the REST endpoints (GET, POST, DELETE).
5) The Spring Boot application main
Create the file hibernate/src/main/java/com/Application.java with following content:
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
6) The application properties file
We need a file where we specify the database connection information for Spring Boot. Create the file hibernate/src/main/resources/application.properties with content:
spring.datasource.url = <your-jdbcUrl> spring.datasource.username = <your-dbUsername> spring.datasource.password = <your-dbPassword> spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect spring.jpa.hibernate.ddl-auto = update
In the above file, replace <your-jdbcUrl>, <your-dbUsername> and <your-dbPassword> with your own values.
7) The pom.xml
Create a file hibernate/pom.xml with content:
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>hibernate</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>hibernate</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<spring.version>2.5.6</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>${spring.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.5</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.16.2</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>1.16.2</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.16.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
</project>
Congratulations ! You now have a fully working Spring Boot REST API with JPA Hibernate, that works with any database (fill the database connection properties with your own database values, as explained in step 6)). To start the API, simply run the Application.main() function from step 5).
8) End to end testing of the API
In this last part of the tutorial, we explain how to automatically test the API, using a MySQL database Testcontainer. We will check that the CRUD customer operations of the API work well.
Create the file /hibernate/src/test/java/com/ApplicationTests.java with content:
package com;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.*;
import org.springframework.test.context.*;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.*;
import static org.assertj.core.api.Assertions.assertThat;
@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApplicationTests {
@LocalServerPort
private int port;
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:5.5").withDatabaseName("database").withPassword("test").withPassword("test");
@DynamicPropertySource
static void mysqlProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", mysql::getJdbcUrl);
registry.add("spring.datasource.password", mysql::getPassword);
registry.add("spring.datasource.username", mysql::getUsername);
}
@Autowired
private TestRestTemplate restTemplate;
@Test
public void contextLoads() throws JsonProcessingException {
String url = "http://localhost:" + port + "/customers";
// POST customer
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<>("{\"name\":\"a\"}", headers);
String resp = restTemplate.postForObject(url, request, String.class);
// extract ID from created customer
ObjectMapper objectMapper = new ObjectMapper();
JsonNode root = objectMapper.readTree(resp);
long id = root.path("id").longValue();
// GET by ID
resp = restTemplate.getForObject(url + "/" + id, String.class);
root = objectMapper.readTree(resp);
String name = root.path("name").textValue();
assertThat(name).isEqualTo("a");
// LIST
resp = restTemplate.getForObject(url, String.class);
root = objectMapper.readTree(resp);
assertThat(root.size()).isEqualTo(1);
name = root.path(0).path("name").textValue();
assertThat(name).isEqualTo("a");
// DELETE
restTemplate.delete(url + "/" + id, request, String.class);
// LIST
resp = restTemplate.getForObject(url, String.class);
root = objectMapper.readTree(resp);
assertThat(root.size()).isEqualTo(0);
// GET by ID should respond 404 NOT FOUND
int httpCode = restTemplate.getForEntity(url + "/" + id, String.class).getStatusCodeValue();
assertThat(httpCode).isEqualTo(404);
}
}
We override the static config application.properties with our own custom code that discovers the Testcontainers JDBC URL, by using the annotation @DynamicPropertySource.
You can now run the end to end test in your IDE like IntelliJ, or using command line, run:
mvn test
That’s it for this tutorial, thank you for reading ! If you have any question, please leave a reply below.