sql >> データベース >  >> RDS >> Mysql

preRemove / postRemoveイベントを使用して、実行できるクエリと実行できないクエリを取得します

    これが私がそれをする方法です。これが最善のアプローチだと言っているわけではありません。誰かがもっと簡単なことやもっと良いことを知っているなら、私が最初にそれを学ぶことに興味があるでしょう。

    まず、これらはドキュメントイベント あなたが使用できること。簡単にするために、削除のためにそれをどのように行うかを説明します。また、簡単にするために、静的配列(他の方法でも実行できます。これが好きです)とライフサイクルコールバック 。この場合、コールバックは非常に単純なメソッドになります(そのため、リスナーまたはサブスクライバー

    このエンティティがあるとしましょう:

    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]
    

    ご覧のとおり、preRemoveイベントとpostRemoveイベントでトリガーされる2つのコールバックを定義しました。

    次に、エンティティのphpコード:

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

    コールバックと静的配列およびメソッドに注意してください。 Carを介してremoveが呼び出されるたび エンティティ、preRemove コールバックは、エンティティのIDを配列$preDeletedEntitiesに格納します 。エンティティが削除されると、postRemove イベントはIDを$entityDeletedに保存します 。 preRemove どのエンティティがトランザクションを失敗させたかを知りたいので、イベントは重要です。

    そして今、コントローラーでこれを行うことができます:

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

    それが役に立てば幸い。最初のアプローチよりも実装が少し面倒ですが、パフォーマンスの点でははるかに優れています。

    更新

    catchの内部で何が起こっているのかをもう少し説明しようと思います ブロック:

    この時点で、トランザクションは失敗しました。一部のエンティティを削除できないため(たとえば、fk制約のため)、例外が発生しました。

    トランザクションはロールバックされており、データベースから実際に削除されたエンティティはありません。

    $deletedCars は、削除された可能性がある(例外は発生しなかった)が削除されなかった(ロールバックのため)エンティティのIDを含む変数です。

    $failToDeleteCar 削除によって例外が発生したエンティティのIDが含まれます。

    $notDeletedCars トランザクションに含まれていたが、成功したかどうかはわからない残りのエンティティIDが含まれています。

    この時点で、entitymanagerをリセットし(閉じています)、問題を引き起こさなかったIDで別のクエリを起動して削除し(必要に応じて)、それらのエンティティを削除したことをユーザーに通知するメッセージを送信できます。 $failToDeleteCar 失敗し、削除されず、$notDeletedCars どちらも削除されませんでした。何をするかはあなた次第です。

    Entity::getDeletedEntities()についておっしゃった問題を再現できません 、ここでは正常に機能しています。

    このメソッドをエンティティに追加する必要がないように(ライフサイクルコールバックさえも)コードを改良することができます。たとえば、サブスクライバーを使用してイベントをキャプチャし、静的メソッドを使用して特別なクラスを使用して、失敗しなかったエンティティ、失敗したエンティティ、および削除する機会がなかったエンティティを追跡できます。更新/挿入。私が提供したドキュメントを参照してください。思ったよりも少し複雑で、数行のコードで一般的な答えを出すことはできません。申し訳ありませんが、さらに調査する必要があります。

    私の提案は、私が偽のエンティティで提供したコードを試して、それがどのように機能するかを完全に理解するためにいくつかのテストを行うことです。次に、それをエンティティに適用してみることができます。

    頑張ってください!




    1. クエリから結果を取得する

    2. OSX 10.6でPythonとDjangoでMySQLdbを使用するにはどうすればよいですか?

    3. mysql_escape_stringの脆弱性

    4. SQL count(*)のパフォーマンス