Spring-style transactional boundaries for SQLAlchemy async sessions.
Project description
sqlalchemy-transactional
Spring-style transactional boundaries for SQLAlchemy async sessions.
Inspired by Spring Framework's @Transactional model.
Quick Start (FastAPI)
Assume you already run FastAPI with async_sessionmaker, and want Spring-like declarative transaction boundaries in service methods.
from fastapi import FastAPI, Request
from pydantic import BaseModel
from sqlalchemy import text
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from sqlalchemy_transactional.asyncio import (
current_session,
sessionmaker_context,
transactional,
)
from sqlalchemy_transactional.common import Propagation
app = FastAPI()
engine = create_async_engine("sqlite+aiosqlite:///app.db")
sessionmaker = async_sessionmaker(engine, expire_on_commit=False)
# 1) Bind async_sessionmaker once per request boundary
@app.middleware("http")
async def transactional_context_middleware(request: Request, call_next):
async with sessionmaker_context(sessionmaker):
return await call_next(request)
class CreateUserRequest(BaseModel):
name: str
# 2) Put transaction boundaries on service methods
class UserService:
@transactional # default propagation = REQUIRED
async def create_user(self, name: str) -> None:
await self._validate_name(name)
await self._insert_user(name)
@transactional(Propagation.MANDATORY)
async def _validate_name(self, name: str) -> None:
result = await current_session().execute(
text("SELECT 1 FROM users WHERE name = :name"),
{"name": name},
)
if result.first() is not None:
raise ValueError("name already exists")
@transactional(Propagation.MANDATORY)
async def _insert_user(self, name: str) -> None:
await current_session().execute(
text("INSERT INTO users (name) VALUES (:name)"),
{"name": name},
)
user_service = UserService()
# 3) Route/controller stays thin
@app.post("/users")
async def create_user(payload: CreateUserRequest) -> dict[str, str]:
await user_service.create_user(payload.name)
return {"status": "ok"}
Spring-to-FastAPI Mapping
| Spring concept | FastAPI + sqlalchemy-transactional |
|---|---|
@Transactional on service methods |
@transactional on async service methods |
@Transactional(propagation = REQUIRED) |
@transactional (default = Propagation.REQUIRED) |
@Transactional(propagation = MANDATORY) |
@transactional(Propagation.MANDATORY) |
| Request filter/interceptor binds tx resources | @app.middleware("http") + sessionmaker_context(sessionmaker) |
| Current tx-bound resource lookup | current_session() |
Why Use This
- Keep transaction plumbing out of service code.
- Declare transaction scope at the function boundary.
- Use familiar propagation semantics from Spring (
REQUIRED,MANDATORY, etc.). - Apply isolation level declaratively when needed.
- Make service methods easier to read, test, and review.
Propagation Modes
REQUIRED(default): Join an active transaction, or create one if none exists.MANDATORY: Require an active transaction; raise an error if missing.REQUIRES_NEW: Always execute in a new transaction.NESTED: Use savepoint semantics inside an active transaction; otherwise behave likeREQUIRED.
Isolation Level
Set isolation level at the decorator boundary:
@transactional(isolation_level="SERIALIZABLE")
async def run_settlement() -> None:
await current_session().execute(...)
If isolation_level is omitted (None, the default), SQLAlchemy uses the engine/dialect default isolation behavior from your database driver.
Combine with propagation when needed:
@transactional(Propagation.REQUIRES_NEW, isolation_level="SERIALIZABLE")
async def write_critical_audit_log() -> None:
await current_session().execute(...)
When isolation_level is applied:
- The decorator creates a new transaction (
REQUIRES_NEW, orREQUIRED/NESTEDwhen no active transaction exists).
When isolation_level is not applied by this decorator:
- The function joins an already active transaction (
REQUIREDwith an active transaction,MANDATORY, orNESTEDwith an active transaction).
Contributing
Development workflow, checks, and test policies are defined in AGENTS.md.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sqlalchemy_transactional-1.0.1.tar.gz.
File metadata
- Download URL: sqlalchemy_transactional-1.0.1.tar.gz
- Upload date:
- Size: 5.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7cb1c41848fd09bc59f3ff527ac39331345bd3e2e78a6e3bf4bfde8d4b2d2263
|
|
| MD5 |
50e4a179c68db8f442c725feeaba4f2b
|
|
| BLAKE2b-256 |
c03f346e902b5222cc14628844c746b541e66e5ab447de3a0d8aa97d11c111c5
|
Provenance
The following attestation bundles were made for sqlalchemy_transactional-1.0.1.tar.gz:
Publisher:
pypi-publish.yml on harryplusplus/sqlalchemy-transactional
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqlalchemy_transactional-1.0.1.tar.gz -
Subject digest:
7cb1c41848fd09bc59f3ff527ac39331345bd3e2e78a6e3bf4bfde8d4b2d2263 - Sigstore transparency entry: 928461658
- Sigstore integration time:
-
Permalink:
harryplusplus/sqlalchemy-transactional@1220ae6af5f6ac47ee624b508c8d5fcf1899cec5 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/harryplusplus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@1220ae6af5f6ac47ee624b508c8d5fcf1899cec5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sqlalchemy_transactional-1.0.1-py3-none-any.whl.
File metadata
- Download URL: sqlalchemy_transactional-1.0.1-py3-none-any.whl
- Upload date:
- Size: 7.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67616f8227ce57393e4a9b61943540f9d1cf18394cba94c077b6cc1c97aaf2db
|
|
| MD5 |
2ad88668f8423d17b7f53f337b4adacb
|
|
| BLAKE2b-256 |
228c2ab6c3de017fb1a7bbb188e4b99e9867da7d42f23a771b5d7edd004776db
|
Provenance
The following attestation bundles were made for sqlalchemy_transactional-1.0.1-py3-none-any.whl:
Publisher:
pypi-publish.yml on harryplusplus/sqlalchemy-transactional
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqlalchemy_transactional-1.0.1-py3-none-any.whl -
Subject digest:
67616f8227ce57393e4a9b61943540f9d1cf18394cba94c077b6cc1c97aaf2db - Sigstore transparency entry: 928461659
- Sigstore integration time:
-
Permalink:
harryplusplus/sqlalchemy-transactional@1220ae6af5f6ac47ee624b508c8d5fcf1899cec5 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/harryplusplus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@1220ae6af5f6ac47ee624b508c8d5fcf1899cec5 -
Trigger Event:
push
-
Statement type: