gorm批量插入解决方案

简介: gorm批量插入解决方案

我有一个朋友, 最近用gorm发现官方不支持批量插入, 看官方issue 2014年就有人提过这个问题了, 不过现在还不支持


但是问题不大, 官方留出来一个执行原生SQL(db.Exec)的方法来解决这个问题, 而且官方现在在开发v2版本, 在v2版本中就会支持这个功能了


image.png


但是生活还得继续, bug还是得继续写, 本来想在网上找一个公用方法来维持一下生活, 结果竟然没找见!


20200415202812852.png


都是指定单个结构体去插入的


20200415203137638.png


这当然不行了, 写代码的意义不就是为了减轻重复的工作嘛, 既然这样, 就只能自己动手丰衣足食了

20200415203343988.png

下面为生成语句的方法

// GetBranchInsertSql 获取批量添加数据sql语句
func GetBranchInsertSql(objs []interface{}, tableName string) string {
  if len(objs) == 0 {
    return ""
  }
  fieldName := ""
  var valueTypeList []string
  fieldNum := reflect.TypeOf(objs[0]).NumField()
  fieldT := reflect.TypeOf(objs[0])
  for a := 0; a < fieldNum; a++ {
    name := GetColumnName(fieldT.Field(a).Tag.Get("gorm"))
    // 添加字段名
    if a == fieldNum-1 {
      fieldName += fmt.Sprintf("`%s`", name)
    } else {
      fieldName += fmt.Sprintf("`%s`,", name)
    }
    // 获取字段类型
    if fieldT.Field(a).Type.Name() == "string" {
      valueTypeList = append(valueTypeList, "string")
    } else if strings.Index(fieldT.Field(a).Type.Name(), "uint") != -1 {
      valueTypeList = append(valueTypeList, "uint")
    } else if strings.Index(fieldT.Field(a).Type.Name(), "int") != -1 {
      valueTypeList = append(valueTypeList, "int")
    }
  }
  var valueList []string
  for _, obj := range objs {
    objV := reflect.ValueOf(obj)
    v := "("
    for index, i := range valueTypeList {
      if index == fieldNum-1 {
        v += GetFormatField(objV, index, i, "")
      } else {
        v += GetFormatField(objV, index, i, ",")
      }
    }
    v += ")"
    valueList = append(valueList, v)
  }
  insertSql := fmt.Sprintf("insert into `%s` (%s) values %s", tableName, fieldName, strings.Join(valueList, ",")+";")
  return insertSql
}
// GetFormatField 获取字段类型值转为字符串
func GetFormatField(objV reflect.Value, index int, t string, sep string) string {
  v := ""
  if t == "string" {
    v += fmt.Sprintf("'%s'%s", objV.Field(index).String(), sep)
  } else if t == "uint" {
    v += fmt.Sprintf("%d%s", objV.Field(index).Uint(), sep)
  } else if t == "int" {
    v += fmt.Sprintf("%d%s", objV.Field(index).Int(), sep)
  }
  return v
}
// GetColumnName 获取字段名
func GetColumnName(jsonName string) string {
  for _, name := range strings.Split(jsonName, ";") {
    if strings.Index(name, "column") == -1 {
      continue
    }
    return strings.Replace(name, "column:", "", 1)
  }
  return ""
}
// BatchCreateModelsByPage 分页批量插入
func BatchCreateModelsByPage(tx *gorm.DB, dataList []interface{}, tableName string) (err error) {
  if len(dataList) == 0 {
    return
  }
  // 如果超过一百条, 则分批插入
  size := 100
  page := len(dataList) / size
  if len(dataList)%size != 0 {
    page += 1
  }
  for a := 1; a <= page; a++ {
    var bills = make([]interface{}, 0)
    if a == page {
      bills = dataList[(a-1)*size:]
    } else {
      bills = dataList[(a-1)*size : a*size]
    }
    sql := GetBranchInsertSql(bills, tableName)
    if err = tx.Exec(sql).Error; err != nil {
      fmt.Println(fmt.Sprintf("batch create data error: %v, sql: %s, tableName: %s", err, sql, tableName))
      return
    }
  }
  return
}

最后会返回批量插入的sql语句, 这样一来就舒服多了

因为我的朋友 现在类型主要用到了string, int和uint, 如果有其他类型需要加的话在 获取字段类型和GetFormatFeild里面加就行了

如果各位大佬有更好的解决方案希望可以拿出来分享学习一下

20200415202716469.png

目录
相关文章
|
数据库
gorm 事务的使用
gorm 事务的使用
297 0
|
消息中间件 数据可视化 Kafka
Mac Kafka可视化工具(kafkatool)
Mac Kafka可视化工具(kafkatool)
1390 0
|
存储 关系型数据库 MySQL
Mysql - 如何决定用 datetime、timestamp、int 哪种类型存储时间戳?
Mysql - 如何决定用 datetime、timestamp、int 哪种类型存储时间戳?
3181 0
|
缓存 JavaScript 前端开发
|
存储 Java Go
巧用 Go Map 特性对数组或切片去重
本文介绍了如何利用 Go 的复合数据类型 Map 的特性对数组或切片进行去重。值得注意的一个地方是,在使用 Map 构建 Set 时,Value 的数据类型指定为 struct{},原因是后面在添加键值对的时候,指定的 Value 为空结构体 strcut{}{},空结构体不占用内存空间。
1095 1
巧用 Go Map 特性对数组或切片去重
|
NoSQL 前端开发 Redis
Windows 下安装和配置 Redis (图文教程)
Windows 下安装和配置 Redis (图文教程)
|
SQL 关系型数据库 Go
Golang ORM框架介绍及比较
Golang ORM框架介绍及比较
|
人工智能 JavaScript Go
介绍 Agency: 使AI与Go语言无缝对接
介绍 Agency: 使AI与Go语言无缝对接
|
SQL 关系型数据库 MySQL
删库,误清数据怎么办?MySQL数据恢复指南
相信很多同学在面对线上数据库时都畏手畏脚,即使这样都难免手滑,一不小心手一抖就将数据或者是表,库删除。当然一些注重规范的公司,不会给开发人员删除表或者是库的权限,但误删数据是常有的事,那么这种情况发生,我们改怎么办呢?跑路?哈哈,当然删库跑路是句玩笑话,本文就为大家介绍一些数据误删除恢复的办法。
3497 0
|
前端开发 架构师 Java
领域驱动设计DDD从入门到代码实践
在本文中,作者将借鉴《实现领域驱动设计》的做法,介绍领域驱动设计的基本概念的同时,用一个虚拟的公司和一个虚拟的项目,把领域驱动设计进行落地实践。
14856 11
领域驱动设计DDD从入门到代码实践