O atributo Connection.autocommit do módulo sqlite3 torna o controle de transações mais explícito no Python. Ele ajuda a decidir quando uma conexão deve seguir o comportamento recomendado pelo padrão DB-API, quando deve operar no modo de autocommit nativo do SQLite e como os métodos commit() e rollback() devem se comportar. Essa clareza é especialmente útil em aplicações web, scripts de importação, testes automatizados e serviços que precisam evitar transações abertas por tempo demais.
O que é autocommit no sqlite3
Uma transação agrupa várias operações para que elas sejam confirmadas ou desfeitas como uma unidade. Sem um controle claro, um programa pode deixar alterações pendentes, bloquear outro processo ou perder dados quando a conexão é encerrada. O atributo autocommit permite configurar esse comportamento diretamente na conexão.
Em versões modernas do Python, a conexão pode usar três estratégias principais. Com autocommit=False, a conexão segue o modelo recomendado pela DB-API e mantém uma transação disponível, abrindo uma nova após cada confirmação ou reversão. Com autocommit=True, o SQLite fica no modo de autocommit de baixo nível: cada instrução que não inicia uma transação explícita é confirmada automaticamente. Já sqlite3.LEGACY_TRANSACTION_CONTROL preserva o comportamento antigo governado por isolation_level.
Criando uma conexão com comportamento explícito
import sqlite3
con = sqlite3.connect("app.db", autocommit=False)
try:
con.execute("CREATE TABLE IF NOT EXISTS clientes (id INTEGER PRIMARY KEY, nome TEXT)")
con.execute("INSERT INTO clientes (nome) VALUES (?)", ("Ana",))
con.commit()
except Exception:
con.rollback()
raise
finally:
con.close()
Nesse exemplo, as operações ficam dentro de uma transação controlada pela aplicação. Se tudo funcionar, commit() persiste os dados. Se ocorrer uma exceção, rollback() desfaz o lote. Esse padrão é adequado quando várias instruções precisam ser atômicas.
Quando usar autocommit=True
O modo autocommit=True é útil para comandos independentes, tarefas administrativas e leituras em que manter uma transação aberta não traz benefício. Ele também pode simplificar sistemas nos quais cada gravação é autônoma. Entretanto, não é uma boa escolha quando várias alterações precisam acontecer juntas.
import sqlite3
with sqlite3.connect("logs.db", autocommit=True) as con:
con.execute("CREATE TABLE IF NOT EXISTS logs (mensagem TEXT)")
con.execute("INSERT INTO logs VALUES (?)", ("serviço iniciado",))
Com o autocommit nativo ativo, chamar commit() ou rollback() não confirma nem desfaz instruções já concluídas fora de uma transação explícita. Para um bloco realmente atômico, é necessário executar BEGIN, realizar as operações e finalizar com COMMIT ou ROLLBACK.
Diferença entre autocommit e isolation_level
O atributo isolation_level pertence ao mecanismo legado. Quando autocommit está definido como LEGACY_TRANSACTION_CONTROL, valores como DEFERRED, IMMEDIATE e EXCLUSIVE influenciam a abertura implícita de transações. Quando o novo controle é usado com True ou False, o atributo autocommit deve ser tratado como a fonte principal da política transacional.
Em projetos novos, explicitar autocommit reduz ambiguidades. Em projetos antigos, a migração deve ser acompanhada de testes porque código que dependia de uma abertura implícita pode passar a confirmar operações em momentos diferentes.
Verificando o estado real da transação
O atributo Connection.in_transaction informa se existe uma transação SQLite ativa no nível de baixo nível. Ele não é sinônimo do valor de autocommit. Uma conexão configurada com autocommit=True pode ficar temporariamente em transação após um BEGIN explícito.
con = sqlite3.connect("app.db", autocommit=True)
print(con.in_transaction) # normalmente False
con.execute("BEGIN")
print(con.in_transaction) # True
con.execute("UPDATE clientes SET nome = ? WHERE id = ?", ("Bia", 1))
con.execute("COMMIT")
print(con.in_transaction) # False
Context manager e fechamento da conexão
Usar a conexão em um bloco with ajuda a confirmar ou reverter uma transação quando o bloco termina, mas não substitui o fechamento da conexão. Para garantir liberação imediata do arquivo e dos locks, feche a conexão explicitamente ou combine o uso com contextlib.closing. Veja também o guia sobre contextlib no Python para entender melhor gerenciadores de contexto.
Concorrência e bloqueios
O SQLite permite vários leitores, mas apenas um gravador por vez. Transações longas aumentam a chance de mensagens como database is locked. Faça validações e transformações antes de iniciar a gravação, mantenha o bloco transacional curto e escolha um timeout adequado. Para tarefas paralelas, consulte o artigo sobre ProcessPoolExecutor e workers e o guia de filas asyncio.
Boas práticas para aplicações reais
Defina a política na chamada de connect(), em vez de depender do padrão da versão instalada. Use parâmetros SQL para valores, nunca concatenação de strings. Agrupe somente operações que realmente precisam ser atômicas. Registre falhas de commit e rollback. Em importações grandes, processe em lotes menores para reduzir locks e uso de memória. O artigo sobre itertools.batched com strict mostra uma forma segura de organizar lotes.
Também é importante testar interrupções. Simule uma exceção entre duas gravações e confirme que o estado final permanece consistente. Para bancos críticos, faça backup antes de migrações e valide o arquivo com as ferramentas do próprio SQLite.
Compatibilidade entre versões
O parâmetro e o atributo autocommit foram introduzidos para tornar o comportamento transacional mais previsível, mas projetos distribuídos para várias versões do Python devem detectar compatibilidade. Uma estratégia é centralizar a criação de conexões em uma função e oferecer um fallback explícito para o modo legado.
def abrir_banco(caminho: str):
try:
return sqlite3.connect(caminho, autocommit=False)
except TypeError:
return sqlite3.connect(caminho, isolation_level="DEFERRED")
Esse fallback deve ser temporário e acompanhado de testes. A documentação oficial do módulo sqlite3 do Python descreve os detalhes da API, enquanto a página oficial do SQLite sobre transações explica o comportamento do mecanismo subjacente.
Exemplo de função transacional reutilizável
from collections.abc import Iterable
import sqlite3
def salvar_produtos(con: sqlite3.Connection, produtos: Iterable[tuple[str, float]]) -> None:
try:
con.executemany(
"INSERT INTO produtos (nome, preco) VALUES (?, ?)",
produtos,
)
con.commit()
except sqlite3.Error:
con.rollback()
raise
A função não abre nem fecha a conexão, permitindo que a camada chamadora controle o ciclo de vida e combine outras operações na mesma transação. Esse desenho facilita testes e evita commits escondidos.
Conclusão
sqlite3.Connection.autocommit oferece uma forma clara de definir a política transacional de uma aplicação Python. Use False quando a aplicação controla commits e rollbacks de unidades de trabalho, True quando cada instrução independente deve ser confirmada pelo SQLite e o modo legado apenas durante migrações conscientes. Ao combinar configuração explícita, transações curtas, parâmetros SQL, testes de falha e observação de in_transaction, é possível criar integrações SQLite mais previsíveis e seguras.







