Como fazer operações assíncronas em um banco de dados com Java?

Operações assíncronas em Java permitem executar consultas ao banco de dados sem bloquear a aplicação, melhorando a escalabilidade e o desempenho.

Como fazer operações assíncronas em um banco de dados com Java?

Em aplicações modernas, a assincronicidade é fundamental para melhorar performance e escalabilidade. No contexto de bancos de dados, executar operações de forma assíncrona permite que a aplicação continue processando outras tarefas enquanto aguarda a resposta do banco.

1. O Que São Operações Assíncronas?

Normalmente, operações de banco de dados em Java são bloqueantes, ou seja, a thread que faz a requisição aguarda a resposta antes de continuar.

Com operações assíncronas, podemos iniciar uma consulta ao banco e continuar executando outras tarefas sem bloquear a aplicação.

2. Como Executar Operações Assíncronas em Java?

Opção 1: Usando CompletableFuture para Consultas Assíncronas

O CompletableFuture permite executar operações em background e retornar os resultados assim que estiverem prontos.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.concurrent.CompletableFuture;

public class BancoAssincrono {
    public static CompletableFuture<Void> buscarUsuariosAsync() {
        return CompletableFuture.runAsync(() -> {
            try (Connection conexao = DriverManager.getConnection("jdbc:mysql://localhost:3306/meubanco", "root", "1234");
                 PreparedStatement stmt = conexao.prepareStatement("SELECT * FROM usuarios");
                 ResultSet rs = stmt.executeQuery()) {

                while (rs.next()) {
                    System.out.println("Usuário: " + rs.getString("nome"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public static void main(String[] args) {
        buscarUsuariosAsync().thenRun(() -> System.out.println("Consulta concluída!"));
        System.out.println("Enquanto isso, a aplicação continua rodando...");
    }
}

Explicação:

  • CompletableFuture.runAsync() executa a consulta ao banco em uma thread separada.
  • thenRun() permite executar código após a conclusão da consulta.
  • A aplicação continua rodando enquanto a consulta é processada.

Opção 2: Usando Spring Boot com @Async

Se estiver usando Spring Boot, podemos tornar métodos assíncronos com a anotação @Async.

Passo 1: Habilitar @Async na Aplicação
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync // Ativa suporte para métodos assíncronos
public class Aplicacao {
    public static void main(String[] args) {
        SpringApplication.run(Aplicacao.class, args);
    }
}
Passo 2: Criar um Serviço Assíncrono
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;

@Service
public class UsuarioService {

    @Async
    public CompletableFuture<String> buscarUsuarios() {
        // Simulação de consulta ao banco
        try { Thread.sleep(3000); } catch (InterruptedException e) {}
        return CompletableFuture.completedFuture("Lista de usuários carregada!");
    }
}
Passo 3: Chamar o Serviço Assíncrono
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;

@RestController
public class UsuarioController {

    @Autowired
    private UsuarioService usuarioService;

    @GetMapping("/usuarios")
    public CompletableFuture<String> getUsuarios() {
        return usuarioService.buscarUsuarios();
    }
}

Explicação:

  • @Async permite que o método seja executado em background.
  • CompletableFuture.completedFuture() retorna um resultado futuro.
  • O controlador não bloqueia a requisição enquanto o banco responde.

Opção 3: Usando Spring WebFlux e R2DBC (Banco Reativo)

O Spring WebFlux permite executar consultas assíncronas utilizando R2DBC (Reactive Relational Database Connectivity).

Passo 1: Adicionar Dependências (Maven)
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-mysql</artifactId>
    <version>0.8.2.RELEASE</version>
</dependency>
Passo 2: Criar o Repositório Reativo
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import reactor.core.publisher.Flux;

public interface UsuarioRepository extends R2dbcRepository<Usuario, Long> {
    Flux<Usuario> findAll();
}
Passo 3: Criar o Serviço Reativo
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

@Service
public class UsuarioService {
    private final UsuarioRepository usuarioRepository;

    public UsuarioService(UsuarioRepository usuarioRepository) {
        this.usuarioRepository = usuarioRepository;
    }

    public Flux<Usuario> buscarUsuarios() {
        return usuarioRepository.findAll();
    }
}
Passo 4: Criar o Controller Reativo
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/usuarios")
public class UsuarioController {
    private final UsuarioService usuarioService;

    public UsuarioController(UsuarioService usuarioService) {
        this.usuarioService = usuarioService;
    }

    @GetMapping
    public Flux<Usuario> listarUsuarios() {
        return usuarioService.buscarUsuarios();
    }
}

Explicação:

  • Flux<Usuario> retorna uma stream reativa de usuários.
  • findAll() não bloqueia a thread principal.

Conclusão

Operações assíncronas melhoram performance, escalabilidade e eficiência em aplicações Java. Podemos usar CompletableFuture, Spring Async ou Spring WebFlux para lidar com operações de banco de dados de maneira não bloqueante.

A execução assíncrona de operações no banco de dados é essencial para sistemas modernos de alta escalabilidade. Com abordagens como CompletableFuture e Spring WebFlux, podemos melhorar a experiência do usuário e otimizar o uso de recursos do servidor.

Algumas aplicações:

  • Melhoria na escalabilidade de sistemas web
  • Redução do tempo de resposta de APIs
  • Suporte a operações de longa duração
  • Evita bloqueios em aplicações multi-thread

Dicas para quem está começando

  • Use CompletableFuture para operações assíncronas simples
  • Spring Boot permite @Async para consultas não bloqueantes
  • WebFlux e R2DBC são ideais para sistemas altamente escaláveis

Contribuições de Rodrigo Farias

Compartilhe este tutorial: Como fazer operações assíncronas em um banco de dados com Java

Compartilhe este tutorial

Continue aprendendo:

Como configurar um pool de conexões no Java

O pool de conexões no Java permite reutilizar conexões abertas com o banco de dados, reduzindo o tempo de resposta e melhorando a escalabilidade.

Tutorial anterior

Como fazer backup e restauração de dados via Java

A realização de backup e restauração de bancos de dados em Java pode ser feita utilizando comandos SQL, ferramentas externas e bibliotecas JDBC.

Próximo tutorial