JavaRush /Курсы /Модуль 4: FastAPI /Основы транзакций в SQL и их использование в SQLAlchemy

Основы транзакций в SQL и их использование в SQLAlchemy

Модуль 4: FastAPI
11 уровень , 0 лекция
Открыта

С транзакциями в реальной жизни вы сталкивались неоднократно. Допустим, вы собираетесь что-то купить и оплатить покупку банковской картой. Пускай это будет мороженое. Вы прикладываете карту к терминалу, тем самым отправляя запрос в банк, банк проверяет баланс, списывает нужную сумму, и только потом сделка считается завершённой. Если какой-то из этих шагов не выполнен, ваши деньги останутся на счёте, и вы не получите мороженое. Каждый такой шаг — это часть операции, а вся последовательность шагов — это транзакция.

Транзакция — это группа операций, которые выполняются как единое целое. Либо все операции транзакции завершаются успешно (commit), либо в случае ошибки откатываются (rollback), оставляя систему в исходном состоянии. Это помогает обеспечить согласованность и целостность данных.


ACID: основы консистентности данных

Термин ACID описывает ключевые свойства транзакций:

  1. Atomicity (Атомарность) — транзакция либо выполняется полностью, либо не выполняется вовсе.
  2. Consistency (Согласованность) — после завершения транзакции система остаётся в согласованном состоянии.
  3. Isolation (Изоляция) — параллельные транзакции не конфликтуют друг с другом.
  4. Durability (Надёжность) — результаты успешной транзакции сохраняются даже в случае сбоя системы.

Без транзакций работа с данными становится хаотичной. Например:

  • Если вы выполняете несколько связанных операций (например, обновляете баланс клиента и записываете транзакцию в журнал), сбой в одной из них приведёт к неконсистентности данных.
  • При параллельной обработке запросов может возникнуть гонка данных (data race), когда несколько пользователей одновременно обновляют одну и ту же запись.

Транзакции спасают нас от подобных проблем, превращая базу данных в спокойное место.


Особенности транзакций в SQLAlchemy

SQLAlchemy предоставляет удобный механизм для работы с транзакциями. Ключевые действия:

  • Начало транзакции. Операция начинается автоматически, когда вы выполняете первую команду (например, добавление или изменение данных).
  • Фиксация изменений (commit). Если все операции успешны, изменения фиксируются в базе данных.
  • Откат изменений (rollback). Если что-то пошло не так, все изменения откатываются, база возвращается к исходному состоянию.

Основные методы

SQLAlchemy поддерживает два подхода для работы с транзакциями:

  1. Явное управление транзакциями. Вы вручную начинаете транзакцию и используете commit или rollback.
  2. Контекстный менеджер. Более лаконичный способ, который автоматически обрабатывает ошибки и управляет транзакцией.

Примеры использования транзакций

Давайте поработаем с кодом, потому что мир транзакций лучше всего раскрывается на практике. Рассмотрим несколько интересных примеров.

Пример: ручное управление транзакцией

Вот пример явного управления транзакцией:


from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base

# Создаём базу данных SQLite
engine = create_engine('sqlite:///example.db')
Base = declarative_base()

# Определяем модель
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)

# Создаём таблицы
Base.metadata.create_all(engine)

# Создаём сессию
Session = sessionmaker(bind=engine)
session = Session()

# Начинаем транзакцию
try:
    new_user = User(name="Alice")
    session.add(new_user)
    session.commit()  # Фиксируем транзакцию
    print("Пользователь сохранён!")
except Exception as e:
    session.rollback()  # Откат транзакции в случае ошибки
    print(f"Произошла ошибка: {e}")
finally:
    session.close()

В этом примере:

  • Мы добавляем нового пользователя.
  • Если возникает ошибка при добавлении, изменения откатываются.

Пример: использование контекстного менеджера

SQLAlchemy значительно упрощает работу с транзакциями через контекстный менеджер:


with Session() as session:
    try:
        new_user = User(name="Bob")
        session.add(new_user)
        session.commit()
        print("Пользователь сохранён!")
    except Exception as e:
        session.rollback()
        print(f"Произошла ошибка: {e}")

Этот код делает то же самое, но выглядит чище. Контекстный менеджер автоматически закрывает сессию, а вы управляете только фиксацией или откатом.


Как применять транзакции для выполнения нескольких операций

Транзакции полезны, если вам нужно выполнить несколько связанных операций. Например, если мы создаём заказ в интернет-магазине:

  1. Уменьшить количество товара на складе.
  2. Создать запись о заказе.
  3. Списать деньги с баланса пользователя.

Эти операции зависят друг от друга. Если одна из них не удалась, система должна вернуться к исходному состоянию.

Пример с несколькими операциями


from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, declarative_base, relationship

# Настраиваем базу данных
engine = create_engine('sqlite:///shop.db')
Base = declarative_base()
Session = sessionmaker(bind=engine)

# Модели
class Product(Base):
    __tablename__ = 'products'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    stock = Column(Integer)

class Order(Base):
    __tablename__ = 'orders'
    id = Column(Integer, primary_key=True)
    product_id = Column(Integer, ForeignKey('products.id'))
    quantity = Column(Integer)

# Создаём таблицы
Base.metadata.create_all(engine)

# Пример транзакции
with Session() as session:
    try:
        # Уменьшаем остатки на складе
        product = session.query(Product).filter_by(name="Laptop").first()
        if product.stock < 1:
            raise ValueError("Товара нет в наличии!")
        product.stock -= 1

        # Создаём заказ
        order = Order(product_id=product.id, quantity=1)
        session.add(order)
        
        # Фиксируем изменения
        session.commit()
        print("Заказ успешно создан!")
    except Exception as e:
        # Откат в случае ошибки
        session.rollback()
        print(f"Не удалось создать заказ: {e}")

Здесь мы:

  1. Проверяем доступность товара на складе.
  2. Уменьшаем количество.
  3. Создаём заказ.
  4. В случае ошибки откатываем все изменения.

Подводные камни и типичные ошибки

Работа с транзакциями может быть сложной, если не учитывать детали:

  1. Многопоточность. SQLAlchemy-сессии не потокобезопасны, поэтому для каждого потока/запроса создавайте отдельную сессию.
  2. Повторное использование сессии. Не пытайтесь повторно использовать одну и ту же сессию после ошибки.
  3. Сбой после commit. Если ошибка произошла после фиксации транзакции, откатить изменения уже невозможно.

Практическое применение

Транзакции — это не просто теория, они играют жизненно важную роль в реальных проектах:

  • Интернет-магазины. Управление заказами, балансами и списанием товаров.
  • Финансовые приложения. Переводы, управление балансом и обработка платёжных операций.
  • CRM-системы. Одновременное обновление данных и связанных сущностей.

Когда вы освоите работу с транзакциями, вы сможете проектировать системы, устойчивые к сбоям и ошибкам, что сделает ваше приложение настоящим шедевром инженерной мысли.

Подробнее о транзакциях в SQLAlchemy можно прочитать в документации.

1
Задача
Модуль 4: FastAPI, 11 уровень, 0 лекция
Недоступна
Создание и откат транзакции
Создание и откат транзакции
1
Задача
Модуль 4: FastAPI, 11 уровень, 0 лекция
Недоступна
Упрощённое управление транзакциями через контекстный менеджер
Упрощённое управление транзакциями через контекстный менеджер
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ