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 можна прочитати в документації.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ