From a8c1c4be7bc637e34824d923461d3de02c3cb6e5 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 20 Dec 2024 23:55:52 +0100 Subject: [PATCH] nette/database 4.0 wip --- database/cs/transactions.texy | 98 ++++++++++++++++++++++++++------- database/en/transactions.texy | 100 +++++++++++++++++++++++++++------- 2 files changed, 159 insertions(+), 39 deletions(-) diff --git a/database/cs/transactions.texy b/database/cs/transactions.texy index e91070c2fb..a6ff27c3d2 100644 --- a/database/cs/transactions.texy +++ b/database/cs/transactions.texy @@ -4,40 +4,100 @@ Transakce .[perex] Transakce zaručují, že se buď provedou všechny operace v rámci transakce, nebo se neprovede žádná. Jsou užitečné pro zajištění konzistence dat při složitějších operacích. + +Základní použití +================ + Nejjednodušší způsob použití transakcí vypadá takto: ```php -$database->beginTransaction(); +$db->beginTransaction(); try { - $database->query('DELETE FROM articles WHERE id = ?', $id); - $database->query('INSERT INTO audit_log', [ - 'article_id' => $id, - 'action' => 'delete' - ]); - $database->commit(); + $db->query('DELETE FROM articles WHERE id = ?', $id); + $db->query('INSERT INTO audit_log', [ + 'article_id' => $id, + 'action' => 'delete' + ]); + $db->commit(); } catch (\Exception $e) { - $database->rollBack(); - throw $e; + $db->rollBack(); + throw $e; } ``` -Mnohem elegantněji můžete to samé zapsat pomocí metody `transaction()`. Jako parametr přijímá callback, který vykoná v transakci. Pokud callback proběhne bez výjimky, transakce se automaticky potvrdí. Pokud dojde k výjimce, transakce se zruší (rollback) a výjimka se šíří dál. +Mnohem elegantněji můžete to samé zapsat pomocí metody `transaction()`: ```php -$database->transaction(function ($database) use ($id) { - $database->query('DELETE FROM articles WHERE id = ?', $id); - $database->query('INSERT INTO audit_log', [ - 'article_id' => $id, - 'action' => 'delete' - ]); +$db->transaction(function ($db) use ($id) { + $db->query('DELETE FROM articles WHERE id = ?', $id); + $db->query('INSERT INTO audit_log', [ + 'article_id' => $id, + 'action' => 'delete' + ]); }); ``` Metoda `transaction()` může také vracet hodnoty: ```php -$count = $database->transaction(function ($database) { - $result = $database->query('UPDATE users SET active = ?', true); - return $result->getRowCount(); // vrátí počet aktualizovaných řádků +$count = $db->transaction(function ($db) { + $result = $db->query('UPDATE users SET active = ?', true); + return $result->getRowCount(); // vrátí počet aktualizovaných řádků +}); +``` + + +Zanořené transakce .{data-version:4.0} +====================================== + +Nette Database podporuje zanořování transakcí pomocí SQL savepointů. To znamená, že můžete spustit transakci uvnitř jiné transakce. Zde je jednoduchý příklad: + +```php +$db->transaction(function ($db) { + // hlavní transakce + $db->query('INSERT INTO users', ['name' => 'John']); + + // vnořená transakce + $db->transaction(function ($db) { + $db->query('UPDATE users SET role = ?', 'admin'); + // pokud zde nastane chyba, vrátí se zpět jen vnořená transakce + // hlavní transakce pokračuje dál + }); + + // pokračování hlavní transakce + $db->query('INSERT INTO user_log', ['action' => 'user created']); }); ``` + +.[note] +Podkladový mechanismus využívá ve skutečnosti jen jednu transakci na úrovni databáze a vnořené transakce emuluje pomocí savepointů. Toto chování je stejné pro všechny databáze a je zcela transparentní. + + +Auto-commit režim .{data-version:4.0} +===================================== + +Auto-commit určuje, zda se každý dotaz automaticky provede v samostatné transakci. Ve výchozím nastavení je auto-commit zapnutý, což znamená, že každý dotaz tvoří samostatnou transakci. + +Auto-commit můžete vypnout v konfiguraci: + +```neon +database: + dsn: 'mysql:host=127.0.0.1;dbname=test' + user: root + password: secret + options: + autoCommit: false # vypne auto-commit +``` + +nebo v kódu: + +```php +$db->setAutoCommit(false); +``` + +Při vypnutém auto-commitu se automaticky spustí nová transakce v těchto případech: +- při připojení k databázi +- po dokončení předchozí transakce (commit nebo rollback) + +.[note] +Pokud změníte nastavení auto-commitu během aktivní transakce, transakce se automaticky potvrdí. diff --git a/database/en/transactions.texy b/database/en/transactions.texy index 52d0e3cbdc..5a17219f2d 100644 --- a/database/en/transactions.texy +++ b/database/en/transactions.texy @@ -2,42 +2,102 @@ Transactions ************ .[perex] -Transactions guarantee that either all operations within the transaction are executed successfully, or none are executed at all. They are essential for maintaining data consistency during more complex operations. +Transactions guarantee that all operations within a transaction are either executed successfully or none of them are executed. They are useful for maintaining data consistency in more complex operations. + + +Basic Usage +=========== The simplest way to use transactions looks like this: ```php -$database->beginTransaction(); +$db->beginTransaction(); try { - $database->query('DELETE FROM articles WHERE id = ?', $id); - $database->query('INSERT INTO audit_log', [ - 'article_id' => $id, - 'action' => 'delete' - ]); - $database->commit(); + $db->query('DELETE FROM articles WHERE id = ?', $id); + $db->query('INSERT INTO audit_log', [ + 'article_id' => $id, + 'action' => 'delete' + ]); + $db->commit(); } catch (\Exception $e) { - $database->rollBack(); - throw $e; + $db->rollBack(); + throw $e; } ``` -A much cleaner and more elegant way to achieve the same result is by using the `transaction()` method. This method accepts a callback as a parameter, which it executes within the transaction. If the callback runs without throwing an exception, the transaction is automatically committed. If an exception is thrown, the transaction is rolled back, and the exception is propagated further. +You can achieve the same result more elegantly with the `transaction()` method: ```php -$database->transaction(function ($database) use ($id) { - $database->query('DELETE FROM articles WHERE id = ?', $id); - $database->query('INSERT INTO audit_log', [ - 'article_id' => $id, - 'action' => 'delete' - ]); +$db->transaction(function ($db) use ($id) { + $db->query('DELETE FROM articles WHERE id = ?', $id); + $db->query('INSERT INTO audit_log', [ + 'article_id' => $id, + 'action' => 'delete' + ]); }); ``` The `transaction()` method can also return values: ```php -$count = $database->transaction(function ($database) { - $result = $database->query('UPDATE users SET active = ?', true); - return $result->getRowCount(); // returns the number of rows updated +$count = $db->transaction(function ($db) { + $result = $db->query('UPDATE users SET active = ?', true); + return $result->getRowCount(); // returns the number of rows updated +}); +``` + + +Nested Transactions .{data-version:4.0} +======================================= + +Nette Database supports nested transactions using SQL savepoints. This means you can start a transaction inside another transaction. Here's a simple example: + +```php +$db->transaction(function ($db) { + // Main transaction + $db->query('INSERT INTO users', ['name' => 'John']); + + // Nested transaction + $db->transaction(function ($db) { + $db->query('UPDATE users SET role = ?', 'admin'); + // If an error occurs here, only the nested transaction will be rolled back, + // while the main transaction will continue. + }); + + // Continuing the main transaction + $db->query('INSERT INTO user_log', ['action' => 'user created']); }); ``` + +.[note] +Internally, only a single database transaction is used, and nested transactions are simulated using savepoints. This behavior is consistent across all databases and is completely transparent to the developer. + + +Auto-commit Mode .{data-version:4.0} +==================================== + +Auto-commit determines whether each query is automatically executed in a separate transaction. By default, auto-commit is enabled, meaning every query forms a separate transaction. + +You can disable auto-commit in the configuration: + +```neon +database: + dsn: 'mysql:host=127.0.0.1;dbname=test' + user: root + password: secret + options: + autoCommit: false # disables auto-commit +``` + +Or in the code: + +```php +$db->setAutoCommit(false); +``` + +When auto-commit is disabled, a new transaction automatically starts in the following cases: +- When connecting to the database. +- After the previous transaction is completed (commit or rollback). + +.[note] +If you change the auto-commit setting while a transaction is active, the current transaction will be automatically committed to maintain consistency.