Как организовать вложенные транзакции?

Вот типовая схема работы с транзакцией:


$transaction=$model->dbConnection->beginTransaction();

try

{

    $post->exec(1);

    $post->exec(2);

    $post->exec(3);

    $transaction->commit();

}

catch(Exception $e)

{

    $transaction->rollBack();

}

Но допустим, внутри метода exec() мне также нужно выполнить транзакцию:


function exec($n) {

    $transaction=$model->dbConnection->beginTransaction();

    try

    {

        $info=$model->findByPk($n);

        $info->func(1);

        $info->func(2);

        $info->func(3);

        $transaction->commit();

    }

    catch(Exception $e)

    {

        // somthing

    }

}

Так вот, когда я пытаюсь начать транзакцию внутри exec(), то получаю ошибку PDO: "There is already an active transaction". Не может быть, чтобы нельзя было использовать вложенные транзакции. Но как это сделать? Пока не могу найти ответа на этот вопрос.

У CDbConnection есть свойство currentTransaction…


if($model->dbConnection->currentTransaction !== null)

    echo 'Транзакция уже запущена';

Честно говоря, корень проблемы в непонимании принципа транзакций.

Какой смысл организовывать оную, если это может быть отменено транзакцией более высокого уровня ?!

А так… Если просто собирается конструктор из готовых вещей, то прав napeHeK.

Объясняю, в чем смысл, и почему решение, которое предложил napeHeK, не подходит.

Например, есть ресурс, где размещаются статьи и комментарии к ним. В разных случаях могут удаляться либо только комментарии, либо статьи со всеми комментариями, к ним добавленными.

Я пишу отдельно функцию удаления коммента (скажем, delComment) и отдельно - функцию удаления статьи (delArticle), из которой будет вызываться delComment для всех комментариев статьи. Процедура удаления даже одного коммента может рассматриваться, как единая транзакция, т.к. внутри нее может быть множество операций с БД (напр., обновление каких-нибудь счетчиков, связей, состояний, уведомлений и проч.). Удаление статьи - это тоже единая транзакция, т.к. с точки зрения целостности - если уж статью удаляем, то обязательно все комменты должны быть удалены. Причем, при нормально работающем механизме вложенных транзакций не имеет значения, чего мы сделаем раньше - саму статью удалим, а потом комменты к ней или наоборот. Т.е. схематично это может выглядеть, скажем, так:


function delArticle($article) {

  // начало транзакции 1

  foreach ($article->comments as $comment) {

    delComment($comment);

  }

  // прочие действия по удалению статьи

  $article->delete;

  // конец транзакции 1

}


function delComment($comment) {

  // начало транзакции 2

  // прочие действия по удалению комментария

  $comment->delete;

  // конец транзакции 2

}

Что произойдет, если beginTransaction() для "вложенных" (якобы) транзакций будет игнорироваться, а commit() и rollBack() - выполняться, сами представьте. Поэтому предложенный вариант не просто бесполезен - он очень вреден, т.к. будет ломать вложенные транзакции напрочь.

А вообще понятие “вложенных транзакций” - это вполне обычное дело в проектировании процедур работы с БД. И MySQL, насколько я знаю, их поддерживает. Но PDO про них, похоже не знает, и Yii-класс по работе с базой, видимо, тоже. Что совсем не айс для фреймворка :(

Вот решение проблемы: http://www.yiiframework.com/wiki/38/how-to-use-nested-db-transactions-mysql-5-pgsql