ThinkPHP 5.0.24
MySql 5.7.26
遇到的问题:
当前需求需要根据不同类型对表添加新的字段,并且这其中还有对(其他)表的增删改查。我打算通过事务来实现所有的数据库操作,理想情况下是可以在出错的时候进行回滚,包括建表建字段这种情况。
然而实际情况是,建表建字段是无法进行回滚的,能回滚的只有数据。而且即使建字段的时候发生了错误,之前的对数据的操作也会被直接被提交。
举个例子:
存在一个表 user
假设我需要修改id=1的name,并且添加一个字段 sex。但是当前sex已经存在,添加的话肯定会报错:
// 添加字段
$sql = "ALTER TABLE `user` ADD `sex` tinyint(4) NOT NULL DEFAULT 0 COMMENT '性别'; ";
// 开启事务
Db::startTrans();
try {
// 修改id=1的name
Db::name('user')->update(['id'=>1,'name'=>'小明'.rand(1,999)]);
Db::execute($sql);
// 提交事务
Db::commit();
var_dump('success');
}catch (\Exception $e){
// 回滚事务
Db::rollback();
var_dump($e->getMessage());
}
执行后:
即使打印了错误,但是数据库中的name还是被修改了。
分析和解决方法
一开始我还以为是TP框架做了限制之类的,然后我通过使用SQL直接执行:
BEGIN;
UPDATE `user` SET `name` = '小明666' WHERE id = 1;
ALTER TABLE `user` ADD `sex` tinyint(4) NOT NULL DEFAULT 0 COMMENT '性别';
COMMIT;
还是一样的结果,所有我猜想这可能就是mysql的机制吧。
解决方法的话,可能就是把DML操作和DDL操作分开来写,DDL操作要独立处理错误,而DML就可以直接catch然后rollback。