2010-08-10 4 views
13

У меня вопрос о тестировании запросов в транзакции. Я использую транзакцию MySQL в течение некоторого времени, и каждый раз, когда я делаю это, я использую нечто вроде:Как проверить транзакции MySQL?

$doCommit = true; 
$error = ""; 
mysql_query("BEGIN"); 

/* repeat this part with the different queries in the transaction 
    this often involves updating of and inserting in multiple tables */ 
$query = "SELECT, UPDATE, INSERT, etc"; 
$result = mysql_query($query); 
if(!$result){ 
    $error .= mysql_error() . " in " . $query . "<BR>"; 
    $doCommit = false; 
} 
/* end of repeating part */ 

if($doCommit){ 
    mysql_query("COMMIT"); 
} else { 
    echo $error; 
    mysql_query("ROLLBACK"); 
} 

Теперь, часто бывает, что я хочу, чтобы проверить свою сделку, так что я изменить mysql_query("COMMIT"); к mysql_query("ROLLBACK"); , но я могу представить, что это не очень хороший способ проверить этот материал. Обычно не реально копировать каждую таблицу в temp_table и обновлять ее и вставлять в эти таблицы и удалять их впоследствии (например, потому что таблицы могут быть очень большими). Конечно, когда код переходит в производство, исправляется обработка ошибок (вместо того, чтобы просто печатать ошибку).

Каков наилучший способ сделать это?

+0

О, боже мой ... Просто узнал, что PHP не поддерживает ничего, как 'finally' или' обеспечить'. –

+2

Да, я знаю ... PHP отстой, и я использую его ... –

+0

В настоящее время поддерживается 'finally', начиная с PHP 5.5 – Alex

ответ

10

Прежде всего, в вашей реализации есть ошибка. В случае ошибки запроса текущая транзакция автоматически откатывается и затем закрывается. Так как вы продолжаете выполнять запросы, они не будут находиться в транзакции (они будут переданы в БД). Затем, когда вы выполняете Rollback, он будет терпеть неудачу. Из MySQL docs:

Rolling back can be a slow operation that may occur implicitly without the user 
having explicitly asked for it (for example, when an error occurs). 

Явная команда ROLLBACK следует использовать только, если вы определить в приложении, которое вам нужно откатить (для других, чем ошибка запроса причин). Например, если вы вычитаете средства из учетной записи, вы явно откажетесь, если узнаете, что у пользователя недостаточно средств для завершения обмена ...

Что касается тестирования транзакций, я делаю скопируйте базу данных. Я создаю новую базу данных и устанавливаю набор «фиктивных данных». Затем я запускаю все тесты с помощью автоматизированного инструмента. Инструмент будет фактически совершать транзакции и отменять откаты и проверять, что ожидаемое состояние базы данных поддерживается на протяжении всех тестов. Поскольку сложнее узнать конечное состояние транзакции, если у вас есть неизвестный ввод транзакции, тестирование живых (или даже скопированных с живого) данных будет непростым. Вы можете сделать это (и должны), но не зависеть от этих результатов для определения того, работает ли ваша система. Используйте эти результаты для создания новых тестовых примеров для автоматизированного тестера ...

+0

Wow, я не сделал домашнее задание прямо на этом, тогда ... Я действительно не знал, что MySQL откатывается, когда возникает ошибка. Спасибо за понимание. Спасибо за остальное, это действительно полезный пост. –

+0

Конечно. Я вижу эту ошибку довольно часто (поскольку она противоречит интуиции, если вы этого не ожидаете) ... Также довольно сложно отладить, когда вы просматриваете журнал выполнения, пытаясь понять, почему несколько запросов хотя команда rollback была выполнена ... Вот почему я всегда делаю исключения из ошибок запросов (особенно если код производства никогда не должен быть ошибкой, если что-то не пошло не так (например, сервер ушел). Поэтому зачем пытаться продолжить, если вы не знаете, что пошло не так?) ... Это намного сложнее игнорировать так же ... – ircmaxell

+0

Вы правы. В своем посте вы говорите об автоматическом тестере, вы сами это сделали? Я могу представить себе, что для разных проектов требуются разные тестеры (и, возможно, даже для разных тестовых случаев). –

2

Обычно я использую что-то вроде (я использую PDO для моего примера):

$db->beginTransaction(); 
try { 
    $db->exec('INSERT/DELETE/UPDATE'); 
    $db->commit(); 
} 
catch (PDOException $e) { 
    $db->rollBack(); 
    // rethrow the error or 
} 

Или если у вас есть свой собственный обработчик исключений, используйте специальный пункт для PDOExceptions, где откатить выполнение. Пример:

function my_exception_handler($exception) { 
    if($exception instanceof PDOException) { 
    // assuming you have a registry class 
    Registry::get('database')->rollBack(); 
    } 
} 
+0

Да, но как вы проверяете транзакцию? Дело в том, что я хочу знать, как вы можете протестировать все запросы в одной транзакции. В вашем случае я заменю '$ db-> commit();' с '$ db-> rollBack();', но это будет то же самое, что и в моем исходном вопросе. –

3

Возможно, вы могли бы реорганизовать свой первый пример и использовать некоторый класс оболочки доступа к БД?

В этом классе-оболочке у вас может быть переменная $ normalCommit = true; и метод SetCommitMode(), который устанавливает переменную $ normalCommit. И у вас есть метод Commit(), который фиксирует if ($ normalCommit == true) Или даже имеет переменную $ failTransaction, которая вызывает mysql_query ("ROLLBACK"); если вы хотите (чтобы вы могли пройти или не пройти много последовательных тестов).

Затем, когда вы запускаете тест, вы можете установить где-нибудь в файле тестового кода: $ myDBClass-> SetCommitMode (false); или $ myDBClass-> RollBackNextOperation (true); перед операцией, которую вы хотите провалить, и она просто потерпит неудачу.Таким образом, код, который вы тестируете, не будет содержать эти проверки fail/commit, только класс DB будет содержать их.

И, как правило, ТОЛЬКО тестовый код (особенно если вы проводите модульное тестирование) должен вызывать методы SetCommitMode и RollBackNextOperation, поэтому вы случайно не оставляете эти вызовы в производственном коде.

Или вы можете передать некоторые сумасшедшие данные вашему методу (если вы тестируете метод), например, отрицательные переменные для сохранения в полях UNSIGNED, а затем ваша транзакция должна потерпеть неудачу на 100%, если ваш код не выполняет фиксацию после такого Ошибка SQL (но это не должно).

+0

Спасибо за ваш ответ, это разумное решение. Если бы я мог разделить щедрость, вы получили бы 50% ... –

Смежные вопросы