web-dev-qa-db-ja.com

Doctrine 2?を使用してテーブルを切り捨てる方法は?

Doctine2を使用してテーブルを切り捨てるネイティブクエリを作成する必要があると思います。

$emptyRsm = new \Doctrine\ORM\Query\ResultSetMapping();
$sql = 'TRUNCATE TABLE Article';
$query = em()->createNativeQuery($sql, $emptyRsm);
$query->execute();

これはエラーを与えます

SQLSTATE[HY000]: General error

この作業を行うには、コードに何を変更する必要がありますか?

30
murze

テーブルの切り捨てに注意

特にコミット/ロールバック機能に明示的なトランザクションを使用する場合は、RDBMS内のテーブルの切り捨てに注意してください。


DDLステートメントは暗黙的なコミットを実行します

Truncate Tableステートメントはデータ定義言語(DDL)ステートメントであるため、Truncate Tableステートメントは、実行時にデータベースへの暗黙的なCOMMITをトリガーしますTABLE TRUNCATEを実行すると、TABLE TRUNCATESTART TRANSACTIONステートメント内にある場合でも、データベースは暗黙的にコミットされ、テーブルは切り捨てられ、ROLLBACKnot復元しません。

Truncate tableステートメントは暗黙的なコミットを実行するため、Maxenceの答えは期待どおりに実行されません(ただし、質問は「テーブルを切り捨てる方法」だったため、間違っていません)。彼の答えはtryブロック内のテーブルを切り捨て、問題が発生した場合にcatchブロック内でテーブルを復元できると想定しているため、期待どおりに機能しません。これは間違った仮定です。


このスレッドでの他のユーザーのコメントと経験

ChrisAelbrechtは、truncate tableステートメントが明示的なトランザクション内にある場合でも、truncate tableステートメントをロールバックできないため、Maxenceのソリューションを適切に動作させることができませんでした。

user2130519は、残念なことに、正しい答えを提供したことでダウンボットされました(私がアップボットするまで-1)。ただし、彼は答えを正当化せずにそうしました。


私のおすすめDELETE FROM

私の推奨はDELETE FROMを使用することです。ほとんどの場合、開発者が期待するとおりに機能します。ただし、DELETE FROMにも欠点はありません。テーブルの自動インクリメント値を明示的にリセットする必要があります。テーブルの自動インクリメント値をリセットするには、別のDDLステートメントを使用する必要があります--ALTER TABLE--そして、再度、tryブロックでALTER TABLEを使用しないでください。期待どおりに動作しません。

DELETE FROM vs TRUNCATEを使用するタイミングに関するヒントが必要な場合は、 TRUNCATEとDELETE FROMの長所と短所 を参照してください。


本当に必要な場合は、切り捨てる方法を次に示します

さて、すべてのことを言った。本当にDoctrine2を使用してテーブルを切り捨てたい場合は、これを使用してください:(以下はテーブルを正しく切り捨てるMaxenceの答えの一部です)

$cmd = $em->getClassMetadata($className);
$connection = $em->getConnection();
$dbPlatform = $connection->getDatabasePlatform();
$connection->query('SET FOREIGN_KEY_CHECKS=0');
$q = $dbPlatform->getTruncateTableSql($cmd->getTableName());
$connection->executeUpdate($q);
$connection->query('SET FOREIGN_KEY_CHECKS=1');


ロールバック/コミット機能でテーブルを削除する方法。

ただし、ロールバック/コミット機能が必要な場合は、DELETE FROMを使用する必要があります(以下はMaxenceの回答の修正版です)。

$cmd = $em->getClassMetadata($className);
$connection = $em->getConnection();
$connection->beginTransaction();

try {
    $connection->query('SET FOREIGN_KEY_CHECKS=0');
    $connection->query('DELETE FROM '.$cmd->getTableName());
    // Beware of ALTER TABLE here--it's another DDL statement and will cause
    // an implicit commit.
    $connection->query('SET FOREIGN_KEY_CHECKS=1');
    $connection->commit();
} catch (\Exception $e) {
    $connection->rollback();
}

自動インクリメント値をリセットする必要がある場合は、ALTER TABLE <tableName> AUTO_INCREMENT = 1を呼び出すことを忘れないでください。

81
cmt

私が使用しているコードは次のとおりです。

$cmd = $em->getClassMetadata($className);
$connection = $em->getConnection();
$dbPlatform = $connection->getDatabasePlatform();
$connection->beginTransaction();
try {
    $connection->query('SET FOREIGN_KEY_CHECKS=0');
    $q = $dbPlatform->getTruncateTableSql($cmd->getTableName());
    $connection->executeUpdate($q);
    $connection->query('SET FOREIGN_KEY_CHECKS=1');
    $connection->commit();
}
catch (\Exception $e) {
    $connection->rollback();
}
37
Maxence

または、これを試すことができます:

$this->getEm()->createQuery('DELETE AcmeBundle:Post p')->execute();

関係がある場合は、リンクされたエンティティを慎重に処理する必要があります。

12
piers.warmers

これは、単体テストで特性からメソッドを切り捨てる例です。

_/**
 * Cleanup any needed table abroad TRUNCATE SQL function
 *
 * @param string $className (example: App\Entity\User)
 * @param EntityManager $em
 * @return bool
 */
private function truncateTable (string $className, EntityManager $em): bool {
    $cmd = $em->getClassMetadata($className);
    $connection = $em->getConnection();
    $connection->beginTransaction();

    try {
        $connection->query('SET FOREIGN_KEY_CHECKS=0');
        $connection->query('TRUNCATE TABLE '.$cmd->getTableName());
        $connection->query('SET FOREIGN_KEY_CHECKS=1');
        $connection->commit();
        $em->flush();
    } catch (\Exception $e) {
        try {
            fwrite(STDERR, print_r('Can\'t truncate table ' . $cmd->getTableName() . '. Reason: ' . $e->getMessage(), TRUE));
            $connection->rollback();
            return false;
        } catch (ConnectionException $connectionException) {
            fwrite(STDERR, print_r('Can\'t rollback truncating table ' . $cmd->getTableName() . '. Reason: ' . $connectionException->getMessage(), TRUE));
            return false;
        }
    }
    return true;
}
_

$em->flush()を使用しない場合、次のドクトリンクエリで問題が発生するリスクがあることに注意してください。

また、コントローラーでこのメソッドを使用する場合は、行_fwrite(STDERR, print_r(..._をロガーサービスが使用できるものに変更する必要があることを理解する必要があります。

1
Borys Ermokhin