54 lines
1.8 KiB
Markdown
54 lines
1.8 KiB
Markdown
# Multi-Repo Transactions
|
|
|
|
Use `RepositoriesContainer` when you need a single atomic transaction spanning multiple repositories.
|
|
|
|
## Creating a container
|
|
|
|
```python
|
|
from metaorm import RepositoriesContainer, RepositorySettings
|
|
|
|
settings = RepositorySettings(dsn="sqlite+aiosqlite:///:memory:")
|
|
container = RepositoriesContainer(settings=settings)
|
|
```
|
|
|
|
## Getting repositories
|
|
|
|
```python
|
|
user_repo = container.get_repository(UserRepository)
|
|
order_repo = container.get_repository(OrderRepository)
|
|
```
|
|
|
|
## Atomic transaction across repositories
|
|
|
|
```python
|
|
async with container.transaction():
|
|
user = await user_repo.create_item(UserTable(name="Alice"))
|
|
await order_repo.create_item(OrderTable(user_id=user.id, total=100))
|
|
```
|
|
|
|
`container.transaction()` stores the session in a `contextvars.ContextVar`. All repository operations within the `async with` block automatically reuse that session. Nested `transaction()` calls yield the same session.
|
|
|
|
## Creating tables via container
|
|
|
|
You can also create tables for multiple repositories at once:
|
|
|
|
```python
|
|
await container.create_tables(UserRepository, OrderRepository)
|
|
```
|
|
|
|
## Nested transactions (savepoints)
|
|
|
|
For partial rollback inside a shared transaction use `container.nested_transaction()` (or `repository.nested_transaction()`). It creates a SQLAlchemy savepoint: an error inside the block rolls back only the savepoint, leaving the outer transaction open for further operations or commit.
|
|
|
|
```python
|
|
async with container.transaction():
|
|
user = await user_repo.create_item(UserTable(name="Bob"))
|
|
try:
|
|
async with container.nested_transaction():
|
|
await order_repo.create_item(OrderTable(user_id=user.id, total=999.99))
|
|
raise ValueError("Rollback nested order")
|
|
except ValueError:
|
|
pass
|
|
# Bob stays, the order is rolled back
|
|
```
|