问题
I assume that I need to build a native query to truncate a table using Doctine2.
$emptyRsm = new \Doctrine\ORM\Query\ResultSetMapping();
$sql = 'TRUNCATE TABLE Article';
$query = em()->createNativeQuery($sql, $emptyRsm);
$query->execute();
This gives the error
SQLSTATE[HY000]: General error
What do I need to change to my code to make this work?
回答1:
Beware of Truncating Tables
Beware of truncating tables in any RDBMS, especially if you want to use explicit transactions for commit/rollback functionality.
DDL statements perform an implicit-commit
Truncate table statements are data definition language (DDL) statements, and as such truncate table statements trigger an implicit COMMIT
to the database upon their execution. If you perform a TABLE TRUNCATE
then the database is implicitly committed to--even if the TABLE TRUNCATE
is within a START TRANSACTION
statement--your table will be truncated and a ROLLBACK
will not restore it.
Because truncate table statements perform implicit commits, Maxence's answer does not perform as expected (but it's not wrong, because the question was "how to truncate a table"). His answer does not perform as expected because it truncates the table in a try
block, and assumes that the table can be restored in the catch
block, if something goes wrong. This is an incorrect assumption.
Other user's comments & experiences in this thread
ChrisAelbrecht was unable to get Maxence's solution to work properly because you cannot rollback a truncate table statement, even if the truncate table statement is in an explicit transaction.
user2130519, unfortunately, was downvoted (-1 until I upvoted) for providing the correct answer--although he did so without justifying his answer, which is like doing math without showing your work.
My recommendation DELETE FROM
My recommendation is to use DELETE FROM
. In most cases, it will perform as the developer expects. But, DELETE FROM
does not come without drawbacks either--you must explicitly reset the auto increment value for the table. To reset the auto increment value for the table, you must use another DDL statement--ALTER TABLE
--and, again, don't use ALTER TABLE
in your try
block. It won't work as expected.
If you want tips on when you should use DELETE FROM
vs TRUNCATE
see Pros & Cons of TRUNCATE vs DELETE FROM.
If you really must, here's how to truncate
Now, with all that said. If you really want to truncate a table using Doctrine2, use this: (Below is the portion of Maxence's answer that correctly truncates a table)
$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');
How to delete a table with rollback/commit functionalty.
But, if you want rollback/commit functionality, you must use DELETE FROM
: (Below is a modified version of Maxence's answer.)
$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();
}
If you need to reset the auto increment value, remember to call ALTER TABLE <tableName> AUTO_INCREMENT = 1
.
回答2:
Here is the code I'm using:
$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();
}
回答3:
Or you could just try this:
$this->getEm()->createQuery('DELETE AcmeBundle:Post p')->execute();
If you have relations you should be careful to handle the linked entities.
回答4:
This is example truncating method from trait in unit tests.
/**
* 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;
}
Please note, that if you do not use $em->flush()
, you have a risk to have a problem with next query to doctrine.
Also you must understand, that if you use this method in a controller, you must change the lines fwrite(STDERR, print_r(...
to something your logger service can use.
来源:https://stackoverflow.com/questions/9686888/how-to-truncate-a-table-using-doctrine-2