2

我写了一个 symfony 命令作为日常 cronjob 执行。任务是将数据从不同的 CSV 文件导入数据库(干净的表,不更新)。

到目前为止,这很容易,但如果导入失败,我还需要回退旧数据。以非教条的方式,我会做以下事情:

  1. 在后缀为 _tmp 的表中导入数据。
  2. 如果一切正常:重命名原始表并添加后缀 _backup,从新表中删除 _tmp 后缀。

    如果出现问题:删除 _tmp-Tables 并且原始表保持原样。

对于学说,我找不到任何好的文档来完成这项任务——而且我目前的知识还没有达到这个要求)。我发现的唯一内容是这篇文章:Doctrine Backuptables 但我对它的理解不够好,也不知道这是否是常见的方式。

我实施的解决方法是:

// Backup original Table, Truncate Original Table, Import new Data
$dbpw = $this->getContainer()->getParameter('database_password');
$dbuser = $this->getContainer()->getParameter('database_user');
$dbname = $this->getContainer()->getParameter('database_name');
exec('mysqldump -u '.$dbuser.' -p'.$dbpw.' '.$dbname.' customer > '.$dbBackupPath.'customer.sql');

$connection = $em->getConnection();
$plattform = $connection->getDatabasePlatform();
$connection->executeQuery('SET FOREIGN_KEY_CHECKS = 0;');
$truncateSQL = $plattform->getTruncateTableSQL('customer');
$connection->executeUpdate($truncateSQL);
$connection->executeQuery('SET FOREIGN_KEY_CHECKS = 1;');

try
{
   $em->flush();
}
catch(\Exception $e)
{
   exec('mysql -u '.$dbuser.' -p'.$dbpw.' '.$dbname.' < '.$dbBackupPath.'customer.sql');
}

但我认为这不是一个好的解决方案,即使它有效?

我想问你更有经验的人我该怎么办?

非常感谢

4

1 回答 1

2

与其备份数据库、修改其中的数据并在出现任何问题时恢复数据库,不如使用 Doctrine2 或 SQL 的显式事务机制。

本质上,在显式事务中,您可以进行多次插入、更新、删除等操作。在显式事务中,开发人员可以完全控制将哪些数据提交到数据库以及何时提交。而且,如果开发人员选择这样做,您甚至可以回滚显式事务,以便数据库处于事务开始之前的状态。


概念和更多信息

MySQL 显式事务参考:https ://dev.mysql.com/doc/refman/5.0/en/commit.html

Doctrine2 显式事务参考:http ://docs.doctrine-project.org/en/2.0.x/reference/transactions-and-concurrency.html 。


示例代码

我整理了一些从 CSV 文件中读取的示例代码,customers.csv并将该数据导入数据库。只有正确插入数据,才会将其提交到数据库。如果出现问题,数据库将回滚到之前的状态。

我使用了两个beginTransaction()s,但是,这个代码示例不需要最里面的一个才能正常工作。最里面的那个是作为提醒的。在您的实际实现中,您可能会调用一个函数来导入 csv 数据(而不是将其复制/粘贴到此块中),并且最里面的beginTransaction(), commit(), 和rollback()应该环绕您用于导入 CSV 数据的任何函数。

$className = 'Customer';
$con = $em->getConnection();
$tableName = $em->getClassMetadata($className)->getTableName();

// start an explicit transaction
$con->beginTransaction();
try {
    // remove the old data from the customers table
    $plat = $con->getDatabasePlatform();
    $con->executeQuery('SET FOREIGN_KEY_CHECKS = 0;');
    $con->executeQuery('DELETE FROM '.$tableName);
    $con->executeQuery('SET FOREIGN_KEY_CHECKS = 1;');

    // start another explicit transaction
    $con->beginTransaction();
    try {
            // import new customer data from `customers.csv`
            if (false !== $handle = fopen("customers.csv", "r")) {
                    // read in a row of CSV data
                    while (false !== $data = fgetcsv($handle)) {
                            // $data[0] is the first column in the csv file
                            // and it holds the customer's name.
                            $customer = new Customer();
                            $customer->setName(trim($data[0]));
                            $em->persist($customer);
                    }
            }
            // commit the imported data to the database
            $em->flush();
            $con->commit();
    } catch(\Exception $e) {
            $con->rollback();
            throw $e; // rethrow to restore database to its original state.
    }

    // If we made it here, then all rows in the customer data table were
    // removed and all new customer data was imported properly. So, save
    // everything to the database.
    $em->flush();
    $con->commit();
} catch (Exception $e) {
    // If we made it here, then something went wrong and the database is in
    // an unstable state. So, rollback to the previously stable state.
    $con->rollback();
}

注意事项

在显式事务(beginTransaction()andcommit()或之间的所有内容rollback())中,您不应执行数据定义语言 (DDL) 语句,例如等ALTER TABLETRUNCATE TABLE因为所有 DDL 都对数据库执行隐式提交,这意味着rollback()不会按预期执行。我在这里写了更多关于此的内容:如何使用 Doctrine 2 截断表格?

如果你仔细阅读上面的代码,你会注意到我使用了DELETE FROM而不是TRUNCATE因为TRUNCATE是一个 DDL 语句。重要的是要知道这DELETE FROM不等于TRUNCATE. DELETE FROM不重置AUTO_INCREMENT。因此,如果您的旧客户表中有 1032 个条目并且您运行上述代码,那么您新插入的客户表将从自动增量值 1033 开始。

你如何解决这个问题?好吧,首先——您不必重置自动增量值。对数据库而言,重要的是该id字段是唯一的。但是,如果您真的必须重置自动增量值,那么我会说您有两个选择:在customer.id导入数据时显式设置,或执行ALTER TABLE语句。一个ALTER TABLE语句听起来很简单,但它很容易出现问题,因为它是另一个 DDL 语句,它将执行对数据库的隐式提交——这与上述代码使用DELETE FROM而不是TRUNCATE. 根据我对您的特定情况的了解,我会id在从 CSV 文件导入数据时显式设置属性。

请记住彻底测试您的实现!交易可能会变得混乱。

于 2013-06-08T19:12:04.297 回答