19 de agosto de 2026 • Java

14 - Isolamento de Transação do Spring

Introdução

Quando vários usuários ou processos acessam os mesmos dados concomitantemente, podem ocorrer problemas inesperados. Uma transação pode ler dados que outra transação está modificando, resultando em resultados inconsistentes ou incorretos.

Para resolver esses problemas de concorrência, bancos de dados relacionais fornecem Níveis de Isolamento de Transação.

No Spring, o isolamento de transação pode ser configurado através da anotação @Transactional, permitindo que os desenvolvedores controlem como as transações interagem umas com as outras.

Compreender os níveis de isolamento é essencial para construir aplicações confiáveis, escaláveis e consistentes.


O que é Isolamento de Transação?

O Isolamento de Transação define como e quando as alterações feitas por uma transação se tornam visíveis para outras transações.

Ele controla o grau de separação entre transações sendo executadas concomitantemente.

O objetivo é equilibrar:

  • Consistência dos dados
  • Performance
  • Concorrência

Níveis de isolamento mais altos fornecem maior consistência, mas podem reduzir a performance e a escalabilidade.

Níveis de isolamento mais baixos melhoram a concorrência, mas podem permitir leituras inconsistentes.


Por Que o Isolamento de Transação Importa

Imagine uma aplicação bancária.

Transação A:

UPDATE account
SET balance = balance - 100
WHERE id = 1;

Transação B:

SELECT balance
FROM account
WHERE id = 1;

Se a Transação B lê o saldo antes da Transação A confirmar (commit), ela pode ver dados que ainda não foram finalizados.

Isso pode levar a decisões de negócio incorretas e comportamento inconsistente da aplicação.

Os níveis de isolamento ajudam a prevenir essas situações.


Problemas Comuns de Concorrência

Antes de entender os níveis de isolamento, é importante entender os problemas que eles resolvem.


Leitura Suja (Dirty Read)

Uma transação lê dados modificados por outra transação que ainda não foi confirmada.

Exemplo

Transação A:

UPDATE account
SET balance = 500
WHERE id = 1;

Transação B:

SELECT balance
FROM account
WHERE id = 1;

Transação A posteriormente reverte (rollback):

ROLLBACK;

A Transação B leu dados que nunca existiram de fato.

Problema

Saldo Original = 1000
Saldo Temporário = 500
Reversão
Saldo Real = 1000

A Transação B usou dados inválidos.


Leitura Não Repetível (Non-Repeatable Read)

Uma transação lê a mesma linha duas vezes e obtém resultados diferentes.

Exemplo

Transação A:

SELECT balance
FROM account
WHERE id = 1;

Resultado:

1000

Transação B:

UPDATE account
SET balance = 800
WHERE id = 1;

COMMIT;

Transação A executa novamente:

SELECT balance
FROM account
WHERE id = 1;

Resultado:

800

A mesma consulta retornou valores diferentes dentro da mesma transação.


Leitura Fantasma (Phantom Read)

Uma transação executa a mesma consulta duas vezes e recebe diferentes conjuntos de linhas.

Exemplo

Transação A:

SELECT *
FROM orders
WHERE status = 'PENDING';

Retorna:

10 linhas

Transação B:

INSERT INTO orders(status)
VALUES('PENDING');

COMMIT;

Transação A executa a consulta novamente:

SELECT *
FROM orders
WHERE status = 'PENDING';

Retorna:

11 linhas

Uma nova linha apareceu como um fantasma.


Atualização Perdida (Lost Update)

Duas transações atualizam o mesmo registro simultaneamente.

Exemplo

Valor inicial:

Estoque = 10

Transação A:

Lê 10

Transação B:

Lê 10

Transação A:

Atualiza para 9

Transação B:

Atualiza para 8

Esperado:

7

Real:

8

Uma atualização foi perdida.


Níveis de Isolamento no Spring

O Spring suporta os níveis de isolamento SQL padrão através de:

@Transactional(isolation = Isolation.READ_COMMITTED)

Níveis disponíveis:

  • DEFAULT
  • READ_UNCOMMITTED
  • READ_COMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

Isolation.DEFAULT

O Que Faz

Usa o nível de isolamento padrão do banco de dados.

@Transactional(isolation = Isolation.DEFAULT)
public void processOrder() {
}

Exemplos de Padrões

Banco de DadosIsolamento Padrão
PostgreSQLREAD_COMMITTED
OracleREAD_COMMITTED
SQL ServerREAD_COMMITTED
MySQL InnoDBREPEATABLE_READ

Vantagens

  • Usa as melhores práticas do banco de dados
  • Configuração simples

Desvantagens

  • O comportamento pode variar entre bancos de dados

Isolation.READ_UNCOMMITTED

O Que Faz

Permite que as transações leiam dados não confirmados de outras transações.

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void generateReport() {
}

Previne

  • Nada

Permite

  • Leituras Sujas
  • Leituras Não Repetíveis
  • Leituras Fantasma

Exemplo

Transação A:

UPDATE account
SET balance = 500;

Ainda não confirmada.

Transação B:

SELECT balance FROM account;

Resultado:

500

Se a Transação A reverte, a Transação B usou dados inválidos.

Vantagens

  • Performance máxima
  • Maior concorrência

Desvantagens

  • Menor consistência
  • Raramente recomendado

Isolation.READ_COMMITTED

O Que Faz

Permite apenas a leitura de dados confirmados.

@Transactional(isolation = Isolation.READ_COMMITTED)
public void processPayment() {
}

Previne

  • Leituras Sujas

Permite

  • Leituras Não Repetíveis
  • Leituras Fantasma

Exemplo

A Transação A atualiza dados, mas não confirma.

A Transação B tenta ler.

Resultado:

A Transação B não pode ver alterações não confirmadas.

Apenas valores confirmados são visíveis.

Vantagens

  • Bom equilíbrio entre consistência e performance
  • Padrão mais comum de bancos de dados

Desvantagens

  • A mesma linha pode retornar valores diferentes dentro de uma transação

Isolation.REPEATABLE_READ

O Que Faz

Garante que leituras repetidas da mesma linha retornem o mesmo resultado.

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void generateStatement() {
}

Previne

  • Leituras Sujas
  • Leituras Não Repetíveis

Permite

  • Leituras Fantasma (dependendo da implementação do banco de dados)

Exemplo

Transação A:

SELECT balance
FROM account
WHERE id = 1;

Transação B:

UPDATE account
SET balance = 500
WHERE id = 1;

Transação A lê novamente:

SELECT balance
FROM account
WHERE id = 1;

Ainda vê:

Valor Original

Vantagens

  • Consistência mais forte
  • Bom para operações financeiras

Desvantagens

  • Mais travamento (locking)
  • Concorrência reduzida

Isolation.SERIALIZABLE

O Que Faz

Fornece o mais alto nível de isolamento.

As transações se comportam como se fossem executadas uma por vez.

@Transactional(isolation = Isolation.SERIALIZABLE)
public void transferMoney() {
}

Previne

  • Leituras Sujas
  • Leituras Não Repetíveis
  • Leituras Fantasma

Exemplo

Transação A:

SELECT *
FROM orders
WHERE status = 'PENDING';

Enquanto a Transação A está em execução:

Transação B:

INSERT INTO orders(status)
VALUES('PENDING');

A Transação B deve esperar.

Vantagens

  • Consistência máxima
  • Previne todos os problemas padrão de concorrência

Desvantagens

  • Menor concorrência
  • Mais travamento (locking)
  • Vazão (throughput) reduzida

Comparação de Níveis de Isolamento

Nível de IsolamentoLeitura SujaLeitura Não RepetívelLeitura Fantasma
READ_UNCOMMITTEDPossívelPossívelPossível
READ_COMMITTEDPrevenidaPossívelPossível
REPEATABLE_READPrevenidaPrevenidaPossível*
SERIALIZABLEPrevenidaPrevenidaPrevenida
  • Dependente do banco de dados.

Configurando o Isolamento no Spring

Nível de Método

@Transactional(
    isolation = Isolation.REPEATABLE_READ
)
public void processPayment() {
}

Combinado com Propagação

@Transactional(
    propagation = Propagation.REQUIRED,
    isolation = Isolation.READ_COMMITTED
)
public void createOrder() {
}

Combinado com Timeout

@Transactional(
    isolation = Isolation.SERIALIZABLE,
    timeout = 30
)
public void transferFunds() {
}

Exemplos do Mundo Real

Transferências Bancárias

Recomendado:

@Transactional(
    isolation = Isolation.SERIALIZABLE
)
public void transferMoney() {
}

Motivo:

  • Consistência máxima
  • Sem atualizações perdidas

Processamento de Pedidos

Recomendado:

@Transactional(
    isolation = Isolation.READ_COMMITTED
)
public void placeOrder() {
}

Motivo:

  • Boa performance
  • Previne leituras sujas

Sistemas de Relatórios

Recomendado:

@Transactional(
    readOnly = true,
    isolation = Isolation.READ_COMMITTED
)
public Report generateReport() {
}

Motivo:

  • Execução mais rápida
  • Consistente o suficiente para relatórios

Gerenciamento de Estoque

Recomendado:

@Transactional(
    isolation = Isolation.REPEATABLE_READ
)
public void reserveInventory() {
}

Motivo:

  • Previne cálculos de estoque inconsistentes

Erros Comuns

Usar Sempre SERIALIZABLE

Má prática:

@Transactional(
    isolation = Isolation.SERIALIZABLE
)
public void findUser() {
}

Problemas:

  • Travamento excessivo (excessive locking)
  • Pobre escalabilidade
  • Performance reduzida

Use apenas quando absolutamente necessário.


Ignorar os Padrões do Banco de Dados

Desenvolvedores frequentemente assumem que todos os bancos de dados se comportam da mesma forma.

Exemplo:

PostgreSQL -> READ_COMMITTED
MySQL -> REPEATABLE_READ

O comportamento da aplicação pode mudar após a migração.

Sempre verifique os padrões do banco de dados.


Usar Alto Isolamento para Consultas Somente Leitura

Ruim:

@Transactional(
    isolation = Isolation.SERIALIZABLE,
    readOnly = true
)
public List<User> findUsers() {
}

Isso oferece pouco benefício enquanto aumenta o uso de recursos.


Melhores Práticas

Comece com READ_COMMITTED

Para a maioria das aplicações:

@Transactional(
    isolation = Isolation.READ_COMMITTED
)
public void processOrder() {
}

Este é geralmente o melhor equilíbrio.


Use REPEATABLE_READ para Cálculos Financeiros

Quando a consistência importa mais do que a vazão (throughput):

@Transactional(
    isolation = Isolation.REPEATABLE_READ
)
public void calculateInterest() {
}

Reserve SERIALIZABLE para Operações de Negócio Críticas

Exemplos:

  • Transferências bancárias
  • Transações financeiras de alto valor
  • Reservas críticas de estoque

Mantenha as Transações Curtas

Bom:

@Transactional
public void saveOrder() {
    repository.save(order);
}

Evite:

@Transactional
public void saveOrder() {

    repository.save(order);

    externalApi.call();

    Thread.sleep(30000);
}

Transações de longa duração aumentam o travamento (locking) e a contenção.


Entenda Seu Banco de Dados

Diferentes bancos de dados implementam o isolamento de maneiras distintas.

Sempre verifique:

  • Nível de isolamento padrão
  • Comportamento de travamento (locking)
  • Implementação MVCC
  • Implicações de performance

Resumo

O Isolamento de Transação controla como as transações concorrentes interagem entre si e determina quais dados são visíveis durante a execução da transação.

O Spring suporta cinco níveis de isolamento:

  • DEFAULT
  • READ_UNCOMMITTED
  • READ_COMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

À medida que o isolamento aumenta, a consistência dos dados melhora, mas a concorrência e a performance geralmente diminuem.

Para a maioria das aplicações corporativas:

  • Use READ_COMMITTED como a escolha padrão.
  • Use REPEATABLE_READ quando leituras repetidas consistentes são necessárias.
  • Use SERIALIZABLE apenas para operações críticas que exigem consistência máxima.

Escolher o nível de isolamento correto é uma das decisões mais importantes no design de sistemas transacionais confiáveis e escaláveis.

← Voltar para o blog