Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Używanie zdarzeń preRemove/postRemove, aby dowiedzieć się, które zapytania mogą być wykonane, a które nie

Oto jak bym to zrobił. Nie mówię, że to najlepsze podejście, jeśli ktoś wie coś łatwiejszego lub lepszego, byłbym pierwszym zainteresowany tym, by się tego nauczyć.

Po pierwsze, są to Wydarzenia doktrynalne którego możesz użyć. Dla uproszczenia wyjaśnię, jak zrobiłbym to dla skreśleń. Również dla uproszczenia zamierzam użyć tablicy statycznej (można to zrobić na inne sposoby, podoba mi się ten) i wywołania zwrotne cyklu życia . W tym przypadku wywołania zwrotne będą bardzo prostymi metodami (dlatego można ich używać zamiast implementować słuchacz lub subskrybent ).

Załóżmy, że mamy tę jednostkę:

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 widać, zdefiniowałem dwa wywołania zwrotne, które będą wyzwalane przez zdarzenie preRemove i postRemove.

Następnie kod php encji:

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

Zwróć uwagę na wywołania zwrotne oraz statyczne tablice i metody. Za każdym razem, gdy usunięcie jest wywoływane przez Car podmiot, preRemove wywołanie zwrotne przechowa identyfikator jednostki w tablicy $preDeletedEntities . Gdy jednostka zostanie usunięta, postRemove zdarzenie zapisze identyfikator w $entityDeleted . preRemove zdarzenie jest ważne, ponieważ chcemy wiedzieć, który podmiot dokonał niepowodzenia transakcji.

A teraz w kontrolerze możemy to zrobić:

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

Mam nadzieję, że to pomoże. Wdrożenie jest nieco bardziej kłopotliwe niż pierwsze podejście, ale znacznie lepsze pod względem wydajności.

AKTUALIZUJ

Spróbuję wyjaśnić nieco więcej, co dzieje się w catch blok:

W tym momencie transakcja nie powiodła się. Zgłoszono wyjątek ze względu na to, że usunięcie jakiejś encji nie jest możliwe (np. z powodu ograniczenia fk).

Transakcja została wycofana i żadne elementy nie zostały faktycznie usunięte z bazy danych.

$deletedCars to zmienna zawierająca identyfikatory tych encji, które mogły zostać usunięte (nie zgłosiły żadnego wyjątku), ale nie są (z powodu wycofania).

$failToDeleteCar zawiera identyfikator podmiotu, którego usunięcie spowodowało wyjątek.

$notDeletedCars zawiera pozostałe identyfikatory podmiotów, które były w transakcji, ale nie wiemy, czy się udało, czy nie.

W tym momencie możesz zresetować menedżera encji (jest zamknięty), uruchomić kolejne zapytanie z identyfikatorami, które nie spowodowały problemu i usunąć je (jeśli chcesz) i odesłać wiadomość informującą użytkownika, że ​​usunąłeś te encje i że $failToDeleteCar nie powiodło się i nie zostało usunięte i $notDeletedCars nie zostały też usunięte. Od Ciebie zależy, co zrobić.

Nie mogę odtworzyć problemu, o którym wspomniałeś o Entity::getDeletedEntities() , tutaj działa dobrze.

Możesz udoskonalić swój kod, aby nie trzeba było dodawać tych metod do swoich encji (nawet wywołań zwrotnych cyklu życia). Możesz na przykład skorzystać z subskrybenta do przechwytywania zdarzeń i specjalnej klasy ze statycznymi metodami, aby śledzić te encje, które nie zawiodły, te, które zawiodły i te, które nie miały możliwości usunięcia/ zaktualizowano/wstawiono. Odsyłam Cię do dostarczonej przeze mnie dokumentacji. To trochę bardziej skomplikowane, niż się wydaje, nie jest w stanie dać ogólnej odpowiedzi w kilku linijkach kodu, przepraszam, będziesz musiał zbadać dalej.

Proponuję wypróbować kod, który dostarczyłem, z fałszywym podmiotem i przeprowadzić kilka testów, aby w pełni zrozumieć, jak to działa. Następnie możesz spróbować zastosować go do swoich jednostek.

Powodzenia!




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. przy użyciu MySql.Data.MySqlClient; nie działa

  2. Różnica między instrukcjami sql a klauzulą

  3. NULL vs DEFAULT NULL vs NULL DEFAULT NULL w tworzeniu kolumny MYSQL?

  4. mysqldump z zapytania

  5. Korzystanie z poprzedniego folderu danych MySQL w nowej instalacji MySQL