Use Spring Data R2DBC with Azure Database for MySQL
This article demonstrates creating a sample application that uses Spring Data R2DBC to store and retrieve information in Azure Database for MySQL by using the R2DBC implementation for MySQL from the r2dbc-mysql GitHub repository.
R2DBC brings reactive APIs to traditional relational databases. You can use it with Spring WebFlux to create fully reactive Spring Boot applications that use non-blocking APIs. It provides better scalability than the classic "one thread per connection" approach.
Prerequisites
An Azure subscription - create one for free.
Java Development Kit (JDK), version 8 or higher.
cURL or a similar HTTP utility to test functionality.
See the sample application
In this article, you'll code a sample application. If you want to go faster, this application is already coded and available at https://github.com/Azure-Samples/quickstart-spring-data-r2dbc-mysql.
Prepare the working environment
First, set up some environment variables by running the following commands:
export AZ_RESOURCE_GROUP=database-workshop
export AZ_DATABASE_NAME=<YOUR_DATABASE_NAME>
export AZ_LOCATION=<YOUR_AZURE_REGION>
export AZ_MYSQL_ADMIN_USERNAME=spring
export AZ_MYSQL_ADMIN_PASSWORD=<YOUR_MYSQL_ADMIN_PASSWORD>
export AZ_MYSQL_NON_ADMIN_USERNAME=spring-non-admin
export AZ_MYSQL_NON_ADMIN_PASSWORD=<YOUR_MYSQL_NON_ADMIN_PASSWORD>
Replace the placeholders with the following values, which are used throughout this article:
<YOUR_DATABASE_NAME>
: The name of your MySQL server, which should be unique across Azure.<YOUR_AZURE_REGION>
: The Azure region you'll use. You can useeastus
by default, but we recommend that you configure a region closer to where you live. You can see the full list of available regions by usingaz account list-locations
.<YOUR_MYSQL_ADMIN_PASSWORD>
and<YOUR_MYSQL_NON_ADMIN_PASSWORD>
: The password of your MySQL database server, which should have a minimum of eight characters. The characters should be from three of the following categories: English uppercase letters, English lowercase letters, numbers (0-9), and non-alphanumeric characters (!, $, #, %, and so on).
Note
Microsoft recommends using the most secure authentication flow available. The authentication flow described in this procedure, such as for databases, caches, messaging, or AI services, requires a very high degree of trust in the application and carries risks not present in other flows. Use this flow only when more secure options, like managed identities for passwordless or keyless connections, are not viable. For local machine operations, prefer user identities for passwordless or keyless connections.
Next, create a resource group:
az group create \
--name $AZ_RESOURCE_GROUP \
--location $AZ_LOCATION \
--output tsv
Create an Azure Database for MySQL instance and set up the admin user
The first thing you'll create is a managed MySQL server with an admin user.
Note
You can read more detailed information about creating MySQL servers in Create an Azure Database for MySQL server by using the Azure portal.
az mysql flexible-server create \
--resource-group $AZ_RESOURCE_GROUP \
--name $AZ_DATABASE_NAME \
--location $AZ_LOCATION \
--admin-user $AZ_MYSQL_ADMIN_USERNAME \
--admin-password $AZ_MYSQL_ADMIN_PASSWORD \
--yes \
--output tsv
Configure a MySQL database
Create a new database called demo
by using the following command:
az mysql flexible-server db create \
--resource-group $AZ_RESOURCE_GROUP \
--database-name demo \
--server-name $AZ_DATABASE_NAME \
--output tsv
Configure a firewall rule for your MySQL server
Azure Database for MySQL instances are secured by default. They have a firewall that doesn't allow any incoming connection.
You can skip this step if you're using Bash because the flexible-server create
command already detected your local IP address and set it on MySQL server.
If you're connecting to your MySQL server from Windows Subsystem for Linux (WSL) on a Windows computer, you need to add the WSL host ID to your firewall. Obtain the IP address of your host machine by running the following command in WSL:
cat /etc/resolv.conf
Copy the IP address following the term nameserver
, then use the following command to set an environment variable for the WSL IP Address:
export AZ_WSL_IP_ADDRESS=<the-copied-IP-address>
Then, use the following command to open the server's firewall to your WSL-based app:
az mysql flexible-server firewall-rule create \
--resource-group $AZ_RESOURCE_GROUP \
--name $AZ_DATABASE_NAME \
--start-ip-address $AZ_WSL_IP_ADDRESS \
--end-ip-address $AZ_WSL_IP_ADDRESS \
--rule-name allowiprange \
--output tsv
Create a MySQL non-admin user and grant permission
This step will create a non-admin user and grant all permissions on the demo
database to it.
Note
You can read more detailed information about creating MySQL users in Create users in Azure Database for MySQL.
First, create a SQL script called create_user.sql for creating a non-admin user. Add the following contents and save it locally:
Note
Microsoft recommends using the most secure authentication flow available. The authentication flow described in this procedure, such as for databases, caches, messaging, or AI services, requires a very high degree of trust in the application and carries risks not present in other flows. Use this flow only when more secure options, like managed identities for passwordless or keyless connections, are not viable. For local machine operations, prefer user identities for passwordless or keyless connections.
cat << EOF > create_user.sql
CREATE USER '$AZ_MYSQL_NON_ADMIN_USERNAME'@'%' IDENTIFIED BY '$AZ_MYSQL_NON_ADMIN_PASSWORD';
GRANT ALL PRIVILEGES ON demo.* TO '$AZ_MYSQL_NON_ADMIN_USERNAME'@'%';
FLUSH PRIVILEGES;
EOF
Then, use the following command to run the SQL script to create the non-admin user:
mysql -h $AZ_DATABASE_NAME.mysql.database.azure.com --user $AZ_MYSQL_ADMIN_USERNAME --enable-cleartext-plugin --password=$AZ_MYSQL_ADMIN_PASSWORD < create_user.sql
Now use the following command to remove the temporary SQL script file:
rm create_user.sql
Create a reactive Spring Boot application
To create a reactive Spring Boot application, we'll use Spring Initializr. The application that we'll create uses:
- Spring Boot 2.7.11.
- The following dependencies: Spring Reactive Web (also known as Spring WebFlux) and Spring Data R2DBC.
Generate the application by using Spring Initializr
Generate the application on the command line by entering:
curl https://start.spring.io/starter.tgz -d dependencies=webflux,data-r2dbc -d baseDir=azure-database-workshop -d bootVersion=2.7.11 -d javaVersion=17 | tar -xzvf -
Add the reactive MySQL driver implementation
Open the generated project's pom.xml file to add the reactive MySQL driver from the r2dbc-mysql repository on GitHub.
After the spring-boot-starter-webflux
dependency, add the following snippet:
<dependency>
<groupId>io.asyncer</groupId>
<artifactId>r2dbc-mysql</artifactId>
<version>0.9.1</version>
</dependency>
Configure Spring Boot to use Azure Database for MySQL
Open the src/main/resources/application.properties file, and add:
logging.level.org.springframework.data.r2dbc=DEBUG
spring.r2dbc.url=r2dbc:pool:mysql://$AZ_DATABASE_NAME.mysql.database.azure.com:3306/demo?tlsVersion=TLSv1.2
spring.r2dbc.username=spring-non-admin
spring.r2dbc.password=$AZ_MYSQL_NON_ADMIN_PASSWORD
Replace the $AZ_DATABASE_NAME
and $AZ_MYSQL_NON_ADMIN_PASSWORD
variables with the values that you configured at the beginning of this article.
Note
For better performance, the spring.r2dbc.url
property is configured to use a connection pool using r2dbc-pool.
You should now be able to start your application by using the provided Maven wrapper:
./mvnw spring-boot:run
Here's a screenshot of the application running for the first time:
Create the database schema
Inside the main DemoApplication
class, configure a new Spring bean that will create a database schema, using the following code:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.r2dbc.connectionfactory.init.ConnectionFactoryInitializer;
import org.springframework.data.r2dbc.connectionfactory.init.ResourceDatabasePopulator;
import io.r2dbc.spi.ConnectionFactory;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(new ClassPathResource("schema.sql"));
initializer.setDatabasePopulator(populator);
return initializer;
}
}
This Spring bean uses a file called schema.sql, so create that file in the src/main/resources folder, and add the following text:
DROP TABLE IF EXISTS todo;
CREATE TABLE todo (id SERIAL PRIMARY KEY, description VARCHAR(255), details VARCHAR(4096), done BOOLEAN);
Stop the running application, and start it again. The application will now use the demo
database that you created earlier, and create a todo
table inside it.
./mvnw spring-boot:run
Here's a screenshot of the database table as it's being created:
Code the application
Next, add the Java code that will use R2DBC to store and retrieve data from your MySQL server.
Create a new Todo
Java class, next to the DemoApplication
class, using the following code:
package com.example.demo;
import org.springframework.data.annotation.Id;
public class Todo {
public Todo() {
}
public Todo(String description, String details, boolean done) {
this.description = description;
this.details = details;
this.done = done;
}
@Id
private Long id;
private String description;
private String details;
private boolean done;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
public boolean isDone() {
return done;
}
public void setDone(boolean done) {
this.done = done;
}
}
This class is a domain model mapped on the todo
table that you created before.
To manage that class, you need a repository. Define a new TodoRepository
interface in the same package, using the following code:
package com.example.demo;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
public interface TodoRepository extends ReactiveCrudRepository<Todo, Long> {
}
This repository is a reactive repository that Spring Data R2DBC manages.
Finish the application by creating a controller that can store and retrieve data. Implement a TodoController
class in the same package, and add the following code:
package com.example.demo;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/")
public class TodoController {
private final TodoRepository todoRepository;
public TodoController(TodoRepository todoRepository) {
this.todoRepository = todoRepository;
}
@PostMapping("/")
@ResponseStatus(HttpStatus.CREATED)
public Mono<Todo> createTodo(@RequestBody Todo todo) {
return todoRepository.save(todo);
}
@GetMapping("/")
public Flux<Todo> getTodos() {
return todoRepository.findAll();
}
}
Finally, halt the application and start it again using the following command:
./mvnw spring-boot:run
Test the application
To test the application, you can use cURL.
First, create a new "todo" item in the database using the following command:
curl --header "Content-Type: application/json" \
--request POST \
--data '{"description":"configuration","details":"congratulations, you have set up R2DBC correctly!","done": "true"}' \
http://127.0.0.1:8080
This command should return the created item, as shown here:
{"id":1,"description":"configuration","details":"congratulations, you have set up R2DBC correctly!","done":true}
Next, retrieve the data by using a new cURL request with the following command:
curl http://127.0.0.1:8080
This command will return the list of "todo" items, including the item you've created, as shown here:
[{"id":1,"description":"configuration","details":"congratulations, you have set up R2DBC correctly!","done":true}]
Here's a screenshot of these cURL requests:
Congratulations! You've created a fully reactive Spring Boot application that uses R2DBC to store and retrieve data from Azure Database for MySQL.
Clean up resources
To clean up all resources used during this quickstart, delete the resource group by using the following command:
az group delete \
--name $AZ_RESOURCE_GROUP \
--yes
Next steps
To learn more about deploying a Spring Data application to Azure Spring Apps and using managed identity, see Tutorial: Deploy a Spring application to Azure Spring Apps with a passwordless connection to an Azure database.
To learn more about Spring and Azure, continue to the Spring on Azure documentation center.
See also
For more information about Spring Data R2DBC, see Spring's reference documentation.
For more information about using Azure with Java, see Azure for Java developers and Working with Azure DevOps and Java.