go-gorm / optimisticlock

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

希望能支持gorm-gen。 #9

Closed noahlann closed 2 years ago

noahlann commented 2 years ago

在使用gorm-gen时,出现panic

生成语句:

g.GenerateModel("user", optVersionType := gen.FieldType("version", "optimisticlock.Version"))

生成的模型:

type User struct {
    ID            int64                  `gorm:"column:id;primaryKey" json:"id"`                                       // 主键
// 忽略其它字段
    Version       optimisticlock.Version `gorm:"column:version;not null" json:"version"`                               // 乐观锁
}

执行操作:

q.User.WithContext(ctx).Create(&model.User{ID: 123})

出现错误:

panic: reflect: call of reflect.Value.Field on slice Value [recovered]
panic: reflect: call of reflect.Value.Field on slice Value

panic({0x13f5d40, 0xc000438060})
    C:/Users/NorthLan/.g/go/src/runtime/panic.go:838 +0x207
reflect.Value.Field({0x13d3160?, 0xc000438048?, 0x13b7da5?}, 0x0?)
    C:/Users/NorthLan/.g/go/src/reflect/value.go:1220 +0xef
gorm.io/gorm/schema.(*Field).setupValuerAndSetter.func1({0xc000132c00?, 0x13?}, {0x13d3160?, 0xc000438048?, 0xf1be5d?})
    C:/Users/NorthLan/go/pkg/mod/gorm.io/gorm@v1.23.6/schema/field.go:446 +0x65
gorm.io/plugin/optimisticlock.VersionCreateClause.ModifyStatement({0x20?}, 0xc00022d340)
    C:/Users/NorthLan/go/pkg/mod/gorm.io/plugin/optimisticlock@v1.0.7/version.go:68 +0x4f
gorm.io/gorm.(*Statement).AddClause(0xc00022d340, {0x155ab08, 0xc000413680})
    C:/Users/NorthLan/go/pkg/mod/gorm.io/gorm@v1.23.6/statement.go:257 +0x46
gorm.io/gorm/callbacks.Create.func1(0xc000432330)
    C:/Users/NorthLan/go/pkg/mod/gorm.io/gorm@v1.23.6/callbacks/create.go:47 +0xf88
gorm.io/gorm.(*processor).Execute(0xc0001ffae0, 0xc00040fb28?)

    C:/Users/NorthLan/go/pkg/mod/gorm.io/gorm@v1.23.6/callbacks.go:130 +0x433
gorm.io/gorm.(*DB).Create(0x144e1c0?, {0x13d3160?, 0xc000438048})
    C:/Users/NorthLan/go/pkg/mod/gorm.io/gorm@v1.23.6/finisher_api.go:24 +0xa5
gorm.io/gen.(*DO).Save(0xc00040fc88, {0x13d3160, 0xc000438048})
    C:/Users/NorthLan/go/pkg/mod/gorm.io/gen@v0.3.8/do.go:553 +0xba

看情况应该是 version.go 中这个方法的异常,大概是通过反射寻找非零值的 Version

func (v VersionCreateClause) ModifyStatement(stmt *gorm.Statement) {
    var value int64 = 1
    if val, zero := v.Field.ValueOf(stmt.Context, stmt.ReflectValue); !zero {
        if version, ok := val.(Version); ok {
            value = version.Int64
        }
    }
    stmt.SetColumn(v.Field.DBName, value)
}

摸一摸具体原因可能是:

因为gorm-gen的 Create 方法是传递的可变参数,stmt.ReflectValue 的 Kind是23(slice)而非25(struct)出现的错误。

// gorm-gen生成的Create方法
// u.DO.Create底层调用的还是 gorm.DB 的 Create 方法
func (u userDo) Create(values ...*model.User) error {
    if len(values) == 0 {
        return nil
    }
    return u.DO.Create(values)
}

特别地,如果使用 gorm.DB 的 Create 就不会存在问题,例: db.Create(&user)

特别地2,如果使用 gorm.DB 的 Create ,但是传入slice,一样也会出现问题。例:db.Create(&[]model.User{user})

由此可得,修改修改 ModifyStatement 应该就好了。

noahlann commented 2 years ago

补充:

这个问题应该不是不支持 gorm-gen ,而是不支持批量插入。

希望能修改ModifyStatement (create) 内部的反射功能。

noahlann commented 2 years ago

实际上将此方法修改为

func (v VersionCreateClause) ModifyStatement(stmt *gorm.Statement) {
    var reflectValue reflect.Value
    switch stmt.ReflectValue.Kind() {
    case reflect.Slice, reflect.Array:
        rv := stmt.ReflectValue.Index(stmt.CurDestIndex)
        reflectValue = reflect.Indirect(rv)
    default:
        reflectValue = stmt.ReflectValue
    }
    var value int64 = 1
    if val, zero := v.Field.ValueOf(stmt.Context, reflectValue); !zero {
        if version, ok := val.(Version); ok {
            value = version.Int64
        }
    }
    stmt.SetColumn(v.Field.DBName, value)
}

即可解决当前问题,但不清楚是否有其它情况发生。

VersionUpdateClause 的此方法暂时还没有修改方案。

icpd commented 2 years ago

最近有点忙,期待你的pr

noahlann commented 2 years ago

最近有点忙,期待你的pr

暂时没想好 Update 的修改方案, struct to map 的意图也还没搞清楚。。。待会儿先将Create的修改pr上去吧,后期等官方修复。

a631807682 commented 2 years ago

@crazy6995 原来你在看啊,Slice/Array需要支持每一项单独设置,使用v.Field.Set 代替 stmt.SetColumn。 看你的意思update也有问题,我把 https://github.com/go-gorm/optimisticlock/pull/10 关了,都交给你了。

icpd commented 2 years ago

暂时没想好 Update 的修改方案, struct to map 的意图也还没搞清楚。。。待会儿先将Create的修改pr上去吧,后期等官方修复。

struct to map是因为version是int型,而期望值version+1是string,struct无法赋值

noahlann commented 2 years ago

@a631807682 我反复测试了一下,update其实并没有类似create的批量api,所以其实是没有问题的。 另外我看了一下你的实现,已经完美解决了问题,就是 setVersionColumn 方法名有点问题。 要不你reopen #10 或者我重新弄个pr也行。