Урок 82. MySQL. Транзакции

📁 Блок: Базы данных ⏱️ Время изучения: ~90 мин 🎯 Сложность: Средняя
#from #import #if #not #print #try #except #as

⚡ Кратко: типичные ошибки

  • Забыть commit() после изменяющих запросов.
  • Использовать DictCursor, но обращаться к строке по индексу.
  • Забыть rollback() в блоке except.
  • Ловить общий Exception вместо конкретных исключений.

🐛 Типичные ошибки

1

Забыть вызвать commit()

Без commit() изменения остаются во временном хранилище транзакции и пропадают после закрытия соединения.

Неправильно:

cursor.execute("INSERT INTO products (name, price) VALUES (%s, %s)", ("Pen", 1.00))
# нет commit() — данные не сохранятся

Правильно:

cursor.execute("INSERT INTO products (name, price) VALUES (%s, %s)", ("Pen", 1.00))
connection.commit()
2

Использовать DictCursor, но обращаться по индексу

Если подключение настроено на DictCursor, каждая строка — словарь. Обращение по индексу вызовет KeyError или неожиданный результат.

Неправильно:

# cursorclass=DictCursor
row = cursor.fetchone()
print(row[0])  # ошибка: row — словарь, не кортеж

Правильно:

row = cursor.fetchone()
print(row["id"])
print(row["name"])
3

Забыть rollback() при ошибке

Если в транзакции произошла ошибка, а rollback() не вызван, часть изменений может остаться в базе, а часть — нет. Данные окажутся в неконсистентном состоянии.

Неправильно:

try:
    cursor.execute("UPDATE products SET stock = stock - 1 WHERE id = %s", (product_id,))
    cursor.execute("UPDATE customers SET balance = balance - %s WHERE id = %s", (price, customer_id))
    connection.commit()
except Exception as e:
    # нет rollback() — часть изменений может сохраниться
    print("Transaction failed:", e)

Правильно:

try:
    cursor.execute("UPDATE products SET stock = stock - 1 WHERE id = %s", (product_id,))
    cursor.execute("UPDATE customers SET balance = balance - %s WHERE id = %s", (price, customer_id))
    connection.commit()
except Exception as e:
    connection.rollback()
    print("Transaction failed:", e)
4

Ловить общий Exception

Хотя в учебных примерах часто используется except Exception, в реальных проектах это может скрыть неожиданные ошибки. Лучше ловить конкретные исключения.

Неправильно:

try:
    connection = pymysql.connect(**config)
except Exception as e:
    print("Error:", e)

Правильно:

import pymysql

try:
    connection = pymysql.connect(**config)
except pymysql.MySQLError as e:
    print("Database error:", e)
⚠️ Проверить по документации: в рабочем коде обычно разделяют ошибки подключения (OperationalError), ошибки SQL (ProgrammingError) и ошибки бизнес-логики (ValueError). Общий Exception допустим только на самом верхнем уровне программы.
5

Забыть выбрать базу через USE

Если при подключении не указана база и не выполнен USE, запросы к таблицам вызовут ошибку.

Неправильно:

config = {"host": "...", "user": "...", "password": "..."}
connection = pymysql.connect(**config)
with connection.cursor() as cursor:
    cursor.execute("SELECT * FROM employees")  # No database selected

Правильно:

with connection.cursor() as cursor:
    cursor.execute("USE hr")
    cursor.execute("SELECT * FROM employees")