使用 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() 和 commit() 或 rollback() 之间的所有内容)中,您不应该执行数据定义语言 (DDL) 语句 - 例如,ALTER TABLE、TRUNCATE TABLE等等,因为所有 DDL 都会对数据库执行隐式提交,这意味着 rollback() 不会按预期执行。我在这里写了更多关于它的内容:How to truncate a table using 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 的原因相同。根据我对您的特殊情况的了解,我会在从 CSV 文件导入数据时显式设置 id 属性。
记得彻底测试你的实现!交易可能会变得混乱。