【问题标题】:How to truncate a table using Doctrine 2?如何使用 Doctrine 2 截断表格?
【发布时间】:2012-03-30 00:32:17
【问题描述】:

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

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

这给出了错误

SQLSTATE[HY000]: General error

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

【问题讨论】:

  • 你的语法是正确的。您是否尝试过使用“从文章中删除”。
  • 可能有一个外键引用了正在删除的表中的某个字段。您是否尝试过从 MySQL 控制台执行查询?

标签: php mysql doctrine-orm nativequery


【解决方案1】:

谨防截断表

注意在任何 RDBMS 中截断表,尤其是当您想使用显式事务来实现提交/回滚功能时。请阅读此答案的“我的建议”。


DDL 语句执行隐式提交

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

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


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

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

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


我的推荐DELETE FROM

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

如果您想知道什么时候应该使用DELETE FROMTRUNCATE,请参阅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

【讨论】:

  • 只是因为我不得不谷歌它: $con->exec('ALTER TABLE ' . $cmd->getTableName() . ' AUTO_INCREMENT = 1;');
  • 别忘了在alter命令后加上$em->clear();,否则你就有机会将旧记录插入数据库
  • 这有一种真正的“不要在家里尝试这个孩子”的感觉!不过经过深思熟虑的答案。我假设如果我更改表来重置自动增量编号,它会破坏回滚的能力,然后根据你所说的?
  • 对于那些使用 PostgreSQL 的人 - 通常 DDL 命令可以在事务中运行,如文档TRUNCATE is transaction-safe with respect to the data in the tables: the truncation will be safely rolled back if the surrounding transaction does not commit.中所述
【解决方案2】:

这是我正在使用的代码:

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

【讨论】:

  • 刚刚用 Symfony 2.1.0 版本测试了这段代码并且工作正常!谢谢!
  • 我正要开始编写特定于引擎的代码来截断表格,感谢您的有用贡献:)
  • 更简单的mysql版本$em->getConnection()->query('START TRANSACTION;SET FOREIGN_KEY_CHECKS=0; TRUNCATE table1; TRUNCATE table2;SET FOREIGN_KEY_CHECKS=1; COMMIT;')
【解决方案3】:

或者你可以试试这个:

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

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

【讨论】:

  • 删除表中的所有记录与截断表不同。
  • 是的,自动增量没有设置为零
  • 删除而不是截断要慢得多
  • 在某些情况下可能有用:例如:我正在编写功能测试,并且由于我的表中的实体很少,我想性能应该不会那么差。
  • 重置一个小表格(比如设置或一个小的常量容器)很酷,因为它非常易读
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

    这就是你如何在 symfony 中使用学说从实体存储库中删除所有实体(不忽略外键检查)。函数返回已删除实体的计数。

    /**
     * @return int
     */
    public function deleteAll(): int
    {
        $qb = $this->createQueryBuilder('t');
    
        $qb->delete();
    
        return $qb->getQuery()->getSingleScalarResult() ?? 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2012-01-21
      • 1970-01-01
      • 2014-11-30
      • 2022-01-11
      • 2012-01-28
      • 2011-08-27
      • 2011-08-23
      • 2012-02-07
      • 1970-01-01
      相关资源
      最近更新 更多