与其备份数据库、修改其中的数据并在出现任何问题时恢复数据库,不如使用 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 TABLE
,TRUNCATE 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 文件导入数据时显式设置属性。
请记住彻底测试您的实现!交易可能会变得混乱。