Фреймворк не может знать, начали ли вы транзакцию. Вы даже можете использовать $db->query('START TRANSACTION')
, о котором фреймворк не знает, потому что он не анализирует выполняемые вами операторы SQL.
Дело в том, что ответственность за отслеживание того, начали ли вы транзакцию или нет, лежит на приложении. Это не то, что фреймворк может сделать.
Я знаю, что некоторые фреймворки пытаются это сделать, и делают такие вещи, как подсчет, например, подсчитывают, сколько раз вы начали транзакцию, только разрешая ее, когда вы сделали коммит или откатили соответствующее количество раз. Но это полностью обманчиво, потому что ни одна из ваших функций не может знать, будет ли это делать коммит или откат, или они находятся на другом уровне вложенности.
(Можете ли вы сказать, что я обсуждал это несколько раз?: -)
edit: Propel - это библиотека доступа к базе данных PHP, которая поддерживает концепцию «внутренней транзакции», которая не фиксируется, когда вы ее указываете. Начало транзакции только увеличивает счетчик, а фиксация / откат уменьшает счетчик. Ниже приведен отрывок из цепочки рассылки, где я описываю несколько сценариев, в которых он терпит неудачу.
Нравится вам это или нет, транзакции являются «глобальными» и не подчиняются объектно-ориентированной инкапсуляции.
Сценарий проблемы # 1
Я звоню commit()
, зафиксированы ли мои изменения? Если я работаю внутри «внутренней транзакции», это не так. Код, управляющий внешней транзакцией, может откатиться, и мои изменения будут отклонены без моего ведома или контроля.
Например:
- Модель A: начать транзакцию
- Модель A: выполнить некоторые изменения
- Модель B: начать транзакцию (без вывода сообщений)
- Модель B: выполнить некоторые изменения
- Модель B: фиксация (без вывода сообщений)
- Модель A: откат (отменяет как изменения модели A, так и изменения модели B)
- Модель B: WTF !? Что случилось с моими изменениями?
Сценарий проблемы # 2
Внутренняя транзакция откатывается, она может отменить законные изменения, сделанные внешней транзакцией. Когда элемент управления возвращается во внешний код, он считает, что его транзакция все еще активна и доступна для принятия. С вашим патчем они могли бы вызвать commit()
, а поскольку transDepth теперь равен 0, он молча установил бы $transDepth
в -1 и вернул бы true, если ничего не фиксировать.
Сценарий проблемы # 3
Если я позвоню commit()
или rollback()
, когда транзакция не активна, для $transDepth
устанавливается -1. Следующий beginTransaction()
увеличивает уровень до 0, что означает, что транзакция не может быть ни откатана, ни зафиксирована. Последующие вызовы commit()
просто уменьшат транзакцию до -1 или более, и вы никогда не сможете зафиксировать, пока не сделаете еще один лишний beginTransaction()
, чтобы снова увеличить уровень.
По сути, попытка управлять транзакциями в логике приложения, не позволяя базе данных вести бухгалтерский учет, является обреченной идеей. Если для двух моделей требуется использовать явное управление транзакциями в одном запросе приложения, необходимо открыть два соединения с БД, по одному для каждой модели. Тогда каждая модель может иметь собственную активную транзакцию, которая может быть зафиксирована или откатана независимо друг от друга.
(см. http://www.nabble.com/Zend-Framework-Db-Table-ORM-td19691776.html)