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

Pomocí událostí preRemove/postRemove získáte, které dotazy lze provést a které ne

Tady je návod, jak bych to udělal. Neříkám, že je to nejlepší přístup, pokud někdo zná něco jednoduššího nebo lepšího, měl bych zájem se to naučit jako první.

Za prvé, toto jsou Události s doktrínou které můžete použít. Pro jednoduchost vysvětlím, jak bych to udělal pro mazání. Také pro jednoduchost použiji statické pole (mohlo by to být provedeno jinými způsoby, líbí se mi tento) a zpětná volání životního cyklu . V tomto případě budou zpětná volání velmi jednoduché metody (proto je v pořádku je použít místo implementace posluchač nebo předplatitel ).

Řekněme, že máme tuto entitu:

Acme\MyBundle\Entity\Car:
    type: entity
    table: cars
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    fields:
        name:
            type: string
            length: '25'
            unique: true
        color:
            type: string
            length: '64'
    lifecycleCallbacks:
        preRemove: [entityDueToDeletion]
        postRemove: [entityDeleted]

Jak můžete vidět, definoval jsem dvě zpětná volání, která budou spuštěna s událostí preRemove a postRemove.

Poté php kód entity:

class Car {

    // Getters & setters and so on, not going to copy them here for simplicity

    private static $preDeletedEntities;// static array that will contain entities due to deletion.
    private static $deletedEntities;// static array that will contain entities that were deleted (well, at least the SQL was thrown).

    public function entityDueToDeletion() {// This callback will be called on the preRemove event
        self::$preDeletedEntities[] = $this->getId();// This entity is due to be deleted though not deleted yet.
    }

    public function entityDeleted() {// This callback will be called in the postRemove event
        self::$deletedEntities[] = $this->getId();// The SQL to delete the entity has been issued. Could fail and trigger the rollback in which case the id doesn't get stored in the array.
    }

    public static function getDeletedEntities() {
        return array_slice(self::$preDeletedEntities, 0, count(self::$deletedEntities));
    }

    public static function getNotDeletedEntities() {
        return array_slice(self::$preDeletedEntities, count(self::$deletedEntities)+1, count(self::$preDeletedEntities));
    }

    public static function getFailedToDeleteEntity() {
        if(count(self::$preDeletedEntities) == count(self::$deletedEntities)) {
            return NULL; // Everything went ok
        }
        return self::$preDeletedEntities[count(self::$deletedEntities)]; // We return the id of the entity that failed.
    }

    public static function prepareArrays() {
        self::$preDeletedEntities = array();
        self::$deletedEntities = array();
    }
}

Všimněte si zpětných volání a statických polí a metod. Pokaždé, když je zavoláno odstranění přes Car entita, preRemove callback uloží id entity do pole $preDeletedEntities . Když je entita odstraněna, postRemove událost uloží ID do $entityDeleted . preRemove událost je důležitá, protože chceme vědět, která entita způsobila selhání transakce.

A nyní v ovladači můžeme udělat toto:

use Acme\MyBundle\Entity\Car;

$qb = $em->createQueryBuilder();
$ret = $qb
        ->select("c")
        ->from('AcmeMyBundle:Car', 'c')
        ->add('where', $qb->expr()->in('c.id', ':ids'))
        ->setParameter('ids', $arrayOfIds)
        ->getQuery()
        ->getResult();

Car::prepareArrays();// Initialize arrays (useful to reset them also)
foreach ($ret as $car) {// Second approach
    $em->remove($car);
}

try {
    $em->flush();
} catch (\Exception $e) {
    $couldBeDeleted = Car::getDeletedEntities();
    $entityThatFailed = Car::getFailedToDeleteEntity();
    $notDeletedCars = Car::getNotDeletedEntities();

    // Do what you please, you can delete those entities that didn't fail though you'll have to reset the entitymanager (it'll be closed by now due to the exception).

    return $this->render('AcmeMyBundle:Car:errors.html.twig', array(// I'm going to respond with the ids that could've succeded, the id that failed and those entities that we don't know whether they could've succeeded or not.
                'deletedCars' => $couldBeDeleted,
                'failToDeleteCar' => $entityThatFailed,
                'notDeletedCars' => $notDeletedCars,
    ));
}

Doufám, že to pomůže. Implementace je o něco obtížnější než první přístup, ale mnohem lepší z hlediska výkonu.

AKTUALIZACE

Pokusím se trochu více vysvětlit, co se děje uvnitř catch blokovat:

V tomto okamžiku se transakce nezdařila. Byla vznesena výjimka kvůli skutečnosti, že smazání některé entity není možné (například kvůli fk omezení).

Transakce byla odvolána a žádné entity nebyly ve skutečnosti odstraněny z databáze.

$deletedCars je proměnná, která obsahuje id těch entit, které mohly být odstraněny (nevyvolaly žádnou výjimku), ale nejsou (kvůli vrácení zpět).

$failToDeleteCar obsahuje id entity, jejíž odstranění vyvolalo výjimku.

$notDeletedCars obsahuje zbývající ID entit, které byly v transakci, ale nevíme, zda by byla úspěšná nebo ne.

V tomto okamžiku můžete resetovat správce entit (je zavřený), spustit další dotaz s ID, která nezpůsobila problém, a smazat je (pokud chcete) a odeslat zpět zprávu, která uživateli sdělí, že jste tyto entity smazali a že $failToDeleteCar selhal a nebyl smazán a $notDeletedCars také nebyly smazány. Je na vás, abyste se rozhodli, co uděláte.

Nemohu reprodukovat problém, který zmiňujete o Entity::getDeletedEntities() , tady to funguje dobře.

Svůj kód byste mohli upřesnit tak, že jste tyto metody nemuseli přidávat do svých entit (ani zpětná volání životního cyklu). Můžete například využít předplatitele k zachycení událostí a speciální třídu se statickými metodami pro sledování těch entit, které se nezdařily, těch, které selhaly, a těch, které neměly možnost být smazány/ aktualizováno/vloženo. Odkazuji vás na dokumentaci, kterou jsem poskytl. Je to trochu složitější, než to zní, není to schopno poskytnout obecnou odpověď v několika řádcích kódu, omlouvám se, budete to muset dále prozkoumat.

Můj návrh je, abyste vyzkoušeli kód, který jsem poskytl, s falešnou entitou a provedli nějaké testy, abyste plně pochopili, jak to funguje. Poté jej můžete zkusit použít na své entity.

Hodně štěstí!




  1. Získání počtu výsledků vrácených dotazem MySQL s JDBC tím nejefektivnějším způsobem

  2. Jak získat dostupné hodnoty pro pole SET?

  3. Dokončete proces kopírování tabulky z jedné databáze do druhé (Export-Import) na SQL Server

  4. Maximální délka SQL dotazu