Azure Database for MySQL Single Server + Spring Boot - Build REST CRUD APIs
Hello everyone, Hope you are doing well. In this tutorial, you will learn how to build REST CRUD APIs with Spring Boot, Spring Data JPA and Microsoft Azure Database for MySQL Single Server.
A little bit of Background
Azure Database for MySQL Single Server
Azure Database for MySQL Single Server is a fully managed database service designed for minimal customization. The single server platform is designed to handle most of the database management functions such as patching, backups, high availability, and security with minimal user configuration and control. The architecture is optimised for built-in high availability with 99.99% availability in a single availability zone. It supports the community version of MySQL 5.6 (retired), 5.7 and 8.0. The service is generally available today in a wide variety of Azure regions.
Spring Boot
Spring Boot makes it easy to create stand-alone, production-grade Spring-based Applications that you can "just run". More Info - https://spring.io/projects/spring-boot
Spring Boot makes it easy to create stand-alone, production-grade Spring-based Applications that you can "just run".
More Info - https://spring.io/projects/spring-boot
Spring Data JPA
Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA-based repositories. This module deals with enhanced support for JPA-based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.
Implementing a data access layer of an application has been cumbersome for quite a while. Too much boilerplate code has to be written to execute simple queries as well as perform pagination, and auditing. Spring Data JPA aims to significantly improve the implementation of data access layers by reducing the effort to the amount that’s actually needed. As a developer, you write your repository interfaces, including custom finder methods, and Spring will provide the implementation automatically.
More Info - https://spring.io/projects/spring-data-jpa
After completing this tutorial what we will build?
We will build REST APIs CRUD features:
Step 1: Sign in to Azure Portal and create a resource group
Sign in to Azure portal https://portal.azure.com/#home and find "Resource groups" like below.
Then, create a resource group like the one below.
Step 2: Create an Azure Database for MySQL Single Server
Select "Azure Database for MySQL servers",
You will be taken to a page like the below image, Then click on the "Create Azure Database for MySQL Server" button.Select Single Server, Then click on the Single Server"Create" button.
You will be taken to a page like the below image,Select/Enter necessary information like the above image and then, click on the "Review + create" button.
Then click on the "Create" button.
Now, You can see "Deployment is in progress" like the below image.
Once deployment is completed you can see the "Your deployment is complete" page like the below image.Copy the JDBC connection string and keep it safe for future purposes.
Whitelisting IP Address for Access to Azure MySQL DB, like the below image.Then click on the "Save" button
Select "Azure Database for MySQL servers",
You will be taken to a page like the below image, Then click on the "Create Azure Database for MySQL Server" button.
Select Single Server, Then click on the Single Server"Create" button.
You will be taken to a page like the below image,
Select/Enter necessary information like the above image and then, click on the "Review + create" button.
Then click on the "Create" button.
Now, You can see "Deployment is in progress" like the below image.
Once deployment is completed you can see the "Your deployment is complete" page like the below image.
Copy the JDBC connection string and keep it safe for future purposes.
Whitelisting IP Address for Access to Azure MySQL DB, like the below image.
Then click on the "Save" buttonStep 3: Creating a simple spring boot web application
First, open the Spring initializr https://start.spring.io/
Then, Provide the Group and Artifact name. We have provided Group name com.knf.dev.demo and Artifact spring-boot-azure-mysql-crud. Here I selected the Maven project - language Java - Spring Boot 2.7.1 and add Spring web dependency, Spring Data JPA, and MySQL Driver.
Then, click on the Generate button. When we click on the Generate button, it starts packing the project in a .zip(spring-boot-azure-mysql-crud.zip) file and downloads the project. Then, Extract the Zip file.
Import the project on your favourite IDE,
First, open the Spring initializr https://start.spring.io/
Then, Provide the Group and Artifact name. We have provided Group name com.knf.dev.demo and Artifact spring-boot-azure-mysql-crud. Here I selected the Maven project - language Java - Spring Boot 2.7.1 and add Spring web dependency, Spring Data JPA, and MySQL Driver.
Then, click on the Generate button. When we click on the Generate button, it starts packing the project in a .zip(spring-boot-azure-mysql-crud.zip) file and downloads the project.
Then, Extract the Zip file.
Import the project on your favourite IDE,
Final Project Directory:
A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details utilized by Maven to build the project.
<?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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>spring-boot-azure-mysql-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-azure-mysql-crud</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<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-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
<?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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>spring-boot-azure-mysql-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-azure-mysql-crud</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<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-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
We need to override the H2 database properties being set by default in Spring Boot. The following properties are needed to configure MySQL with Spring Boot. spring.datasource.url=jdbc:mysql://<servername>:3306/test?createDatabaseIfNotExist=true
spring.datasource.username=knfAdmin@knf-server
spring.datasource.password=<password>
spring.jpa.hibernate.ddl-auto=update
**Important note** Do not publish the fields directly for security. It would be a good choice to define these variables as environment variables or use the Azure Key Vault.
We need to override the H2 database properties being set by default in Spring Boot. The following properties are needed to configure MySQL with Spring Boot.
spring.datasource.url=jdbc:mysql://<servername>:3306/test?createDatabaseIfNotExist=true
spring.datasource.username=knfAdmin@knf-server
spring.datasource.password=<password>
spring.jpa.hibernate.ddl-auto=update
**Important note** Do not publish the fields directly for security. It would be a good choice to define these variables as environment variables or use the Azure Key Vault.
Creating the Entity User
The @Entity annotation specifies that the class is an entity and is mapped to a database table. The @Id annotation specifies the primary key of an entity and the @GeneratedValue provides for the specification of generation strategies for the values of primary keys. The @Column annotation is used to specify the mapped column for a persistent property or field. If no Column annotation is specified, the default value will be applied.package com.knf.dev.demo.entity;
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 = "_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email", nullable = false, length = 200)
private String email;
public User() {
super();
}
public Long getId() {
return id;
}
public void setId(Long 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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public User(String firstName, String lastName,
String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
}
The @Entity annotation specifies that the class is an entity and is mapped to a database table.
The @Id annotation specifies the primary key of an entity and the @GeneratedValue provides for the specification of generation strategies for the values of primary keys.
The @Column annotation is used to specify the mapped column for a persistent property or field. If no Column annotation is specified, the default value will be applied.
package com.knf.dev.demo.entity;
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 = "_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email", nullable = false, length = 200)
private String email;
public User() {
super();
}
public Long getId() {
return id;
}
public void setId(Long 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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public User(String firstName, String lastName,
String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
}
Creating the User Repository
CrudRepository is a Spring Data interface for generic CRUD operations on a repository of a specific type. It provides several methods out of the box for interacting with a database.package com.knf.dev.demo.repository;
import org.springframework.data.repository.CrudRepository;
import com.knf.dev.demo.entity.User;
public interface UserRepository
extends CrudRepository<User, Long> {
}
CrudRepository is a Spring Data interface for generic CRUD operations on a repository of a specific type. It provides several methods out of the box for interacting with a database.
package com.knf.dev.demo.repository;
import org.springframework.data.repository.CrudRepository;
import com.knf.dev.demo.entity.User;
public interface UserRepository
extends CrudRepository<User, Long> {
}
Exception Handler with Controller Advice in Spring
Spring supports exception handling by a global Exception Handler (@ExceptionHandler) with Controller Advice (@ControllerAdvice). This enables a mechanism that makes ResponseEntity work with the type safety and flexibility of @ExceptionHandler:package com.knf.dev.demo.exception;
import java.util.Date;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFound.class)
public ResponseEntity<ErrorMessage>
userNotFound(Exception ex, WebRequest request) {
ErrorMessage errors =
new ErrorMessage(404, new Date(),
ex.getMessage(), "User Not Found");
return new ResponseEntity<>
(errors, HttpStatus.NOT_FOUND);
}
}
Spring supports exception handling by a global Exception Handler (@ExceptionHandler) with Controller Advice (@ControllerAdvice). This enables a mechanism that makes ResponseEntity work with the type safety and flexibility of @ExceptionHandler:
package com.knf.dev.demo.exception;
import java.util.Date;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFound.class)
public ResponseEntity<ErrorMessage>
userNotFound(Exception ex, WebRequest request) {
ErrorMessage errors =
new ErrorMessage(404, new Date(),
ex.getMessage(), "User Not Found");
return new ResponseEntity<>
(errors, HttpStatus.NOT_FOUND);
}
}
Custom Exception - UserNotFound
package com.knf.dev.demo.exception;
public class UserNotFound extends RuntimeException {
private static final long serialVersionUID = 1L;
public UserNotFound(String msg) {
super(msg);
}
}
package com.knf.dev.demo.exception;
public class UserNotFound extends RuntimeException {
private static final long serialVersionUID = 1L;
public UserNotFound(String msg) {
super(msg);
}
}
Error Message
package com.knf.dev.demo.exception;
import java.util.Date;
public class ErrorMessage {
private Integer statusCode;
private Date timestamp;
private String message;
private String description;
public Integer getStatusCode() {
return statusCode;
}
public void setStatusCode(Integer statusCode) {
this.statusCode = statusCode;
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public ErrorMessage(Integer statusCode,
Date timestamp, String message,
String description) {
this.statusCode = statusCode;
this.timestamp = timestamp;
this.message = message;
this.description = description;
}
}
package com.knf.dev.demo.exception;
import java.util.Date;
public class ErrorMessage {
private Integer statusCode;
private Date timestamp;
private String message;
private String description;
public Integer getStatusCode() {
return statusCode;
}
public void setStatusCode(Integer statusCode) {
this.statusCode = statusCode;
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public ErrorMessage(Integer statusCode,
Date timestamp, String message,
String description) {
this.statusCode = statusCode;
this.timestamp = timestamp;
this.message = message;
this.description = description;
}
}
User Rest Controller
The @RestController annotation was introduced in Spring 4.0 to simplify the engendering of RESTful web services. It's a convenience annotation that combines @Controller and @ResponseBody. @RequestMapping annotation maps HTTP requests to handler methods of MVC and REST controllers.package com.knf.dev.demo.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.knf.dev.demo.entity.User;
import com.knf.dev.demo.exception.UserNotFound;
import com.knf.dev.demo.repository.UserRepository;
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
@Autowired
UserRepository userRepository;
// Create user
@PostMapping
public ResponseEntity<User>
createUser(@RequestBody User user) {
User newuser = new User(user.getFirstName(),
user.getLastName(), user.getEmail());
userRepository.save(newuser);
return new ResponseEntity<>
(newuser, HttpStatus.CREATED);
}
// Update user
@PutMapping("/{id}")
public ResponseEntity<User>
updateUser(@PathVariable("id") Long id,
@RequestBody User user) {
Optional<User> userdata = userRepository.findById(id);
if (userdata.isPresent()) {
User _user = userdata.get();
_user.setEmail(user.getEmail());
_user.setFirstName(user.getFirstName());
_user.setLastName(user.getLastName());
return new ResponseEntity<>
(userRepository.save(_user), HttpStatus.OK);
} else {
throw new UserNotFound("Invalid User Id");
}
}
// Get all Users
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = new ArrayList<User>();
userRepository.findAll().forEach(users::add);
return new ResponseEntity<>(users, HttpStatus.OK);
}
// Get user by ID
@GetMapping("/{id}")
public ResponseEntity<User>
getUserByID(@PathVariable("id") Long id) {
Optional<User> userdata = userRepository.findById(id);
if (userdata.isPresent()) {
return new ResponseEntity<>
(userdata.get(), HttpStatus.OK);
} else {
throw new UserNotFound("Invalid User Id");
}
}
// Delete user
@DeleteMapping("/{id}")
public ResponseEntity<User>
deleteUser(@PathVariable("id") Long id) {
Optional<User> userdata = userRepository.findById(id);
if (userdata.isPresent()) {
userRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
throw new UserNotFound("Invalid User Id");
}
}
}
The @RestController annotation was introduced in Spring 4.0 to simplify the engendering of RESTful web services. It's a convenience annotation that combines @Controller and @ResponseBody. @RequestMapping annotation maps HTTP requests to handler methods of MVC and REST controllers.
package com.knf.dev.demo.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.knf.dev.demo.entity.User;
import com.knf.dev.demo.exception.UserNotFound;
import com.knf.dev.demo.repository.UserRepository;
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
@Autowired
UserRepository userRepository;
// Create user
@PostMapping
public ResponseEntity<User>
createUser(@RequestBody User user) {
User newuser = new User(user.getFirstName(),
user.getLastName(), user.getEmail());
userRepository.save(newuser);
return new ResponseEntity<>
(newuser, HttpStatus.CREATED);
}
// Update user
@PutMapping("/{id}")
public ResponseEntity<User>
updateUser(@PathVariable("id") Long id,
@RequestBody User user) {
Optional<User> userdata = userRepository.findById(id);
if (userdata.isPresent()) {
User _user = userdata.get();
_user.setEmail(user.getEmail());
_user.setFirstName(user.getFirstName());
_user.setLastName(user.getLastName());
return new ResponseEntity<>
(userRepository.save(_user), HttpStatus.OK);
} else {
throw new UserNotFound("Invalid User Id");
}
}
// Get all Users
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = new ArrayList<User>();
userRepository.findAll().forEach(users::add);
return new ResponseEntity<>(users, HttpStatus.OK);
}
// Get user by ID
@GetMapping("/{id}")
public ResponseEntity<User>
getUserByID(@PathVariable("id") Long id) {
Optional<User> userdata = userRepository.findById(id);
if (userdata.isPresent()) {
return new ResponseEntity<>
(userdata.get(), HttpStatus.OK);
} else {
throw new UserNotFound("Invalid User Id");
}
}
// Delete user
@DeleteMapping("/{id}")
public ResponseEntity<User>
deleteUser(@PathVariable("id") Long id) {
Optional<User> userdata = userRepository.findById(id);
if (userdata.isPresent()) {
userRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
throw new UserNotFound("Invalid User Id");
}
}
}
Spring Boot main driver
package com.knf.dev.demo;
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, args);
}
}
package com.knf.dev.demo;
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, args);
}
}
Step 4: Local Setup and Run the application
Step 1: Download or clone the source code from GitHub to the local machine - Click here
Step 2: mvn clean install
Step 3: Run the Spring Boot application -
mvn spring-boot:run
Step 1: Download or clone the source code from GitHub to the local machine - Click here
Step 2: mvn clean install
Step 3: Run the Spring Boot application -
mvn spring-boot:run