gorm目前的穩定版是無法進行批量插入(v2.0已列入開發計劃),那麼我們要如何解決呢?
分析結構
列名獲取
gorm的結構體有固定tag,那麼我們就可以取到列名
當然是通過反射來進行了。
舉個例子: AlbumId int64 gorm:"column:album_id;type:bigint(20)" json:"album_id"
我們可以看到,tag的值是以;爲分隔爲鍵值對,然後鍵值對是以:爲分隔符,那麼這樣,我們可以很輕鬆的取到列名。
func GetColumnNameObj(obj interface{}) []string {
fieldNum := reflect.TypeOf(obj).NumField()
fieldType := reflect.TypeOf(obj)
var fieldsName []string
for i := 0; i < fieldNum; i++ {
tagValue := fieldType.Field(i).Tag.Get("gorm")
for _, name := range strings.Split(tagValue, ";") {
if strings.Index(name, "column") == -1 {
continue
}
fieldsName = append(fieldsName, strings.Replace(name, "column:", "", 1))
}
fieldsName = append(fieldsName, tagValue)
}
return fieldsName
}
值獲取
既然我們能通過反射獲取列名,那麼獲取值當然也是easy.
func GetColumnValueObj(obj interface{}) []string {
fieldNum := reflect.TypeOf(obj).NumField()
fieldType := reflect.TypeOf(obj)
var fieldsValue []string
for i := 0; i < fieldNum; i++ {
fName := fieldType.Field(i).Type.Name()
// 獲取字段類型
if fName == "string" {
fieldsValue = append(fieldsValue, "string")
} else if strings.Index(fName, "uint") != -1 {
fieldsValue = append(fieldsValue, "uint")
} else if strings.Index(fName, "int") != -1 {
fieldsValue = append(fieldsValue, "int")
}
}
return fieldsValue
}
當然,我們需要處理更多的類型,我這裏只處理了通用的(u)int64和string。
sql封裝
既然我們都知道了列名,值,那剩下的東西就簡單多了。
拼接sql
func BuildSql(columns []string, lenValues int, table Tabler) (insertSql string) {
fieldNames := strings.Join(columns, ",")
lenColumns := len(columns)
var builder strings.Builder
if lenValues%lenColumns == 0 {
//長度驗證
var i = 0
for ; i < lenValues/lenColumns; i++ {
builder.WriteString("(")
for j := 0; j < lenColumns; j++ {
if j == lenColumns-1 {
builder.WriteString("?")
} else {
builder.WriteString("?,")
}
}
builder.WriteString("),")
}
}
params := strings.TrimRight(builder.String(), ",") + ";"
insertSql = fmt.Sprintf("insert into `%s` (%s) values %s", table.TableName(), fieldNames, params)
return
}
接下來就只剩下執行了
整體代碼如下:
func BatchInsertGromStruct(objs []Tabler) {
if len(objs) == 0 {
return
}
//獲取列名
columns := GetColumnNameObj(objs[0])
//獲取值
values := GetColumnValueObj(objs)
//封裝sql
sql := BuildSql(columns, len(values), objs[0])
//執行
GetOrm().Exec(sql, values...)
}
總結
其他ORM也大同小異可以使用此方式