sql >> Databasteknik >  >> RDS >> Mysql

Använda preRemove/postRemove-händelser för att se vilka frågor som kan köras och vilka som inte kan

Så här skulle jag göra. Jag säger inte att detta är det bästa tillvägagångssättet, om någon vet något enklare eller bättre, skulle jag vara den första som är intresserad av att lära mig det.

Först och främst är dessa Lärande händelser som du kan använda. För enkelhetens skull ska jag förklara hur jag skulle göra för raderingar. För enkelhetens skull kommer jag också att använda en statisk array (det kan göras på andra sätt, jag gillar den här) och livscykelåteruppringningar . I det här fallet kommer återuppringningarna att vara väldigt enkla metoder (det är därför det är ok att använda dem istället för att implementera en lyssnare eller prenumerant ).

Låt oss säga att vi har den här enheten:

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]

Som du kan se har jag definierat två återuppringningar som kommer att utlösas med händelsen preRemove och postRemove.

Sedan php-koden för enheten:

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();
    }
}

Notera återuppringningarna och de statiska arrayerna och metoderna. Varje gång en borttagning anropas över en Car enhet, preRemove callback kommer att lagra entitetens id i arrayen $preDeletedEntities . När enheten är raderad visas postRemove händelsen kommer att lagra id:t i $entityDeleted . preRemove händelsen är viktig eftersom vi vill veta vilken enhet som gjorde att transaktionen misslyckades.

Och nu, i kontrollenheten kan vi göra detta:

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,
    ));
}

Hoppas det hjälper. Det är lite mer besvärligt att implementera än det första tillvägagångssättet men mycket mycket bättre när det gäller prestanda.

UPPDATERA

Jag ska försöka förklara lite mer vad som händer i catch blockera:

Vid det här laget har transaktionen misslyckats. Ett undantag har tagits upp på grund av att det inte är möjligt att radera någon enhet (t.ex. på grund av en fk-begränsning).

Transaktionen har återställts och inga enheter har faktiskt tagits bort från databasen.

$deletedCars är en variabel som innehåller ID:t för de entiteter som kunde ha tagits bort (de gjorde inget undantag) men inte är det (på grund av återställningen).

$failToDeleteCar innehåller id för den enhet vars radering ledde till undantaget.

$notDeletedCars innehåller resten av enhets-ID:erna som var med i transaktionen men som vi inte vet om skulle ha lyckats eller inte.

Vid det här laget kan du återställa entitymanager (den är stängd), starta en annan fråga med ID:n som inte orsakade problem och ta bort dem (om du vill) och skicka tillbaka ett meddelande som låter användaren veta att du har tagit bort dessa enheter och att $failToDeleteCar misslyckades och togs inte bort och $notDeletedCars raderades inte heller. Det är upp till dig att bestämma vad du ska göra.

Jag kan inte återskapa problemet du nämner om Entity::getDeletedEntities() , det fungerar bra här.

Du kan förfina din kod så att du inte behövde lägga till den här metoden till dina enheter (inte ens livscykelåteranrop). Du kan till exempel använda dig av en prenumerant för att fånga händelser och en speciell klass med statiska metoder för att hålla reda på de entiteter som inte misslyckades, den som misslyckades och de som inte hade möjlighet att raderas/ uppdaterad/insatt. Jag hänvisar dig till den dokumentation som jag tillhandahållit. Det är lite mer komplicerat än det låter, kan inte ge dig ett generellt svar på några rader kod, förlåt, du måste undersöka det vidare.

Mitt förslag är att du provar koden jag gav med en falsk enhet och gör några tester för att helt förstå hur det fungerar. Sedan kan du försöka tillämpa det på dina enheter.

Lycka till!




  1. Är det verkligen bättre att använda normaliserade tabeller?

  2. Importera .frm- och .opt-filer till MySQL

  3. Hur man sammanfogar strängar i SQL Server med CONCAT()

  4. Laddar utf-8-kodad text i MySQL-tabellen