ThinkPHP 事务操作

1. 前言

前面小节学习了如何对数据库增删改查操作,本小节介绍一下和事务相关的操作。

2. MySQL 事务介绍

事务其实就是同一组具有原子性的 SQL 操作,也可以说是一个独立的工作单元。若同一组数据库操作中有任何一条 SQL 语句由于崩溃或其它的原因不能执行成功,则该组数据库操作都不会执行,事务内的语句要么全部执行成功,要么全部执行失败。

3. 事务相关的案例

例如在一个银行转账业务中存在一笔转账,小明向小红转账 100 元,数据库的操作逻辑主要分为三步,步骤如下:

  • 第一步,查询小明账户余额数据,若大于 100元 就可以继续转账步骤,否则转账失败。
  • 第二步,小明账户余额 减少100元
  • 第三步,小红账户余额 增加100元
    上面的例子中的三个步骤属于同一组数据库操作,必须要在同一个事务中操作,任何一个步骤执行失败,则所有的数据操作需要回滚至操作数据库之前的状态。

4. 事务特性

4.1 原子性

同一个事务可以被视为同一个不可分割的最小操作单元,其中全部的数据库操作要么全部都成功,要么全部都失败回滚,不能只执行其中某一部分,这就是事务的 原子性

4.2 一致性

同一个事务操作总是从一个一致性的状态转换到另一个一致性的状态,如前面小明向小红转账业务中,即使步骤三执行失败也不会导致小明账户减少 100 元,而小红账户还没增加 100 元这种情况。

4.3 隔离性

对于某一个事务来说,其内部一组数据库操作没有提交之前,其他的事务操作是对该事务操作是不可见的。MySQL 的四种隔离级别分别是 顺序读可重复读读提交读未提交

4.4 持久性

若同一个事务中对数据库的操作一旦被提交,那么它们对数据库所做的数据修改会永久地保存到数据库中,不会因为系统崩溃而丢失。

5. ThinkPHP 事务操作

这里以之前第 08 小节介绍的课程表(course)教师表(teacher) 创建教师和课表信息数据接口为例学习事务操作。

5.1 定义接口路由

route/study.php 路由文件中定义一个路由如下:

//插入数据事务操作
Route::post('transaction','app\controller\Study\TeacherController@transaction');

如下图所示:
图片描述

5.2 新建控制器

接下来在 app\controller\Study\ 目录中添加 TeacherController 控制器:

<?php


namespace app\controller\Study;


use app\BaseController;

class TeacherController extends BaseController
{

}

如下图所示:
图片描述

5.3 定义路由指向的方法

下面定义一下路由里面指向的 transaction() 方法,方法如下:

<?php

namespace app\controller\Study;
use app\BaseController;
use app\Models\Study\CourseModel;
use app\Models\Study\TeacherModel;
use think\Exception;
use think\facade\Db;
use think\Request;

class TeacherController extends BaseController
{
    public function transaction(Request $request)
    {
        //启动事务
        Db::startTrans();
        try {
            $time = time();
            //插入教师 A 信息
            $teacherModel             = new TeacherModel();
            $teacherModel->name       = $request->param("name", "");
            $teacherModel->age        = $request->param("age", 0, "intval");
            $teacherModel->id_number  = $request->param("id_number", "");
            $teacherModel->created_at = $time;
            $teacherModel->save();

            //插入 A 教师课程相关相关信息
            $courseModel              = new CourseModel();
            $courseModel->course_name = $request->param("course_name", "");
            $courseModel->teacher_id  = $teacherModel->id;//上面插入教师产生的 id
            $courseModel->created_at  = $time;
            $courseModel->save();

            //提交事务
            Db::commit();
        } catch (\Exception $exception) { //捕获异常
            //若出现异常错误则回滚事务
            Db::rollback();
            throw new Exception($exception->getMessage());//抛出错误异常
        }

        return json("请求成功");
    }
}

如下图所示:
图片描述

Tips: 其中 $request 是依赖注入的请求对象,和之前的 $this->request->param() 获取参数本质是一样的,另外需要用到的类请记得 use 一下,如 use think\facade\Db,若使用 phpStorm 编辑器会自动补齐。

5.4 请求接口

如下图所示,可以在 postman 请求上述定义好的接口,并且带上需要添加的参数字段,字段名需要和上面接收的一致:

{
    "name" : "爱因斯坦",
    "age" : 55,
    "id_number" : "42011720100512065X",
    "course_name" : "广义相对论"
}

请求如下图所示:
图片描述

教师表(teacher)信息如下:
图片描述
课程表(course)信息如下:
图片描述

5.5 查看 sql 日志信息

可以在 runtime/log 目录对应的以日期命名的目录中,找到新增的日志文件可以看到内容如下图:
图片描述

6. 小结

本小节介绍了事务,事务其实就是同一组具有原子性的 SQL 操作,也可以说是一个独立的工作单元,事务有四个特点,分别是 原子性一致性隔离性持久性,然后通过定义一个教师和课程表插入接口来展示事务操作,如在实际业务中遇到类似的业务逻辑也可以按照本小节的方式操作。

Tips: 代码仓库:https://gitee.com/love-for-poetry/tp6