Skip to content

Commit

Permalink
nette/database 4.0 wip
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Dec 26, 2024
1 parent aabfd58 commit a8c1c4b
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 39 deletions.
98 changes: 79 additions & 19 deletions database/cs/transactions.texy
Original file line number Diff line number Diff line change
Expand Up @@ -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í.
100 changes: 80 additions & 20 deletions database/en/transactions.texy
Original file line number Diff line number Diff line change
Expand Up @@ -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.

0 comments on commit a8c1c4b

Please sign in to comment.