Nejste přihlášen(a)
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
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
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
}
}
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í?
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.
… 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ě.
Chybami se člověk učí – ale někteří lidé jsou nepoučitelní.
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)
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)
Chybami se člověk učí – ale někteří lidé jsou nepoučitelní.
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.
Neptej se, jestli se můžeš ptát | Blog | Twitter | GitHub | CMS Kdyby
Nette Jabber Room – nette@conf.netlab.cz , všichni jste vítáni
… 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é.
Chybami se člověk učí – ale někteří lidé jsou nepoučitelní.
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?
Neptej se, jestli se můžeš ptát | Blog | Twitter | GitHub | CMS Kdyby
Nette Jabber Room – nette@conf.netlab.cz , všichni jste vítáni
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.
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.
Chybami se člověk učí – ale někteří lidé jsou nepoučitelní.
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.
… 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)
Chybami se člověk učí – ale někteří lidé jsou nepoučitelní.