最后更新 : 2013-11-21 18:11:44
选择语言 :

数据库事务

数据库事务对于关系复杂并且要求数据完整性的应用非常有用。本系统的事务支持事务内嵌套事务,但不支持跨库事务。要注意,MySQL 中 MyISAM 表引擎是不支持事务的,推荐数据库使用 InnoDB。

事务的用途

当你同时需要更新(插入,替换等,甚至是修改表结构等)多个表,又希望确保所有的SQL都是成功执行的,此时你就可以考虑使用事务了。使用它可以让你程序在执行异常时将前面执行成功的SQL撤销掉,以确保不会造成残留、残缺数据甚至破坏性的数据。

比如,网站需要删除一个会员数据,而和会员相关的数据表有很多,此时,你可以开启事务,然后删除要删除的数据,最后全部成功了提交事务,操作完毕,否则若发现某个SQL有问题,可以回滚数据,这样前面删了一半的数据也不会出现异常。否则,若不用事务,当删除一半数据程序异常,结果数据库里的数据就有问题了。

总结:

  • 需要保证数据完整性的多表操作推荐使用事务
  • 单表操作不需要使用事务
  • 对于不需要确保数据完整性的多表操作可以不用事务

简单的例子

// 以下代码可实现以下逻辑:
// 1.当执行插入test1表失败,则回滚,
// 2.当插入test1成功,test2失败,则回滚,test1的插入的数据也被撤销
// 3.当插入test1,test2都成功,但是更新test3的count失败时回滚,撤销掉对test1,test2的数据插入
// 4.只有全部都执行通过,才会提交生效

// 获取一个数据库对象
$db = Database::instance();
// 获取事务对象
$transaction = $db->transaction();
// 开启事务
$transaction->start();
try
{
    // 执行数据库,若下面的SQL执行失败会直接跳转到catch里

    // 插入test1表数据
    $db->insert('test1',array('name'=>'abc','set'=>'......'));
    // 插入test2数据表
    $db->insert('test2',array('name'=>'abc','set'=>'......'));
    // 更新test3数据表,计数+1
    $transactionatus = $db->value_increment('count', 1)->update('test3');
    if (!$transactionatus)
    {
        // 没有更新到数据?抛出
        throw new Exception('更新失败');
    }
    // 提交事务
    $transaction->commit();

    echo 'ok';
}
catch(Exception $e)
{
    // 有错误?回滚,回滚后,已执行的SQL都不会生效
    $transaction->rollback();

    echo $e->getMessage();
}

事务嵌套事务举例

// 获取一个数据库对象
$db = Database::instance();
// 获取事务对象
$transaction = $db->transaction();
// 开启事务
$transaction->start();
try
{
    // 执行数据库,若下面的SQL执行失败会直接跳转到catch里

    // 插入test1表数据
    $db->insert('test1',array('name'=>'abc','set'=>'......'));
    // 插入test2数据表
    $db->insert('test2',array('name'=>'abc','set'=>'......'));
    // 更新test3数据表,计数+1
    $transactionatus = $db->value_increment('count', 1)->update('test3');
    if (!$transactionatus)
    {
        // 没有更新到数据?抛出
        throw new Exception('更新失败');
    }

    // 再开启一个事务,此时相当于开启了一个子事务
    $transaction->start();
    try
    {
        // 插入test4表数据
        $db->insert('test4',array('name'=>'abc','set'=>'......'));
        // 插入test5表数据
        $db->insert('test5',array('name'=>'abc','set'=>'......'));
        $transaction->comment();
    }
    catch(Exception $e)
    {
        $transaction->rollback();
    }
    //////////////////////////////////////////////////////////////////////////////////////// 
    // 若插入test4,test5成功,则会提交子事务,否则回滚子事务。
    // 但你需要明白:因为它是属于前面的一个事务,所以若最后前面的事务也回滚了,那么这些操作都会被回滚
    // 
    // 这样就会有以下情况发生:
    // 1.父事务成功,子事务成功 - 所有的SQL都执行
    // 2.父事务成功,子事务失败 - 父事务的SQL语句被执行,子事务的SQL被撤销
    // 3.父事务失败       - 无论子事务是否成功,所有的SQL都被撤销
    ////////////////////////////////////////////////////////////////////////////////////////


    // 提交事务
    $transaction->commit();

    echo 'ok';
}
catch(Exception $e)
{
    // 有错误?回滚,回滚后,已执行的SQL都不会生效
    $transaction->rollback();

    echo $e->getMessage();
}

start()

开启事务,若之前已经开启了一个事务,则开启的会是一个子事务,支持多级嵌套

// 获取事务对象
$transaction = Database::instance()->transaction();
// 开启事务
$transaction->start();

commit()

提交事务,开启事务后执行的SQL只有在commit()后才会生效

rollback()

回滚事务,若发现有异常可以执行回滚函数撤销之前执行的SQL

// 获取事务对象
$transaction = Database::instance()->transaction();
// 开启事务
$transaction->start();
Database::instance()->insert('test1',array('title'=>'abc'));
$st = Database::instance()->insert('test2',array('title'=>'456'));
if ($st)
{
    // 提交
    $transaction->commit();
}
else
{
    // 回滚
    $transaction->rollback();
}

is_root()

判断当前是否父事务。若返回true则表示它上面没有事务了,否则表明它上面还有一个或多个事务