Goframe的Orm本身不支持 Save方法的
官方文档里写着
It does not support Save/Replace features.
It does not support LastInsertId.
看了半天文档不会用Hook方法,所以动了core代码
临时这么用用,希望后期官方正式支持
1. 添加 onDuplicateKey 属性
修改文件路径: /golang/pkg/mod/github.com/gogf/gf/v2@v2.1.3/database/gdb/gdb_model.go
type Model struct {
// ... 省略
onDuplicateKey interface{} // custom onDuplicateKey
}
2. 添加OnDuplicateKey方法
修改文件路径: /golang/pkg/mod/github.com/gogf/gf/v2@v2.1.3/database/gdb/gdb_model_insert.go
// 添加OnDuplicateKey方法
func (m *Model) OnDuplicateKey(onDuplicateKey ...interface{}) *Model {
model := m.getModel()
if len(onDuplicateKey) > 1 {
model.onDuplicateKey = onDuplicateKey
} else {
model.onDuplicateKey = onDuplicateKey[0]
}
return model
}
3. 添加option属性
修改文件路径: /golang/pkg/mod/github.com/gogf/gf/v2@v2.1.3/database/gdb/gdb.go
type DoInsertOption struct {
OnDuplicateRawStr string
OnDuplicateKeyStr string // Custom string for `on duplicated` statement.
// ...省略
}
3. 修改formatDoInsertOption方法
修改文件路径: /golang/pkg/mod/github.com/gogf/gf/v2@v2.1.3/database/gdb/gdb_model_insert.go
func (m *Model) formatDoInsertOption(insertOption int, columnNames []string) (option DoInsertOption, err error) {
// ... 省略
if m.onDuplicateKey != nil {
switch m.onDuplicateKey.(type) {
case Raw, *Raw:
option.OnDuplicateKeyStr = gconv.String(m.onDuplicateKey)
default:
reflectInfo := reflection.OriginValueAndKind(m.onDuplicateKey)
switch reflectInfo.OriginKind {
case reflect.String:
option.OnDuplicateKeyStr = gconv.String(m.onDuplicateKey)
default:
return option, gerror.NewCodef(
gcode.CodeInvalidParameter,
`unsupported OnDuplicateKey parameter type "%s"`,
reflect.TypeOf(m.onDuplicateKey),
)
}
}
}
// 添加到此为止
// ...其他代码不动
}
return
}
5. 修改 pgsql DoInsert 驱动
修改文件路径: /golang/pkg/mod/github.com/gogf/gf/contrib/drivers/pgsql/v2@v2.1.3/pgsql.go
// 修改 DoInsert方法
func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case gdb.InsertOptionSave:
if option.OnDuplicateKeyStr == "" {
return nil, gerror.NewCode(
gcode.CodeNotSupported,
`使用Save方法前请先调用 OnDuplicateKey()设置key 例: g.table("user").OnDuplicateKey("id,code").Save()`,
)
} else {
option.OnDuplicateRawStr = d.formatOnDuplicate(list, option)
}
// ...下面省略
// 添加 formatOnDuplicate 方法
func (d *Driver) formatOnDuplicate(list gdb.List, option gdb.DoInsertOption) string {
var (
onDuplicateStr string
onDuplicateKeyStr string
)
if option.OnDuplicateKeyStr != "" {
onDuplicateKeyStr = option.OnDuplicateKeyStr
}
if option.OnDuplicateStr != "" {
onDuplicateStr = option.OnDuplicateStr
} else if len(option.OnDuplicateMap) > 0 {
for k, v := range option.OnDuplicateMap {
if len(onDuplicateStr) > 0 {
onDuplicateStr += ","
}
switch v.(type) {
case gdb.Raw, *gdb.Raw:
onDuplicateStr += fmt.Sprintf(
"%s=%s",
d.QuoteWord(k),
v,
)
default:
onDuplicateStr += fmt.Sprintf(
"%s=EXCLUDED.%s",
d.QuoteWord(k),
d.QuoteWord(gconv.String(v)),
)
}
}
} else {
var (
keys []string // Field names.
)
// Handle the field names and placeholders.
for k := range list[0] {
keys = append(keys, k)
}
for _, column := range keys {
// If it's SAVE operation, do not automatically update the creating time.
if len(onDuplicateStr) > 0 {
onDuplicateStr += ","
}
onDuplicateStr += fmt.Sprintf(
"%s=EXCLUDED.%s",
d.QuoteWord(column),
d.QuoteWord(column),
)
}
}
return fmt.Sprintf("ON CONFLICT (%s) DO UPDATE SET %s", onDuplicateKeyStr, onDuplicateStr)
}
5. 修改 gdb_core文件
修改文件路径: /golang/pkg/mod/github.com/gogf/gf/v2@v2.1.3/database/gdb/gdb_core.go
// 修改 option.InsertOption == InsertOptionSave 部分
if option.InsertOption == InsertOptionSave {
if option.OnDuplicateRawStr != "" {
onDuplicateStr = option.OnDuplicateRawStr
} else {
onDuplicateStr = c.formatOnDuplicate(keys, option)
}
}
然后使用方法是这样的
testModel := dao.Test.Ctx(ctx)
result, err := testModel.Data(testEntity).OnDuplicateKey("test_id").Save()
// 生成如下: INSERT INTO test (test_id,text) VALUES(2,'john') ON CONFLICT (test_id) DO UPDATE SET "test_id"=EXCLUDED.test_id "text1"=EXCLUDED.text1