Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Menggunakan acara preRemove/postRemove untuk mendapatkan kueri mana yang dapat dieksekusi dan mana yang tidak

Inilah cara saya melakukannya. Saya tidak mengatakan ini adalah pendekatan terbaik, jika ada yang tahu sesuatu yang lebih mudah atau lebih baik, saya akan menjadi orang pertama yang tertarik untuk mempelajarinya.

Pertama, ini adalah Acara doktrin yang dapat Anda gunakan. Demi kesederhanaan, saya akan menjelaskan bagaimana saya akan melakukannya untuk penghapusan. Juga untuk kesederhanaan, saya akan menggunakan array statis (bisa dilakukan dengan beberapa cara lain, saya suka yang ini) dan panggilan balik siklus hidup . Dalam hal ini, callback akan menjadi metode yang sangat sederhana (itulah mengapa tidak apa-apa untuk menggunakannya daripada menerapkan pendengar atau pelanggan ).

Katakanlah kita memiliki entitas ini:

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]

Seperti yang Anda lihat, saya telah menetapkan dua callback yang akan dipicu dengan event preRemove dan event postRemove.

Kemudian kode php entitas:

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

Perhatikan callback dan array dan metode statis. Setiap kali penghapusan dipanggil melalui Car entitas, preRemove callback akan menyimpan id entitas dalam array $preDeletedEntities . Ketika entitas dihapus, postRemove acara akan menyimpan id di $entityDeleted . preRemove event ini penting karena kita ingin tahu entitas mana yang membuat transaksi gagal.

Dan sekarang, di controller kita bisa melakukan ini:

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

Semoga membantu. Ini sedikit lebih rumit untuk diterapkan daripada pendekatan pertama tetapi jauh lebih baik dalam hal kinerja.

PERBARUI

Saya akan mencoba menjelaskan sedikit lebih banyak apa yang terjadi di dalam catch blok:

Pada titik ini, transaksi telah gagal. Pengecualian telah diajukan karena fakta bahwa penghapusan beberapa entitas tidak dimungkinkan (misalnya karena kendala fk).

Transaksi telah dibatalkan dan tidak ada entitas yang benar-benar dihapus dari database.

$deletedCars adalah variabel yang berisi id entitas yang dapat dihapus (mereka tidak memunculkan pengecualian apa pun) tetapi tidak (karena roll back).

$failToDeleteCar berisi id entitas yang penghapusannya menimbulkan pengecualian.

$notDeletedCars berisi id entitas lainnya yang ada dalam transaksi tetapi kita tidak tahu apakah akan berhasil atau tidak.

Pada titik ini, Anda dapat mengatur ulang entitasmanager (sudah ditutup), meluncurkan kueri lain dengan id yang tidak menyebabkan masalah dan menghapusnya (jika Anda suka) dan mengirim kembali pesan yang memberi tahu pengguna bahwa Anda menghapus entitas tersebut dan itu $failToDeleteCar gagal dan tidak dihapus dan $notDeletedCars juga tidak dihapus. Terserah Anda untuk memutuskan apa yang harus dilakukan.

Saya tidak dapat mereproduksi masalah yang Anda sebutkan tentang Entity::getDeletedEntities() , ini berfungsi dengan baik di sini.

Anda dapat memperbaiki kode Anda sehingga Anda tidak perlu menambahkan metode ini ke entitas Anda (bahkan callback siklus hidup). Anda dapat, misalnya, menggunakan pelanggan untuk merekam acara dan kelas khusus dengan metode statis untuk melacak entitas yang tidak gagal, yang gagal, dan yang tidak memiliki peluang untuk dihapus/ diperbarui/dimasukkan. Saya merujuk Anda ke dokumentasi yang saya berikan. Ini sedikit lebih rumit daripada kedengarannya, tidak dapat memberikan jawaban umum dalam beberapa baris kode, maaf, Anda harus menyelidiki lebih lanjut.

Saran saya adalah Anda mencoba kode yang saya berikan dengan entitas palsu dan melakukan beberapa tes untuk sepenuhnya memahami cara kerjanya. Kemudian Anda dapat mencoba menerapkannya ke entitas Anda.

Semoga berhasil!




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Manajer db mana untuk Tabel 100Go?

  2. GALAT 1115 (42000):Kumpulan karakter tidak dikenal:'utf8mb4'

  3. Memanggil prosedur di dalam fungsi melempar MySQL ERROR 1422

  4. Grup fasihDengan membuat SQLSTATE[42000] dengan kueri SQL yang valid di Laravel 5.3

  5. MySQL memilih baris teratas dengan nilai kondisi yang sama