37

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

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

这给出了错误

SQLSTATE[HY000]: General error

我需要对我的代码进行哪些更改才能使其正常工作?

4

5 回答 5

93

谨防截断表

小心在任何 RDBMS 中截断表,特别是如果您想使用显式事务来实现提交/回滚功能。请阅读此答案的“我的建议”。


DDL 语句执行隐式提交

截断表语句是数据定义语言 (DDL) 语句,因此截断表语句在执行时触发对数据库的隐式COMMIT。如果您执行 aTABLE TRUNCATE那么数据库将被隐式提交——即使TABLE TRUNCATE它在一个START TRANSACTION语句中——你的表将被截断并且 a不会ROLLBACK恢复它。

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


其他用户在此线程中的评论和经验

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

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


我的推荐DELETE FROM

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

如果您想了解何时应该使用DELETE FROMvs的提示,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.

于 2013-06-12T14:39:15.150 回答
40

这是我正在使用的代码:

$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();
}
于 2012-03-14T21:14:48.980 回答
11

或者你可以试试这个:

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

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

于 2013-03-04T05:06:34.313 回答
1

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

/**
 * 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(...您的记录器服务可以使用的内容。

于 2018-08-10T12:01:35.010 回答
1

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

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

    $qb->delete();

    return $qb->getQuery()->getSingleScalarResult() ?? 0;
}
于 2020-05-13T15:34:42.810 回答