Oznámení
Jak presne na transakce v dibi
před 8 lety
- darthcz
- Člen | 114
Dobrý den,
nikde jsem nenašel popsanou dokumentaci transakcí v dibi. Chtěl bych se proto zeptat, jak přesně to s transakcemi v dibi je. Potřeboval bych následující věc:
Zakládám klienta – zjistím selectem, zda existuje a pokud ne potřeboval bych začít transakci:
insert do tabulky klientů (zjištění id), vložení do N:M tabulky a dále vložení různých údajů do dalších tabulek. Potřeboval bych ale při zjištění chyby v kterémkoli procesu ze zmíněných, aby byly všechny inserty vraceny.
Napadlo mě:
begin transakce
- insert
- insert
- insert
commit
Nevím ale, jak vrátit všechny předchozí inserty. Například nastane výjimka u insertu 3. Mám tedy v ošetření výjimky provést 2* rollback?
Nebo to funguje jako v doctrine? Tím myslím začnu transakci a všechny dotazy se ukládají do fronty, která se provede až najednou při zavolání commitu.
Za odpověď děkuji
před 8 lety
- Milo
- Nette Core | 1119
Asi nejjednodušší způsob je:
$db = dibi::connect(...);
$client = $db->query('SELECT xxx FROM yyy WHERE zzz')->fetch();
if (klient neexistuje)
{
$db->begin();
try
{
$db->query('INSERT INTO ....');
$db->query('INSERT INTO ....');
$db->query('INSERT INTO ....');
$db->commit();
}
catch (DibiException $e)
{
$db->rollback();
// zpracovani vyjimky
}
}
před 8 lety
- darthcz
- Člen | 114
Děkuji za odpověď,
zkoušel jsem a vypadá to, že změny se provedou skutečně až po zavolání funkce commit. Překvapilo mě, že je to schopné předpovědět id uživatele, který fyzicky vlastně ani nebyl vložen bez commitu. Taktéž nebylo potřeba volat rollback, což také nechápu :) Rollback vrátí zpět úplně všechny změny, nebo jen tu poslední?
před 8 lety
- Milo
- Nette Core | 1119
Transakce neprobíhá na úrovni dibi, ale na úrovni databáze. Dibi (resp. jakýkoliv klient) pouze žádá server o začátek transakce, něco provede a pak požádá buď o potvrzení (COMMIT) nebo o zrušení (ROLLBACK). Při rollbacku se změněné tabulky (INSERT, UPDATE, DELETE) vrací do původního stavu, ale např. sekvence už ne. V průběhu transakce můžeš se změněnými daty počítat tak, jako kdyby se zapsali normálně.
Během transakce umí některé DB vytvářet takzvané SAVEPOINTy a při ROLLBACK se vracet pouze k nim.
Tohle je hodně zhruba napsané, existují ještě úrovně izolace transakce, atd… to je ale nad rámec fóra a nic není lepší, než dokumentace, např. PostegreSQL.
před 8 lety
- Václav M.
- Člen | 34
… začal jsem uvažovat o tom, že některé operace upravím na transakce, ale nějak si nejsem jist, jak u tohoto kódu
$Dotaz_Delete = array("Uzivatele_Email", "Uzivatele_Heslo", "Uzivatele_Info", "Uzivatele_Jmeno", "Uzivatele_Prava");
foreach($Dotaz_Delete as $Tabulka)
{
$Vysledek = dibi::delete($Tabulka)
->where("Jmeno_ID = %s", $_POST['RusenyUzivatel'])
->execute();
}
bez vyjímek zajistit, že se všechna smazání provedla správně tak, aby mohl být použit commit (nebo v případě neúspěchu rollback)- a zároveň vypsán status vykonání úprav
if( ... )
{
dibi:commit();
return HlaseniSpravnehoVysledku("...");
}
else
{
dibi:rollback();
return HlaseniChybyAkce("...");
}
Já bych se totiž bez vyjímek velmi rád obešel (co nejdéle). Obzvlášť, když nepotřebuji (a nechci) vypisovat co se stalo špatně.
před 8 lety
- Milo
- Nette Core | 1119
Nevidím do Tvé aplikace, ale ta struktura tabulek mi přijde dost zvrácená. Pro 100% spolehlivost smazání všech řádků bych doporučil použití cizích klíčů.
Co si jinak představuješ pod pojmem „…se všechna smazání provedla správně…“? Vyjímka Ti vyskočí pouze pokud máš chybu v syntaxi SQL. Jinak musíš zjistit počet smazaných řádků a rozhodntou se, jestli to je OK.
$tabulky = array('tabulka1', 'tabulka2');
dibi::begin();
$deleted = 0;
foreach ($tabulky AS $tabulka) {
$deleted += dibi::query('DELETE FROM %n WHERE id = %i', $tabulka, $id);
}
if ($deleted === 5) {
dibi::commit();
echo "Smazano 5 zaznamu";
} else {
dibi::rollback();
echo "Chyba";
}
EDIT: Resp. vyjímka vyskočí nejen při syntaktické chybě, ale i při chybě při provádění dotazu. Nicméně úspěch smazaných řádků musíš zjistit ručně.
Editoval Milo (6. 11. 2011 21:32)
před 8 lety
- Václav M.
- Člen | 34
Na ty cizí klíče se podívám.
A k těm tabulkám: proč by měla být ta struktura zvrácená? Je možné, že by se některé z těch tabulek daly sloučit do jedné, takže by nakonec místo pěti tabulek by jen dvě, ale … to je asi tak všechno, co se s tím dá udělat.
Editoval Václav M. (6. 11. 2011 22:39)
před 8 lety
- HosipLan
- Moderator | 4693
To je asi tak účel. Vybírat data z jedné tabulky je rychlejší než joinovat hromadu tabulek. Obzvláště to pocítíš v momentě, kdy ti nakynou.
před 8 lety
- Václav M.
- Člen | 34
… divné …
Nejdříve jsem to zkusil takhle
dibi::begin();
$Vysledek = 0;
foreach($Dotaz_From as $Tabulka)
{
$Vysledek += dibi::delete($Tabulka)
->where($Dotaz_Where)
->execute();
}
if($Vysledek == count($Dotaz_From))
{
dibi::commit();
return HlaseniSpravnehoVysledku("Sekce byla zrušena");
}
else
{
dibi::rollback();
return HlaseniChybyAkce("Zrušení sekce se nezdařilo");
}
… ale zkusil jsem i
if($Vysledek === 4)
a
if($Vysledek === count($Dotaz_From))
a akce vždcky skončila rollbackem … ale když jsem zkusil použít vyjímku, akce skončila commitem. Což mi přijde naprosto šílené.
před 8 lety
- HosipLan
- Moderator | 4693
Tvoje logika je chybná. Počítáš s tím, že součet smazaných řádků bude stejný jako počet tabulek. To tě napadlo jak?
před 8 lety
- Milo
- Nette Core | 1119
A jak jsi zkoušel použít vyjímku? Pokud předpokládáš, že v každé tabulce bude vždy jeden řádek pro každého uživatele, potom může být vše v jediné tabulce a máš po starostech.
před 8 lety
- Václav M.
- Člen | 34
HosipLan napsal(a):
Tvoje logika je chybná. Počítáš s tím, že součet smazaných řádků bude stejný jako počet tabulek. To tě napadlo jak?
Milo to napsal přesně – v každé té zasažené tabulce pro jednoho uživatele jeden řádek (zatím).
Milo napsal(a):
A jak jsi zkoušel použít vyjímku? Pokud předpokládáš, že v každé tabulce bude vždy jeden řádek pro každého uživatele, potom může být vše v jediné tabulce a máš po starostech.
- try – catch
- Zatím by to šlo, mít vše v jedné tabulce
před 8 lety
- Milo
- Nette Core | 1119
Princip try – catch chápu, ale jak to použiješ s delete? Použij cizí klíče a tyhle věci vůbec nemusíš řešit.
před 8 lety
- Václav M.
- Člen | 34
… jestli tomu rozumím dobře, tak ty cizí klíče způsobí, že když se změní jedna tabulka (když tam zmizí určitý řádek), tak se automaticky tyto položky odstraní i z těch dalších tabulek, které jsou na ní navázány bez nutnosti zásahu člověka.
Takže potom v věci delete není nutné použít více než jeden příkaz – a odpadá nutnost použití transakce.
-->
Ovšem s těmi cizími klíči mám docela problém – server mi odmítá
tabulky s cizími klíči vytvořit (a vyhazuje errno 150) – a selhává
i pokus o jejich dodatečné nastavení. To samozřejmě není problém dibi,
ale serveru samotného (protože jsem ty příkazy zkusil zadat i v PMA –
s stejným výsledkem) a ani s pomocí lidí z fora zive.cz se to zatím
nepodařilo vyřešit.
Editoval Václav M. (8. 11. 2011 16:44)