在ORM框架中,数据创建(Create/Insert)是最基础也是最复杂的操作之一。它不仅涉及SQL语句的构建,还需要处理各种数据库特性的适配,如自增主键、冲突处理、批量插入等。本文将根据达梦官网提供的go-20250513版本的go驱动分析达梦数据库(DM)GORM驱动中create.go文件的实现细节,揭示达梦数据库对应的解决方案。
create.go文件主要包含两个核心函数:
Create(db *gorm.DB) - 处理标准的插入操作MergeCreate(db *gorm.DB, onConflict clause.OnConflict, values clause.Values) - 处理带冲突解决的合并操作这两个函数共同构成了达梦数据库GORM驱动的写入层核心,实现了与达梦数据库特性的深度适配。
func Create(db *gorm.DB) {C
if db.Error != nil {
return
}
if db.Statement.Schema != nil && !db.Statement.Unscoped {
for _, c := range db.Statement.Schema.CreateClauses {
db.Statement.AddClause(c)
}
}
// ...继续执行
}
其中CreateClauses在dm.go中被定义和注册到GORM的回调配置中:
// CreateClauses create clauses
CreateClauses = []string{"INSERT", "VALUES", "ON CONFLICT"}
// 回调注册在dm.go的Initialize函数中
// register callbacks
callbackConfig := &callbacks.Config{
CreateClauses: CreateClauses,
QueryClauses: QueryClauses,
UpdateClauses: UpdateClauses,
DeleteClauses: DeleteClauses,
}
callbacks.RegisterDefaultCallbacks(db, callbackConfig)
db.Callback().Create().Replace("gorm:create", Create)
技术细节:
var onConflict clause.OnConflict
var hasConflict bool
if db.Statement.SQL.String() == "" {
var (
values = callbacks.ConvertToCreateValues(db.Statement)
c = db.Statement.Clauses["ON CONFLICT"]
)
// 获取ON CONFLICT字句信息
onConflict, hasConflict = c.Expression.(clause.OnConflict)
if hasConflict {
// 判断主键是否定义
if len(db.Statement.Schema.PrimaryFields) > 0 {
columnsMap := map[string]bool{}
for _, column := range values.Columns {
columnsMap[column.Name] = true
}
for _, field := range db.Statement.Schema.PrimaryFields {
// 只要有一个主键字段不在插入列中,就将 hasConflict 设为false
if _, ok := columnsMap[field.DBName]; !ok {
hasConflict = false
}
}
} else {
hasConflict = false
}
}
if hasConflict {
MergeCreate(db, onConflict, values)
} else {
// 标准插入流程
// ...
}
}
技术细节:
ON CONFLICT子句是否存在columnsMap快速查找已插入的列代码逻辑:根据 hasConflict 的值决定执行路径:
hasConflict 为true时,调用 MergeCreate 函数执行MERGE操作hasConflict 为false时,执行普通的INSERT操作达梦数据库对自增列有特殊要求,当需要显式插入自增列值时,必须启用IDENTITY_INSERT:
// 正常执行INSERT操作部分
setIdentityInsert := false
if db.Statement.Schema != nil {
if field := db.Statement.Schema.PrioritizedPrimaryField; field != nil && field.AutoIncrement {
switch db.Statement.ReflectValue.Kind() {
// 单个对象
case reflect.Struct:
_, isZero := field.ValueOf(db.Statement.Context, db.Statement.ReflectValue)
setIdentityInsert = !isZero
// 多个对象
case reflect.Slice, reflect.Array:
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
obj := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(obj).Kind() == reflect.Struct {
_, isZero := field.ValueOf(db.Statement.Context, db.Statement.ReflectValue.Index(i))
setIdentityInsert = !isZero
}
break
}
}
if setIdentityInsert && !db.DryRun && db.Error == nil {
// 启用 IDENTITY_INSERT
db.Statement.SQL.Reset()
db.Statement.WriteString("SET IDENTITY_INSERT ")
db.Statement.WriteQuoted(db.Statement.Table)
db.Statement.WriteString(" ON;")
_, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
if db.AddError(err) != nil {
return
}
// 确保最后关闭 IDENTITY_INSERT
defer func() {
db.Statement.SQL.Reset()
db.Statement.WriteString("SET IDENTITY_INSERT ")
db.Statement.WriteQuoted(db.Statement.Table)
db.Statement.WriteString(" OFF;")
db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
}()
}
}
}
技术细节:
defer确保即使发生错误也能关闭IDENTITY_INSERT当没有冲突处理需求时,构建标准的INSERT语句:
// 清空现有的SQL构建器内容,准备创建新语句
db.Statement.SQL.Reset()
db.Statement.AddClauseIfNotExists(clause.Insert{})
db.Statement.Build("INSERT")
db.Statement.WriteByte(' ')
db.Statement.AddClause(values)
if values, ok := db.Statement.Clauses["VALUES"].Expression.(clause.Values); ok {
if len(values.Columns) > 0 {
db.Statement.WriteByte('(')
for idx, column := range values.Columns {
if idx > 0 {
db.Statement.WriteByte(',')
}
// 对列名进行引号转义
db.Statement.WriteQuoted(column)
}
db.Statement.WriteByte(')')
db.Statement.WriteString(" VALUES ")
for idx, value := range values.Values {
if idx > 0 {
db.Statement.WriteByte(',')
}
db.Statement.WriteByte('(')
// AddVar参数化查询,防止SQL注入
db.Statement.AddVar(db.Statement, value...)
db.Statement.WriteByte(')')
}
db.Statement.WriteString(";")
} else {
db.Statement.WriteString("DEFAULT VALUES;")
}
}
SQL构建:
AddClauseIfNotExists确保INSERT子句存在AddVar安全处理参数化查询DEFAULT VALUES语法执行SQL后,处理结果并更新自增主键:
if !db.DryRun && db.Error == nil {
var (
rows *sql.Rows
result sql.Result
err error
updateInsertID bool // 是否需要更新主键自增列
insertID int64 // 主键自增列最新值
)
if hasConflict {
// 处理 MERGE INTO 结果
rows, err = db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
if db.AddError(err) != nil {
return
}
defer rows.Close()
if rows.Next() {
rows.Scan(&insertID)
if insertID > 0 {
updateInsertID = true
}
}
} else {
// 处理标准 INSERT 结果
result, err = db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
if db.AddError(err) != nil {
return
}
db.RowsAffected, _ = result.RowsAffected()
// 获取自增ID
if db.RowsAffected != 0 && db.Statement.Schema != nil &&
db.Statement.Schema.PrioritizedPrimaryField != nil &&
db.Statement.Schema.PrioritizedPrimaryField.HasDefaultValue {
insertID, err = result.LastInsertId()
insertOk := err == nil && insertID > 0
if !insertOk {
db.AddError(err)
return
}
updateInsertID = true
}
}
// 更新模型主键值
if updateInsertID {
switch db.Statement.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
// 批量插入:倒序分配ID
// 注意:这里返回的是最后插入的ID,需根据增量计算前面的ID
for i := db.Statement.ReflectValue.Len() - 1; i >= 0; i-- {
rv := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(rv).Kind() != reflect.Struct {
break
}
_, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(db.Statement.Context, rv)
if isZero {
db.AddError(db.Statement.Schema.PrioritizedPrimaryField.Set(db.Statement.Context, rv, insertID))
insertID -= db.Statement.Schema.PrioritizedPrimaryField.AutoIncrementIncrement
}
}
case reflect.Struct:
// 单个对象
_, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(db.Statement.Context, db.Statement.ReflectValue)
if isZero {
db.AddError(db.Statement.Schema.PrioritizedPrimaryField.Set(db.Statement.Context, db.Statement.ReflectValue, insertID))
}
}
}
}
关键处理逻辑:
双模式结果处理:
ExecContext和LastInsertId()QueryContext和Scan获取结果批量插入ID分配:
AutoIncrementIncrement计算前面记录的ID反射更新:
当存在冲突处理需求时,使用MERGE INTO语法:
func MergeCreate(db *gorm.DB, onConflict clause.OnConflict, values clause.Values) {
// 构建MERGE INTO基础语句
db.Statement.WriteString("MERGE INTO ")
db.Statement.WriteQuoted(db.Statement.Table)
db.Statement.WriteString(" USING (")
// 构建源数据(虚拟表)
for idx, value := range values.Values {
if idx > 0 {
db.Statement.WriteString(" UNION ALL ")
}
db.Statement.WriteString("SELECT ")
db.Statement.AddVar(db.Statement, value...)
db.Statement.WriteString(" FROM DUAL")
}
db.Statement.WriteString(") AS \"excluded\" (")
// 指定列名
for idx, column := range values.Columns {
if idx > 0 {
db.Statement.WriteByte(',')
}
db.Statement.WriteQuoted(column.Name)
}
db.Statement.WriteString(") ON ")
// 构建关联条件(通常为主键)
var where clause.Where
for _, field := range db.Statement.Schema.PrimaryFields {
where.Exprs = append(where.Exprs, clause.Eq{
Column: clause.Column{Table: db.Statement.Table, Name: field.DBName},
Value: clause.Column{Table: "excluded", Name: field.DBName},
})
}
where.Build(db.Statement)
// 处理匹配时的更新
if len(onConflict.DoUpdates) > 0 {
// 过滤掉关联条件中的列
var withoutOnColumns = make([]clause.Assignment, 0, len(onConflict.DoUpdates))
a:
for _, assignment := range onConflict.DoUpdates {
for _, field := range db.Statement.Schema.PrimaryFields {
if assignment.Column.Name == field.DBName {
continue a // 跳过主键列
}
}
withoutOnColumns = append(withoutOnColumns, assignment)
}
onConflict.DoUpdates = clause.Set(withoutOnColumns)
if len(onConflict.DoUpdates) > 0 {
db.Statement.WriteString(" WHEN MATCHED THEN UPDATE SET ")
onConflict.DoUpdates.Build(db.Statement)
}
}
// 处理不匹配时的插入
db.Statement.WriteString(" WHEN NOT MATCHED THEN INSERT (")
written := false
for _, column := range values.Columns {
// 跳过自增主键
if db.Statement.Schema.PrioritizedPrimaryField == nil ||
!db.Statement.Schema.PrioritizedPrimaryField.AutoIncrement ||
db.Statement.Schema.PrioritizedPrimaryField.DBName != column.Name {
if written {
db.Statement.WriteByte(',')
}
written = true
db.Statement.WriteQuoted(column.Name)
}
}
db.Statement.WriteString(") VALUES (")
written = false
for _, column := range values.Columns {
// 同样跳过自增主键
if db.Statement.Schema.PrioritizedPrimaryField == nil ||
!db.Statement.Schema.PrioritizedPrimaryField.AutoIncrement ||
db.Statement.Schema.PrioritizedPrimaryField.DBName != column.Name {
if written {
db.Statement.WriteByte(',')
}
written = true
db.Statement.WriteQuoted(clause.Column{Table: "excluded", Name: column.Name})
}
}
db.Statement.WriteString(")")
db.Statement.WriteString(";")
// 自增主键特殊处理
if db.Statement.Schema.PrioritizedPrimaryField != nil &&
db.Statement.Schema.PrioritizedPrimaryField.AutoIncrement {
db.Statement.WriteString("SELECT ")
db.Statement.WriteQuoted(db.Statement.Schema.PrioritizedPrimaryField.DBName)
db.Statement.WriteString(" FROM ")
db.Statement.WriteQuoted(db.Statement.Table)
db.Statement.WriteString(" ORDER BY ")
db.Statement.WriteQuoted(db.Statement.Schema.PrioritizedPrimaryField.DBName)
db.Statement.WriteString(" DESC LIMIT 1;")
}
}
多阶段构建过程:
基础框架:
MERGE INTO target_table USING source_dataSELECT ... FROM DUAL构建,支持多行插入关联条件:
ON条件clause.Eq确保正确转义更新处理:
WHEN MATCHED THEN UPDATE SET语法插入处理:
WHEN NOT MATCHED THEN INSERT语法自增ID获取:
MERGE INTO中直接获取自增IDSELECT ... ORDER BY id DESC LIMIT 1获取达梦特性适配:
FROM DUAL:达梦(及Oracle)要求SELECT必须有FROM子句LastInsertId()获取,需额外查询defer func() {
// 确保 IDENTITY_INSERT 被关闭
// 即使前面出错也会执行
}()
db.AddError累积错误而非立即返回for _, assignment := range onConflict.DoUpdates {
for _, field := range db.Statement.Schema.PrimaryFields {
if assignment.Column.Name == field.DBName {
continue a // 跳过主键列
}
}
withoutOnColumns = append(withoutOnColumns, assignment)
}
// 自增主键,自动处理
user := User{Name: "test", Age: 18}
db.Create(&user) // user.ID 自动填充
// 需要显式设置ID
user := User{ID: 100, Name: "test", Age: 18}
db.Create(&user) // 自动启用 IDENTITY_INSERT
// 存在则更新,不存在则插入
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"name", "age"}),
}).Create(&user)
达梦数据库GORM驱动中的create.go实现,展示了如何在保持GORM框架一致性的同时,深度适配特定数据库的特性。通过对IDENTITY_INSERT的精细管理、MERGE INTO语法的完整实现、自增ID的智能获取等技术,解决了达梦数据库特有的技术挑战。
该实现对开发者而言,理解这些底层实现有助于更高效地使用GORM方言包连接达梦数据库,避免常见陷阱,充分发挥ORM框架和数据库的优势。
在企业级应用开发中,这种对细节的关注和对特性的深度适配,往往是系统稳定性和性能的关键。通过对create.go的深度剖析,我们不仅学到了具体的技术实现,更领悟到高质量数据库驱动的设计哲学:尊重数据库特性、保持框架一致性、确保资源安全、优化性能表现。
文章
阅读量
获赞
