sql >> Databáze >  >> RDS >> Mysql

Je provádění správy transakcí u správce špatným postupem?

Důvod, proč říkám, že transakce nepatří do modelové vrstvy, je v podstatě tento:

Modely mohou volat metody v jiných modelech.

Pokud se model pokusí zahájit transakci, ale neví, zda jeho volající již transakci zahájil, musí model podmíněně zahájit transakci, jak je znázorněno v příkladu kódu v odpověď @Bubba . Metody modelu musí přijmout příznak, aby mu volající mohl říci, zda má povoleno zahájit vlastní transakci, nebo ne. Nebo jinak musí mít model možnost dotazovat se na stav „v transakci“ svého volajícího.

public function setPrivacy($privacy, $caller){
    if (! $caller->isInTransaction() ) $this->beginTransaction();

    $this->privacy = $privacy;
    // ...action code..

    if (! $caller->isInTransaction() ) $this->commit();
}

Co když volající není objekt? V PHP to může být statická metoda nebo jednoduše neobjektově orientovaný kód. To je velmi chaotické a vede to k mnoha opakovaným kódům v modelech.

Je to také příklad Control Coupling , což je považováno za špatné, protože volající musí vědět něco o vnitřním fungování volaného objektu. Například některé z metod vašeho modelu může mít parametr $transakční, ale jiné metody tento parametr mít nemusí. Jak má volající vědět, kdy na parametru záleží?

// I need to override method's attempt to commit
$video->setPrivacy($privacy, false);  

// But I have no idea if this method might attempt to commit
$video->setFormat($format); 

Dalším řešením, které jsem viděl navržené (nebo dokonce implementované v některých rámcích, jako je Propel), je vytvořit beginTransaction() a commit() no-ops, když DBAL ví, že je již v transakci. To však může vést k anomáliím, pokud se váš model pokusí zavázat a zjistí, že se skutečně nezavazuje. Nebo se pokusí vrátit zpět a má tento požadavek ignorovat. O těchto anomáliích jsem již psal.

Kompromis, který jsem navrhl, je, že Modely nevědí o transakcích . Model neví, zda jeho požadavek na setPrivacy() je něco, co by měla provést okamžitě, nebo je to součást většího obrazu, složitější série změn, které zahrnují více modelů a měly by pouze být zavázán, pokud všechny tyto změny uspějí. To je smysl transakcí.

Pokud tedy Modely nevědí, zda mohou nebo by měly zahájit a provést svou vlastní transakci, pak kdo ví? GRASP obsahuje vzor ovladače což je třída mimo uživatelské rozhraní pro případ použití a je jí přidělena odpovědnost za vytvoření a řízení všech částí, aby se tento případ použití uskutečnil. Správci vědí o transakcích protože zde jsou dostupné všechny informace o tom, zda je celý případ použití složitý a vyžaduje provedení více změn v modelech v rámci jedné transakce (nebo možná v rámci několika transakcí).

Příklad, o kterém jsem psal dříve, je zahájení transakce v beforeAction() metodu MVC Controlleru a potvrďte ji v afterAction() je zjednodušení . Řadič by měl mít možnost spustit a potvrdit tolik transakcí, kolik logicky vyžaduje k dokončení aktuální akce. Nebo se někdy může správce zdržet explicitní kontroly transakcí a umožnit Modelům, aby každou změnu provedly automaticky.

Jde ale o to, že informace o tom, jaké transakce jsou nezbytné, je něco, co Modely neznají – musí jim to být sděleno (ve formě $transakčního parametru) nebo se na ně zeptat jejich volajícího, což by stejně musel delegovat otázku až na akci Kontrolora.

Můžete také vytvořit vrstvu služby tříd, z nichž každá ví, jak provádět takové složité případy použití, a zda zahrnout všechny změny do jediné transakce. Tímto způsobem se vyhnete mnoha opakovaným kódům. Ale není běžné, aby aplikace PHP obsahovaly odlišnou vrstvu služeb; akce správce se obvykle shoduje s vrstvou služeb.



  1. Odstraňování problémů AlwaysOn – Někdy to vyžaduje mnoho pohledů

  2. Bezpečnost vlákna MySQL Select Last_Insert_ID

  3. PostgreSQL:přetypovaný řetězec k datu DD/MM/RRRR

  4. Příklady MySQL REGEXP