关于数据库事务提交位置的疑惑

今天和同事在讨论关于事务这块,在事务提交位置发生了分歧,我也尝试了一下,不管是同事主张的写法,还是我的写法,都能达到效果,但是谁也说服不了谁,因此想请教一下,哪种方式更合理?实例代码:

// 我主张的写法。
public function test() {
    DB::beginTransaction();
    try {
        $user = UserInfo::query()->create([
            "name" => "李大",
            "age" => 18
        ]);

        $book = Book::query()->create([
            "name" => "测试书籍1",
            "user_id" => $user->id
        ]);
        DB::commit();
        if ($book) {
            return("提交成功");
        }
        throw new Exception("提交失败");
    } catch (Exception $exception) {
        DB::rollback();
        throw new Exception($exception->getMessage());
    }
}

// 同事主张的写法:
public function test() {
    DB::beginTransaction();
    try {
        $user = UserInfo::query()->create([
            "name" => "李大asdadasdasd",
            "age" => 18
        ]);

        $book = Book::query()->create([
            "name" => "测试书籍1",
            "user_id" => $user->id
        ]);
        if ($book) {
            DB::commit();
            return("提交成功");
        }
        throw new Exception("提交失败");
    } catch (Exception $exception) {
        DB::rollback();
        throw new Exception($exception->getMessage());
    }
}

我主张的写法理由是:返回的结果是在事务都提交成功以后去获取,并不需要在最后一个新增成功以后再去提交。事务本身就是都为真true才会提交,有一个为false就回滚并且抛出异常。
同事的理由是,使用serverRepository模式的时候,控制器调用server层的业务代码,会存在很多条件判断和保存不成功的情况,因此需要判断最后一个为true的时候才提交。
针对这一点,我提出的反驳是,如果在控制器层使用的try,catchserver层代码中有异常就可以抛出,这样的话就能及时捕获异常。
但是谁也说服不了谁,请问谁的主张更为合理一点,或者说针对的是不同的场景和业务?

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 15

事物提交了以后还可以回滚吗?

1年前 评论
skarner

赞成你同事的写法

1年前 评论

if ($book) {} 这里是判断数据是否有问题

如果不判断数据是否存在问题,事务的作用会打折的。

你们的争论焦点应该是这个,如果数据存在问题, 还需要写入数据库吗?

1年前 评论
她来听我的演唱会 (楼主) 1年前
kis龍 (作者) 1年前
Tomo11111

可以试试事务的闭包写法,全局捕捉异常。

DB::transaction(function () {
    $user = UserInfo::query()->create([
        "name" => "foo",
        "age" => 18
    ]);

    $user->books()->create([
        "name" => "bar"
    ]);
});
1年前 评论

只有我看不明白为什么要加判断吗?
还有这个异常什么情况才会抛出呢?
如果 $book 保存成功会走到 return,如果不成功框架应该会抛出异常吧

throw new Exception("提交失败");

所以直接这样是不是就行了。

public function test() {
    DB::beginTransaction();
    try {
        $user = UserInfo::query()->create([
            "name" => "李大",
            "age" => 18
        ]);

        $book = Book::query()->create([
            "name" => "测试书籍1",
            "user_id" => $user->id
        ]);
        DB::commit();
        // 这个 return 写法我也没见过
        return("提交成功");
    } catch (Exception $exception) {
        DB::rollback();
        throw new Exception($exception->getMessage());
    }
}
1年前 评论
kis龍 1年前

首先你这个业务创建为什么会失败?如果失败了是属于什么异常?外层的异常捕获是捕获你运行发生的异常但是你主动抛出的这个异常明显就很奇怪无论它有没有创建成功你都不应该抛出异常而是应该提示,再一个你这个关联是必须有的吗?第一个创建失败那么第二个其实成不成功都无所谓这种怎么能成为一个事务,如果第一个用户创建失败那么这本是肯定创建失败所有看你业务怎么需要怎么实现。。

1年前 评论

我会这样写:

    public function test()
    {
        try {
            DB::beginTransaction();
            $user = UserInfo::query()->create([
                "name" => "李大",
                "age" => 18
            ]);
            Book::query()->create([
                "name" => "测试书籍1",
                "user_id" => $user->id
            ]);
            DB::commit();
            return '提交成功';
        } catch (Exception $e) {
            DB::rollback();
            // log $e->getMessage()
            return '提交失败';
        }
    }

明确告诉你把,insert语句在没有产生异常的情况下,是一定有影响记录数返回的,你那个 if 就是多余的。然后说 update语句,大部分业务场景,也是不需要判断返回值的,只关注语句有没有产生异常就好了,少部分需要关注返回值确定有影响记录的行数,这时候才加 if 判断

1年前 评论
她来听我的演唱会 (楼主) 1年前

不如先讨论一下什么情况下既创建失败,又不触发 Exception。。。

1年前 评论
她来听我的演唱会 (楼主) 1年前

为啥你们都把 DB::beginTransaction(); 写在 try catch 之外呢 我都是写在里面的

1年前 评论
忆往昔弹指间 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!