如何使用Doctrine 2截断表格?

时间:2012-03-13 15:19:31

标签: php mysql doctrine-orm nativequery

我假设我需要构建一个本机查询来使用Doctine2截断表。

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

这给出了错误

SQLSTATE[HY000]: General error

我需要更改为我的代码才能使其正常工作?

5 个答案:

答案 0 :(得分:80)

小心截断表

请注意截断任何RDBMS中的表,特别是如果要将显式事务用于提交/回滚功能。


DDL语句执行隐式提交

截断表语句是数据定义语言(DDL)语句,因此 truncate table语句在执行时会触发对数据库的隐式COMMIT 。如果执行TABLE TRUNCATE,则隐式提交数据库 - 即使TABLE TRUNCATESTART TRANSACTION语句中 - 您的表将被截断,ROLLBACK恢复它。

因为truncate table语句执行隐式提交,所以 Maxence的答案没有按预期执行(但它没有错,因为问题是“如何截断表”)。他的答案没有按预期执行,因为它截断了try块中的表,并假设如果出现问题,可以在catch块中恢复该表。这是一个不正确的假设。


其他用户的评论&这个帖子的经验

ChrisAelbrecht无法让Maxence的解决方案正常工作,因为即使truncate table语句处于显式事务中,您也无法回滚truncate table语句。

不幸的是,用户2130519因为提供了正确的答案而被低估(-1直到我投票) - 尽管他没有证明他的答案是正确的,这就像在没有展示你的工作的情况下做数学一样。


我的建议DELETE FROM

我的建议是使用DELETE FROM。在大多数情况下,它将按照开发人员的预期执行。但是,DELETE FROM也没有缺点 - 你必须明确重置表的自动增量值。要重置表的自动增量值,必须使用另一个DDL语句 - ALTER TABLE - 再次,不要在ALTER TABLE块中使用try。它不会按预期工作。

如果您需要有关何时使用DELETE FROM vs TRUNCATE的提示,请参阅Pros & Cons of TRUNCATE vs 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

答案 1 :(得分:37)

以下是我正在使用的代码:

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

答案 2 :(得分:13)

或者你可以试试这个:

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

如果你有关系,你应该小心处理链接的实体。

答案 3 :(得分:0)

这是在单元测试中从特征截断方法的示例。

/**
 * 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(...行更改为记录程序服务可以使用的行。

答案 4 :(得分:0)

这是您可以使用原则(不忽略外键检查)从symfony中的实体存储库中删除所有实体的方法。该函数返回已删除实体的数量。

/**
 * @return int
 */
public function deleteAll(): int
{
    $qb = $this->createQueryBuilder('t');

    $qb->delete();

    return $qb->getQuery()->getSingleScalarResult() ?? 0;
}