Как обрезать таблицу с помощью Doctrine 2?
Я предполагаю, что мне нужно создать собственный запрос для усечения таблицы с использованием Doctine2.
$emptyRsm = new \Doctrine\ORM\Query\ResultSetMapping();
$sql = 'TRUNCATE TABLE Article';
$query = em()->createNativeQuery($sql, $emptyRsm);
$query->execute();
Это дает ошибку
SQLSTATE[HY000]: General error
Что мне нужно изменить в моем коде, чтобы это работало?
4 ответа
Остерегайтесь усеченных столов
Остерегайтесь усечения таблиц в любой СУБД, особенно если вы хотите использовать явные транзакции для фиксации / отката.
Операторы DDL выполняют неявную фиксацию
Операторы усеченной таблицы являются операторами языка определения данных (DDL), и, как таковые, операторы усеченной таблицы вызывают неявное COMMIT
в базу данных при их исполнении. Если вы выполняете TABLE TRUNCATE
то база данных неявно привержена - даже если TABLE TRUNCATE
находится в пределах START TRANSACTION
заявление - ваша таблица будет обрезана и ROLLBACK
не восстановит его.
Поскольку операторы усеченной таблицы выполняют неявные коммиты, ответ Maxence не работает должным образом (но это не так, потому что вопрос был "как усечь таблицу"). Его ответ не работает должным образом, потому что он усекает таблицу в try
и предполагает, что таблица может быть восстановлена в catch
заблокировать, если что-то пойдет не так. Это неверное предположение.
Комментарии других пользователей и опыт в этой теме
ChrisAelbrecht не удалось заставить решение Maxence работать должным образом, потому что вы не можете откатить оператор усеченной таблицы, даже если оператор усеченной таблицы находится в явной транзакции.
К сожалению, пользователь 2130519 получил отрицательный ответ (-1, пока я не проголосовал) за предоставление правильного ответа - хотя он сделал это без обоснования своего ответа, что похоже на выполнение математики без показа вашей работы.
Моя рекомендация DELETE FROM
Я рекомендую использовать DELETE FROM
, В большинстве случаев он будет работать так, как ожидает разработчик. Но, DELETE FROM
Также не обошлось без недостатков - вы должны явно сбросить значение автоматического приращения для таблицы. Чтобы сбросить значение автоматического приращения для таблицы, вы должны использовать другой оператор DDL: ALTER TABLE
- и, опять же, не используйте ALTER TABLE
в вашем try
блок. Это не будет работать, как ожидалось.
Если вы хотите советы о том, когда вы должны использовать DELETE FROM
против TRUNCATE
смотрите плюсы и минусы TRUNCATE против 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
,
Вот код, который я использую:
$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();
}
Или вы можете просто попробовать это:
$this->getEm()->createQuery('DELETE AcmeBundle:Post p')->execute();
Если у вас есть отношения, вы должны быть осторожны, чтобы обращаться со связанными объектами.
Вот как вы можете удалить все объекты из репозитория объектов в symfony с помощью doctrine (не игнорируя проверки внешнего ключа). Функция возвращает количество удаленных объектов.
/**
* @return int
*/
public function deleteAll(): int
{
$qb = $this->createQueryBuilder('t');
$qb->delete();
return $qb->getQuery()->getSingleScalarResult() ?? 0;
}
Это пример метода усечения черты в модульных тестах.
/**
* 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(...
к чему-то, что может использовать ваша служба регистрации.