не понимаю, зачем в части "сохранение отредактированных элементов" и дальше нужны действия над объектом $DB. Если бы вместо CRubric там была обычная ORM сущность, ее методы update, delete и так вроде все пишут в БД. Или не пишут, если ошибка.
Или это какая-то специфика класса CRubric,и вообще старых классов, которая тянет за собой тяжкое наследие старого ядра, а для D7 ORM сущности эти танцы с бубном вокруг $DB уже не нужны, или я чего-то не понимаю. Собственно, вопрос - не "зачем", а нужны ли эти действия с $DB, если там использовать D7 ORM сущность или ее наследницу?
Да, не понимаете. Операции изменения сущностей (хоть через старое ядро, хоть через orm) могут затрагивать не одну, а несколько строк в БД, а в общем случае - несколько таблиц. Это если даже не вспоминать про обработчики событий. На любом этапе этого процесса ошибка может возникнуть как на стороне php, так и на стороне БД. Механизм транзакций нужен для того, чтобы в таких случаях "откатить" состояние БД к моменту до начала изменений. Почитайте про транзакции в базах данных, станет ясно.
Не надо сверлить зубы через задний проход дрелью от Сваровски
Почитайте про транзакции в базах данных, станет ясно.
Спасибо, стало понятнее, но не до конца. Ок, механизм транзакций нужен, но почему он тогда не реализован внутри методов update, add и т.п. всех сущностей. Т.к. $DB - глобальный, то отлавливать вложенные транзакции (если серию таких методов пользователь еще и сам оборачивает транзакцией) вроде не проблема - если серия методов обернута транзакцией пользователем, то не стартовать транзакцию внутри метода. Ну и еще когнитивный диссонанс)) остался - почему малоквалифицированный программист типа меня, а таких битрикс наплодил очень много, узнает, что update и т.п. надо заключать в транзакцию, только когда добирается до кастомизации админки? Перед этим написав 100500 раз update, add, delete безо всяких транзакций.Да и то, узнает таким странным образом.
Вот тут надо быть внимательным. В битриксе нет поддержки вложенных транзакций, и если в вашем update будет обработчик события, в котором вы тоже всё обернёте в транзакцию, то предыдущая транзакция автоматически применится, а вам это добавит много часов увлекательного дебага Обычно применяют транзакции не для "тупо обернуть update", а для более каких то осмысленных действий в несколько операций (что если нужно сделать несколько update и add, а если не получиться, то вернуть всё взад?)
Есть еще куча приколов с Persistent Connections и транзакциями, где вы можете получить неработающую базу)
написал: Ок, механизм транзакций нужен, но почему он тогда не реализован внутри методов update, add и т.п. всех сущностей. Т.к. $DB - глобальный, то отлавливать вложенные транзакции (если серию таких методов пользователь еще и сам оборачивает транзакцией) вроде не проблема - если серия методов обернута транзакцией пользователем, то не стартовать транзакцию внутри метода.
1. Открытие/закрытие транзакции - дорогая (затратная) операция. Внедрение ее в методы изменения сущностей неприемлемо ухудшит проихводительность. 2. Поддерживаемые в продукте версии MYSQL вложенные транзакции не поддерживают.
Не надо сверлить зубы через задний проход дрелью от Сваровски
Первый пункт у вас возражений не вызывает, я так понимаю?
Любая блокировка, это очень затратно. Я не хочу этого видеть в ядре и это там не нужно. Но вот как рабочий инструмент, для внедрения в свой код - приветствуется
Код
class DB
{
protected static $transactions = 0;
public static function transaction(Closure $callback)
{
self::beginTransaction();
try {
$result = $callback();
} catch (Throwable $e) {
self::rollbackTransaction();
throw $e;
}
try {
if (self::$transactions == 1) {
Application::getConnection()->commitTransaction();
}
self::$transactions = max(0, self::$transactions - 1);
} catch (Throwable $e) {
self::$transactions = 0;
}
return $result;
}
public static function beginTransaction()
{
if (self::$transactions == 0) {
Application::getConnection()->startTransaction();
} elseif (self::$transactions >= 1) {
self::createSavepoint();
}
self::$transactions++;
}
public static function rollbackTransaction()
{
$toLevel = min(0, self::$transactions - 1);
if ($toLevel == 0) {
Application::getConnection()->rollbackTransaction();
} else {
self::removeSavepoint($toLevel + 1);
}
self::$transactions = $toLevel;
}
protected static function createSavepoint()
{
Application::getConnection()->query('SAVEPOINT trans' . (self::$transactions + 1));
}
protected static function removeSavepoint(int $level)
{
Application::getConnection()->query('ROLLBACK TO SAVEPOINT trans' . $level);
}
}