事务

事务

理解

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作要么完全地执行,要么完全地不执行。 

操作流程

设想网上购物的一次交易,其付款过程至少包括以下几步数据库操作:

一、更新客户所购商品的库存信息

二、保存客户付款信息--可能包括与银行系统的交互

三、生成订单并且保存到数据库中

四、更新用户相关信息,例如购物数量等等

正常的情况下,这些操作将顺利进行,最终交易成功,与交易相关的所有数据库信息也成功地更新。但是,如果在这一系列过程中任何一个环节出了差错,例如在更新商品库存信息时发生异常、该顾客银行帐户存款不足等,都将导致交易失败。一旦交易失败,数据库中所有信息都必须保持交易前的状态不变,比如最后一步更新用户信息时失败而导致交易失败,那么必须保证这笔失败的交易不影响数据库的状态--库存信息没有被更新、用户也没有付款,订单也没有生成。否则,数据库的信息将会一片混乱而不可预测。

数据库事务正是用来保证这种情况下交易的平稳性和可预测性的技术。

自增ID问题

比如当前ID是7,插入一条数据后,又回滚了。

然后你再插入一条数据,此时插入成功,这时候你的ID不是8,而是9.

因为虽然你之前插入回滚,但是ID还是自增了。

如果你认为自增ID不应该被事务化,那么其他事务不得不等待着,检查自增ID是被使用还是被回滚,这就导致阻塞。

Tp3 事务

//        M()->startTrans(); // 开启事务
//        M()->commit(); // 事务提交方法
//        M()->rollback(); // 事务回滚方法
<?php
namespace SMS\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index(){
        //echo 111;
        $data['operator'] = 'Testss';
        M()->startTrans();
        $result = M('feehistory')->add($data);
        $result1 = $result2 = true;
        if(!empty($result)){
            $regdelData['level'] = '111';
            $result1 = M('regdel')->add($regdelData);

            $regData['level'] = '101';
            $result2 = M('reg')->where("registryCode='13693536752-SJB-HUAX-12345678'")->save($regData);
        }

        if(!empty($result) && !empty($result1) && !empty($result2) ){
            M()->commit();    
            //$this->success('事物提交',__ROOT__);
            echo '事物提交';
        }else{
            M()->rollback();
            //$this->error('事物回滚',__ROOT__);
            echo '事物回滚';
        }
    }
}

mysqli 原生写法案例

<?php
namespace SMS\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index(){
        //echo 111;
        $data['operator'] = 'Testss';
        M()->startTrans();
        $result = M('feehistory')->add($data);
        $result1 = $result2 = true;
        if(!empty($result)){
            $regdelData['level'] = '111';
            $result1 = M('regdel')->add($regdelData);

            $regData['level'] = '101';
            $result2 = M('reg')->where("registryCode='13693536752-SJB-HUAX-12345678'")->save($regData);
        }

        if(!empty($result) && !empty($result1) && !empty($result2) ){
            M()->commit();    
            //$this->success('事物提交',__ROOT__);
            echo '事物提交';
        }else{
            M()->rollback();
            //$this->error('事物回滚',__ROOT__);
            echo '事物回滚';
        }
    }
}

mysqli 原生写法案例

<?php
$dbhost = 'localhost:3306';  // mysql服务器主机地址
$dbuser = 'root';            // mysql用户名
$dbpass = '123456';          // mysql用户名密码
$conn = mysqli_connect($dbhost, $dbuser, $dbpass);
if(! $conn )
{
    die('连接失败: ' . mysqli_error($conn));
}
// 设置编码,防止中文乱码
mysqli_query($conn, "set names utf8");
mysqli_select_db( $conn, 'RUNOOB' );
mysqli_query($conn, "SET AUTOCOMMIT=0"); // 设置为不自动提交,因为MYSQL默认立即执行
mysqli_begin_transaction($conn);            // 开始事务定义
 
if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(8)"))
{
    mysqli_query($conn, "ROLLBACK");     // 判断当执行失败时回滚
}
 
if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(9)"))
{
    mysqli_query($conn, "ROLLBACK");      // 判断执行失败时回滚
}
mysqli_commit($conn);            //执行事务
mysqli_close($conn);
?>

mysql 原生写法案例

<?php
$conn = mysql_connect('localhost','root','root') or die ("数据连接错误!!!");
mysql_select_db('test',$conn);
mysql_query("set names 'GBK'"); //使用GBK中文编码;
//开始一个事务
mysql_query("BEGIN"); //或者mysql_query("START TRANSACTION");
$sql = "INSERT INTO `user` (`id`, `username`, `sex`) VALUES (NULL, 'test1', '0')";
$sql2 = "INSERT INTO `user` (`did`, `username`, `sex`) VALUES (NULL, 'test1', '0')";//这条我故意写错
$res = mysql_query($sql);
$res1 = mysql_query($sql2); 
if($res && $res1){
mysql_query("COMMIT");
echo '提交成功。';
}else{
mysql_query("ROLLBACK");
echo '数据回滚。';
}
mysql_query("END"); 
?>

ci 事务

要使用事务来运行你的查询,你可以使用 $this->db->trans_start() 和 $this->db->trans_complete() 两个方法

$this->db->trans_start();     // 事务 开始
$this->db->query('AN SQL QUERY...');
$this->db->query('ANOTHER QUERY...');
$this->db->query('AND YET ANOTHER QUERY...');
$this->db->trans_complete(); // 事务 完成

ci 在 start 和 complete 之间,你可以运行任意多个查询,根据查询执行 成功或失败,系统将自动提交或回滚。

手动运行事务

如果你想要手动运行事务, 可以使用下面的方法:

$this->db->trans_begin(); // 手动运行事务时, 请务必使用 $this->db->trans_begin() 函数, 而不是 $this->db->trans_start().

$this->db->query('AN SQL QUERY...');
$this->db->query('ANOTHER QUERY...');
$this->db->query('AND YET ANOTHER QUERY...');

if ($this->db->trans_status() === FALSE)
{
    $this->db->trans_rollback();
}
else
{
    $this->db->trans_commit();
}

TP5 事务

自动控制事务处理:

public function delete(){
    //由于第二条语句写的有问题,只执行第一条
    Db::table('user')->delete(1038);
    Db::table('user')->deletesssssss(1039);

    //由于第二条写的有问题,两条删除都不执行
    Db::transaction(function(){
        Db::table('user')->delete(1040);                    
        Db::table('user')->deletesssssss(1041);
    });
}
手动控制事务
public function delete(){
    // 启动事务
    Db::startTrans();
    try{
        $res=Db::table('user')->delete(1039);
        if(!$res){
            throw new \Exception('删除失败');
        }else{
            throw new \Exception('删除成功');
        }
        // 提交事务
        Db::commit();
    } catch (\Exception $e) {
         dump($e->getMessage());
        // 回滚事务
        Db::rollback();
        //注意:我们做了回滚处理,所以id为1039的数据还在
    }
}

吉ICP备16008059号

Back to top