Dibi a zanořené transakce
- jannek19
- Člen | 47
Dobrý den,
jak řešíte zanořené transakce v Dibi? Typický problém, na který narážím je, že ve fasádě něco persistuji do více tabulek, otevřu si tedy transakci, volám jednotlivé repositáře pro uložení dat, na konci metody transakci commitnu (nebo zavolám rollback při selhání).
Potíž nastane, když si nějaký repositář také z nějakého dobrého důvodu otevře transakci, tu pak commitne, ale tím mi ukončí transkaci otevřenou ve fasádě.
Obvykle používám MySQL a mysqli
driver v Dibi.
Jak podobné situace řešíte? Co byste v podobných situacích doporučili? Používáte nějakou knihovnu?
Díky moc.
- Tharos
- Člen | 1030
Nevím teď od stolu, zda to nemá dibi nějak vyřešené, ale
univerzálním řešením je vytvoření si služby (takové proxy) pro
řízení transakcí. Řekněme nějaký Transactor
… Skelet
může být úplně jednoduchý:
class Transactor
{
/**
* @var Connection
*/
private $connection;
/**
* @param Connection $connection
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function begin()
{
$this->connection->beginTransaction();
}
public function commit()
{
$this->connection->commit();
}
public function rollback()
{
$this->connection->rollBack();
}
/**
* @param Closure $func
*/
public function transactional(Closure $func)
{
// ...
}
}
No a do něj si pak jen doplníš podporu pro zanoření, stačí tam jen
hlídat nějaký int private $level
…
Editoval Tharos (16. 8. 2017 12:19)
- Tharos
- Člen | 1030
Dodám, že tenhle přístup sám využívám, protože umožňuje řídit
transakci i z fasád na vyšší úrovni, které jinak o nějakém
Connection
nemají ani tucha. :)
Používám přímo kód výše, ale ve spojení s Doctrine DBAL, které si
samo řeší zanořování transakcí na úrovni Connection
…
Proto ten $level
v mém kódu není.
- jannek19
- Člen | 47
Díky moc za odpověď. Obalit connection do nějaké pomocné třídy,
která bude počítat zanoření, mě taky napadlo, spíš jsem ale uvažoval
směrem, který je naznačený v tomto článku, kdy je každá transakce reprezentována
samostatným objektem. Problém popsaný v článku by ale měla řešit metoda
transactional
v tvé ukázce.
Jen ještě přemýšlím nad tím, jestli obyčejné počítání levelu
bude stačit a hlavně jak se to má zachovat při rollback
u. Begin
a commit je jasný (level++
, resp. level--
), jak se
ale zachovat při rollbacku
– udělat jen level--
,
nebo provést reset level=0
? Asi se budu muset podívat, jak to
řeší např. Doctrine, případně nedávno tu někdo (@Felix?) dával
knihovnu pro Nette Database.
- Tharos
- Člen | 1030
U rollbacku se dělá
$level--
. Koncepčně můžeš implementaci obšlehnout
z Doctrine, je v ní hezky
naimplementovaná i ta transactional
metoda.
- Felix
- Nette Core | 1183
@jannek19
Ahoj, jak popisuje @Tharos, ten jeho priklad je dost presny. Ja se pokusil implementovat nested transaction s vyuzitim neuzavrenych transakci podle clanku od @OndřejMirtes.
Mrkni: https://github.com/…cs/README.md#…
Je to uplne easy. Btw, podobnou techniku s
$inst->transactional()
pouzivam take (https://github.com/…/Transaction).
- jannek19
- Člen | 47
@Felix tvoje implementace je díky save pointům docela přímočará; implementace v Doctrine DBAL je kvůli tomu, že umožňuje i použití bez save pointů trochu složitější, ale oboje vypadá docela snadně.
Jen škoda, že z toho ještě nikdo neudělal rozšíření pro Dibi :)
Ještě jednou díky vám oběma.
- Felix
- Nette Core | 1183
jannek19 napsal(a):
@Felix tvoje implementace je díky save pointům docela přímočará; implementace v Doctrine DBAL je kvůli tomu, že umožňuje i použití bez save pointů trochu složitější, ale oboje vypadá docela snadně.
Jen škoda, že z toho ještě nikdo neudělal rozšíření pro Dibi :)
Ještě jednou díky vám oběma.
Pro dibi to muzeme jednoduse backportovat z NetteDatabase.