Организация нескольких последовательных запросов в транзакции

Midgard90
Дата: 11.03.2015 08:47:09
Всем добрых суток!
Стоит следующая задача: По событию провести несколько последовательных запросов Insert или Update, но в случае чего - с откатом изменений. Что-то типа транзакции. Пытался сделать таким образом:
try
   {adoquery->clear();
    adoquery->add("INSERT....");
    adoquery->ExecSQL();
    adoquery->clear();
    adoquery->add("UPDATE....");
    adoquery->ExecSQL();
    }
catch(...)
    {
    //не вышло
    }

В этом случае, если первый запрос выполнился, а второй заклинило, то выскакивает исключение, но первый уже выполнился, что не допустимо для задачи..
Прочитал что надо использовать связку: BEGIN;COMIT;ROLLBACK; Но нигде не нашёл как использовать, мысли такие:
вариант 1:
try
   {adoquery->clear();
    adoquery->add("BEGIN;");
    adoquery->add("INSERT....");
    adoquery->add("UPDATE....");
    adoquery->add("COMMIT;");
    adoquery->ExecSQL();
    }
catch(...)
    {
    //не вышло
    adoquery->add("ROLLBACK;");
    adoquery->ExecSQL();
    }

Вариант 2:
try
   {adoquery->clear();
    adoquery->add("BEGIN;");
    adoquery->ExecSQL();
    adoquery->add("INSERT....");
    adoquery->ExecSQL();
    adoquery->clear();
    adoquery->add("UPDATE....");
    adoquery->ExecSQL();
    adoquery->add("COMMIT;");
    adoquery->ExecSQL();
    }
catch(...)
    {
    adoquery->add("ROLLBACK;");
    adoquery->ExecSQL();
    }

Укажите верный путь, как надо использовать подобные вещи, если можно пример. СУБД postgres, среда Borland 2009
Dimitry Sibiryakov
Дата: 11.03.2015 11:53:38

RTFM TADOConnection.BeginTrans, CommitTrans, RollbackTrans.

Posted via ActualForum NNTP Server 1.5

MasterZiv
Дата: 11.03.2015 12:08:16
Midgard90
Прочитал что надо использовать связку: BEGIN;COMIT;ROLLBACK; Но нигде не нашёл как использовать, мысли такие:

Вариант 2:
try
   {adoquery->clear();
    adoquery->add("BEGIN;");
    adoquery->ExecSQL();
    adoquery->add("INSERT....");
    adoquery->ExecSQL();
    adoquery->clear();
    adoquery->add("UPDATE....");
    adoquery->ExecSQL();
    adoquery->add("COMMIT;");
    adoquery->ExecSQL();
    }
catch(...)
    {
    adoquery->add("ROLLBACK;");
    adoquery->ExecSQL();
    }



Так вот так вот (как выше, Вариант 2) и надо их использовать.
Только там наверное не add надо вызывать, а set или что-то такое.
Или

    adoquery->clear();
    adoquery->add("BEGIN;");
    adoquery->ExecSQL();
    adoquery->clear();
    adoquery->add("INSERT....");
    adoquery->ExecSQL();


т.е. запрос, который уже выполнился, должен из adoquery удаляться.


Midgard90
вариант 1:
try
   {adoquery->clear();
    adoquery->add("BEGIN;");
    adoquery->add("INSERT....");
    adoquery->add("UPDATE....");
    adoquery->add("COMMIT;");
    adoquery->ExecSQL();
    }
catch(...)
    {
    //не вышло
    adoquery->add("ROLLBACK;");
    adoquery->ExecSQL();
    }




т.е. тут идея в том, чтобы сформировать пачку из нескольких запросов, и выполнить их скопом.
Это тоже возможно, и в принципе от варианта 2 это ничем не отличается в смысле транзакций,
за исключением двух моментов:

  • выполнять по одному оператору удобнее.
  • не все СУБД позволяют сформировать батч (пачку операторов SQL) для посылки в СУБД для выполнения, и во всех СУБД это делается по-разному. Как -- надо смотреть в документации по СУБД и может быть по используемому CLAPI.
  • MasterZiv
    Дата: 11.03.2015 12:10:40
    Dimitry Sibiryakov
    RTFM TADOConnection.BeginTrans, CommitTrans, RollbackTrans.


    Идея этого API в том, чтобы вместо операторов типа
    START TRANSACTION
    COMMIT
    ROLLBACK

    использовать вызовы этих функций. Это имеет смысл, поскольку операторы управления транзакциями бывают
    в разных СУБД немного разными, поэтому использование этих функций имеет смысл, особенно для кросс-СУБД-шных приложений.

    Но в принципе они делают то же самое, что и соотв. операторы языка SQL данной СУБД.
    Midgard90
    Дата: 11.03.2015 13:38:30
    Да, не думал что всё довольно просто. Как поступил:
    ADOQuery привязал к ADOConnection, через него изначально шли все значимые запросы.
    В коде сделал следующее:
    try 
        {
        Form5->ADOConnection1->BeginTrans();
        adoquery->clear();
        adoquery->add("INSERT....");
        try
           {
            adoquery->ExecSQL();
           }
        catch.....//Отловил, что ошибка в первом запросе
        adoquery->clear();
        adoquery->add("UPDATE....");
        try
           {
           adoquery->ExecSQL();
           }
        catch......//Отловил, что ошибка во втором запросе
        Form5->ADOConnection1->CommitTrans();
        }
    catch(...)
        {
        //не вышло, и указываем из за какого запроса
        Form5->ADOConnection1->RollbackTrans();
        }
    


    На удивление чётко сработало, без рукоблудства в коде. Проверил нарочной ошибкой в запросе. Спасибо за помощь! Весьма признателен!
    egorych
    Дата: 11.03.2015 13:48:33
    Midgard90
    На удивление чётко сработало, без рукоблудства в коде.
    хранимую процедуру на сервере что помешало написать?
    Dima T
    Дата: 11.03.2015 14:02:54
    Midgard90
    В коде сделал следующее:...

    Зачем тебе тут транзакция если RollbackTrans() никогда не сработает? Т.к. каждый запрос в свой try...catch обернут.
    MasterZiv
    Дата: 11.03.2015 14:05:49
    Midgard90
    Да, не думал что всё довольно просто. Как поступил:
    ADOQuery привязал к ADOConnection, через него изначально шли все значимые запросы.
    В коде сделал следующее:
    try 
        {
        Form5->ADOConnection1->BeginTrans();
        adoquery->clear();
        adoquery->add("INSERT....");
        try
           {
            adoquery->ExecSQL();
           }
        catch.....//Отловил, что ошибка в первом запросе
        adoquery->clear();
        adoquery->add("UPDATE....");
        try
           {
           adoquery->ExecSQL();
           }
        catch......//Отловил, что ошибка во втором запросе
        Form5->ADOConnection1->CommitTrans();
        }
    catch(...)
        {
        //не вышло, и указываем из за какого запроса
        Form5->ADOConnection1->RollbackTrans();
        }
    


    На удивление чётко сработало, без рукоблудства в коде. Проверил нарочной ошибкой в запросе. Спасибо за помощь! Весьма признателен!



    try/catch-а хватит и одного на весь блок.
    egorych
    Дата: 11.03.2015 14:11:37
    Dima T
    Зачем тебе тут транзакция если RollbackTrans() никогда не сработает? Т.к. каждый запрос в свой try...catch обернут.
    то есть, думаешь, throw из catch-блока очень сложно сделать, да? ))
    Хотя, конечно, лучше так:
    MasterZiv
    try/catch-а хватит и одного на весь блок
    Midgard90
    Дата: 11.03.2015 14:12:36
    Да, переборщил) общего try хватило.. Но с каждым обёрнутым тоже работает, только откатывает транзакцию сразу чуть в каком запросе не вышло. Матрёшка своего рода)