go-gorm / optimisticlock

optimistic lock plugin for gorm
MIT License
88 stars 16 forks source link

事务内多次 update, 生成的 SQL 错误 #4

Closed 42thcoder closed 2 years ago

42thcoder commented 2 years ago
func TestUpdateWithLock(t *testing.T) {
    db := initializers.DB.WithContext(context.Background())

    err := db.Transaction(func(tx *gorm.DB) error {
        account := models.Account{}

        tx.Model(&account).Where("id = ?", 1).First(&account)

        account.CostDiamond(decimal.NewFromInt(1))
        if result := tx.Updates(&account) ; result.RowsAffected == 0 {
            return errors.New("更新用户账户失败")
        }

        account.CostGold(decimal.NewFromInt(1))
        if result := tx.Updates(&account) ; result.RowsAffected == 0 {
            return errors.New("更新用户账户失败")
        }

        return nil
    })
    if err != nil {
        fmt.Println("error", err.Error())
        return
    }
}

生成两条 SQL

UPDATE `accounts` SET `version`=`version`+1 WHERE `accounts`.`deleted_at` IS NULL AND `accounts`.`version` = 2 AND `id` = 1 {rows": 1}

UPDATE `accounts` SET `version`=`version`+1 WHERE `accounts`.`deleted_at` IS NULL AND `accounts`.`version` = 2 AND `id` = 1 {"rows": 0}
icpd commented 2 years ago

@42thcoder 结果符合预期。 第一次update时version+1,数据库version值已被更新。第二次update时使用旧的version值作为where条件必然是会更新失败的。

42thcoder commented 2 years ago

按一般 ORM 的套路, 对象和数据库的记录应该是尽量保持一致的.

插件既然生了一条更新 version 值的 SQL, 那对象的 field 再要求开发者手动更新, 就有点奇怪.

可以参考下 JPA 和 Active Record 的实现

个人浅见哈, 如果跟维护者思路不一样, 也建议文档里增加说明

icpd commented 2 years ago

@jinzhu What do u think?

icpd commented 2 years ago

Readme has been updated