李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
GO
正文
golang之gorm操作示例并兼容达梦数据库
Leefs
2024-03-31 PM
4296℃
0条
[TOC] ### 前言 关于gorm的概念和安装本篇将不做过多赘述,本篇只针对Gorm的简单使用进行讲述。 同时本篇使用的是beego框架,兼容MySQL和达梦数据库。 达梦数据库gorm驱动可以使用:https://github.com/Leefs/gorm-driver-dm **在该驱动中小编解决了达梦双单引号转义问题** 同时在编码过程中会存在以下三个问题: + 添加语句中无法返回自增ID + 驱动中`dmSchema.Clob`类型兼容性问题 + 查询语句时间格式兼容性问题 ### 一、连接配置 #### 1.1 app.conf写入mysql连接信息 ```ini # 连接数据库类型 mysql/dm db_type = mysql # 连接数据库账号 db_username = root # 连接数据库密码 db_password = xxxxxx # 数据库地址,可以是Ip或者域名 db_host = 127.0.0.1 # 数据库端口 db_port = 3306 # 数据库名 db_dbname = test ``` #### 1.2 初始化配置信息 ##### **1、全局配置** + 文件名称:`db_init_config.go` ```go package tools import ( "fmt" "github.com/astaxie/beego" "gorm.io/gorm" "path/filepath" "runtime" ) //定义全局的db对象,我们执行数据库操作主要通过他实现 var _db *gorm.DB var ( dbType = "" username = "" password = "" host = "" port = 0 Dbname = "" ) func init() { _, file, _, _ := runtime.Caller(0) apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".."+string(filepath.Separator)))) beego.TestBeegoInit(apppath) } func init() { dbType = beego.AppConfig.DefaultString("db_type", "mysql") fmt.Println("dbType = ", dbType) if dbType == "mysql" { initMySQL() } else if dbType == "dm" { initDM() } sqlDB, _ := _db.DB() //设置数据库连接池参数 sqlDB.SetMaxOpenConns(20) //设置数据库连接池最大连接数 sqlDB.SetMaxIdleConns(5) //连接池最大允许的空闲连接数,如果没有sql任务需要执行的连接数大于20,超过的连接会被连接池关闭。 } //获取gorm db对象,其他包需要执行数据库查询的时候,只要通过tools.getDB()获取db对象即可。 //不用担心协程并发使用同样的db对象会共用同一个连接,db对象在调用他的方法的时候会从数据库连接池中获取新的连接 func GetDB() *gorm.DB { return _db } ``` ##### **2、mysql相关初始化信息** + 文件名称:`mysql_init.go` ```go package tools import ( "fmt" "github.com/astaxie/beego" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) //包初始化函数,golang特性,每个包初始化的时候会自动执行init函数,这里用来初始化gorm。 func initMySQL() { //配置MySQL连接参数 username = beego.AppConfig.String("db_username") password = beego.AppConfig.String("db_password") host = beego.AppConfig.String("db_host") port = beego.AppConfig.DefaultInt("db_port", 3306) Dbname = beego.AppConfig.String("db_dbname") dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, Dbname) // 声明err变量,下面不能使用:=赋值运算符,否则_db变量会当成局部变量,导致外部无法访问_db变量 var err error //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。 _db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), DisableForeignKeyConstraintWhenMigrating: true, }) if err != nil { panic("连接MySQL数据库失败, error=" + err.Error()) } } ``` ##### **3、达梦相关初始化信息** + 文件名称:`dm_init.go` ```go package tools import ( "fmt" "github.com/astaxie/beego" dm "github.com/nfjBill/gorm-driver-dm" "gorm.io/gorm" "gorm.io/gorm/logger" ) /** 接入DM数据库 */ func initDM() { username = beego.AppConfig.String("db_username") password = beego.AppConfig.String("db_password") host = beego.AppConfig.String("db_host") port = beego.AppConfig.DefaultInt("db_port", 5236) //dsn := "dm://DATAOJO:Xskj@0321@114.255.136.222:5236?autoCommit=true" dsn := fmt.Sprintf("dm://%s:%s@%s:%d?autoCommit=true", username, password, host, port) // 声明err变量,下面不能使用:=赋值运算符,否则_db变量会当成局部变量,导致外部无法访问_db变量 var err error _db, err = gorm.Open(dm.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), DisableForeignKeyConstraintWhenMigrating: true, }) if err != nil { fmt.Printf("Error: failed to connect dm server: %v\n", err) return } } ``` #### 二、插入和简单基本操作 ##### 2.1 数据库映射结构体类型 ```go //定义User模型,绑定users表,ORM库操作数据库,需要定义一个struct类型和MYSQL表进行绑定或者叫映射,struct字段和MYSQL表字段一一对应 // 注意:查询时gorm中的字段对大小写敏感,如果对应不上则会返回空列 type GormUser struct { ID int64 // 主键 //通过在字段后面的标签说明,定义golang字段和表字段的关系 //例如 `gorm:"column:username"` 标签说明含义是: Mysql表的列名(字段名)为username Username string `gorm:"column:USERNAME"` Password string `gorm:"column:PASSWORD"` //创建时间,时间戳 CreateTime int64 `gorm:"column:CREATETIME"` } const ( TALE_NAME = "gorm_users" ) func (u GormUser) TableName() string { //绑定MYSQL表名为users return "gorm_users" } ``` ##### 2.2 基本操作示例 ```go // user.ID 返回插入数据的主键 // result.Error 返回 error // result.RowsAffected 返回插入记录的条数 //插入数据 func Save(user *GormUser) { err := tools.GetDB().Create(user).Error if err != nil { log.Println("insert fail : ", err) } } //根据id查询数据 func GetGormUserById(id int64) GormUser { var gormUser GormUser err := tools.GetDB().Where("id = ?", id).First(&gormUser).Error if err != nil { log.Println("get gormUser by id fail : ", err) } return gormUser } //查询全部用户 func GetAllGormUser() []GormUser { var gormUser []GormUser err := tools.GetDB().Find(&gormUser).Error if err != nil { log.Println("get users fail : ", err) } return gormUser } //根据id更新单列 func UpdateGormUserById(id int64, name string) { gormUser := GormUser{} //tools.GetDB().Where("id = ?", id).Take(&gormUser) //Update:更新一条数据 err := tools.GetDB().Model(&gormUser).Where("id = ?", id).Update("username", name).Error if err != nil { log.Println("update users fail : ", err) } } //根据id删除一条数据 func DeleteGormUserById(id int64) { err := tools.GetDB().Where("id = ?", id).Delete(&GormUser{}).Error if err != nil { log.Println("delete users fail : ", err) } } ``` ##### 2.2 插入操作示例 ```go // -------------------------------- 创建语句 ---------------------------------------------- //使用指定的字段创建 func SaveField(user *GormUser) { err := tools.GetDB().Select("username", "password").Create(user).Error if err != nil { log.Println("insert fail : ", err) } } //忽略字段插入数据 func SaveOmitField(user *GormUser) { err := tools.GetDB().Omit("password").Create(user).Error if err != nil { log.Println("insert fail : ", err) } } //通过Create批量插入 func BatchCreate(user []GormUser) { err := tools.GetDB().Create(user).Error if err != nil { log.Println("insert fail : ", err) } } //使用CreateInBatches批量插入 func CreateInBatchesFunc(user []GormUser) { // 2 表示每批插入 2 条数据 result := tools.GetDB().CreateInBatches(user, 2) fmt.Println("插入成功条数", result.RowsAffected) if result.Error != nil { log.Println("insert fail : ", result.Error) } } //使用Map集合批量插入数据 func MapBatchCreate(user []map[string]interface{}) { result := tools.GetDB().Model(&GormUser{}).Create(user) fmt.Println("插入成功条数:", result.RowsAffected) if result.Error != nil { log.Println("insert fail : ", result.Error) } } //原生SQL插入一条数据 func ExecCreate(user *GormUser) { result := tools.GetDB().Exec("insert into gorm_users (username,password,createtime) values (?,?,?)", user.Username, user.Password, user.CreateTime) if result.Error != nil { log.Println("insert fail : ", result.Error) } } ``` ##### 2.3 测试示例 + **基础功能测试** ```go package test import ( "fmt" "github.com/astaxie/beego" "gorm_dm_demo/models/mysql" "path/filepath" "runtime" "testing" "time" ) func init() { _, file, _, _ := runtime.Caller(0) apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".."+string(filepath.Separator)))) beego.TestBeegoInit(apppath) } //测试MySQL添加功能 func TestSaveGormUser(t *testing.T) { gormUser := &mysql.GormUser{ Username: "wangwu5", Password: "123456", CreateTime: time.Now().Unix(), } mysql.Save(gormUser) } // 测试根据id进行查询 func TestGetGormUserById(t *testing.T) { gormUser := mysql.GetGormUserById(1) fmt.Println("gormUser=", gormUser) } // 测试查询全部用户信息 func TestGetGormUser(t *testing.T) { gormUserAll := mysql.GetAllGormUser() //遍历全部信息 for i, user := range gormUserAll { fmt.Println(i, user) } //遍历用户的名称 /*for i, user := range gormUserAll { fmt.Println(i, user.Username) }*/ } // 测试更新一条数据 func TestUpdateGormUserNameById(t *testing.T) { mysql.UpdateGormUserById(11, "李四2") } //根据id删除一条数据 func TestDeleteGormUserById(t *testing.T) { mysql.DeleteGormUserById(12) } ``` + **添加功能测试** ```go import ( "fmt" "gorm_dm_demo/models/mysql" "testing" "time" ) // -------------------------------- 创建语句 ---------------------------------------------- //使用指定的字段创建 func TestSaveGormUserField(t *testing.T) { gormUser := &mysql.GormUser{ Username: "lisi2", Password: "123456", CreateTime: time.Now().Unix(), } mysql.SaveField(gormUser) //指定字段插入 //mysql.SaveOmitField(gormUser) //忽略字段插入 } //通过Create批量插入 func TestBatchCreate(t *testing.T) { users := []mysql.GormUser{} userDemo01 := mysql.GormUser{ Username: "lisi3", Password: "123456", CreateTime: time.Now().Unix(), } users = append(users, userDemo01) userDemo02 := mysql.GormUser{ Username: "lisi4", Password: "123456", CreateTime: time.Now().Unix(), } users = append(users, userDemo02) fmt.Println(users) mysql.BatchCreate(users) } //使用CreateInBatches批量插入 func TestCreateInBatchesFunc(t *testing.T) { users := []mysql.GormUser{} userDemo01 := mysql.GormUser{ Username: "lisi5", Password: "123456", CreateTime: time.Now().Unix(), } userDemo02 := mysql.GormUser{ Username: "lisi6", Password: "123456", CreateTime: time.Now().Unix(), } userDemo03 := mysql.GormUser{ Username: "lisi7", Password: "123456", CreateTime: time.Now().Unix(), } users = append(users, userDemo01) users = append(users, userDemo02) users = append(users, userDemo03) mysql.CreateInBatchesFunc(users) } //使用Map创建 func TestMapCreateInBatchesFunc(t *testing.T) { mapBatch := []map[string]interface{}{} mapParameter01 := map[string]interface{}{"username": "wangwu", "password": "123", "createtime": 1679023589} mapParameter02 := map[string]interface{}{"username": "wangwu2", "password": "123", "createtime": 1679023589} mapParameter03 := map[string]interface{}{"username": "wangwu3", "password": "123", "createtime": 1679023589} mapBatch = append(mapBatch, mapParameter01) mapBatch = append(mapBatch, mapParameter02) mapBatch = append(mapBatch, mapParameter03) mysql.MapBatchCreate(mapBatch) } //自定义SQL插入一条数据 func TestExecCreate(t *testing.T) { user := &mysql.GormUser{ Username: "zhaoliu", Password: "123456", CreateTime: time.Now().Unix(), } mysql.ExecCreate(user) } ``` ### 三、更新操作 ##### 3.1 数据库映射结构体类型 ```go type Goods struct { Id int Title string `gorm:"column:title"` Price float64 `gorm:"column:price"` Stock int Type int CreateTime time.Time } func (v Goods) TableName() string { return "goods" } ``` 以下示例中的查询和删除操作都使用该结构体类型 ##### 3.2 更新操作示例 ```go // -------------------------------- 更新语句 ---------------------------------------------- //通过保存数据进行修改 func UpdateGoodsById(id int64, title string) { goods := Goods{} //先通过id查询出旧数据 //tools.GetDB().Where(" id = ?", id).Take(&goods) tools.GetDB().Where("id = ?", id).Take(&goods) fmt.Println(goods) goods.Title = title result := tools.GetDB().Save(&goods) fmt.Println("修改成功条数:", result.RowsAffected) if result.Error != nil { log.Println("update fail : ", result.Error) } } //更新单个列 func UpdateGoodsSingleColumnById(id int64, title string) { goods := Goods{} tools.GetDB().Where("id = ?", id).Take(&goods) fmt.Println(goods) result := tools.GetDB().Model(&goods).Update("title", title) fmt.Println("修改成功条数:", result.RowsAffected) if result.Error != nil { log.Println("update fail : ", result.Error) } } //更新多列 func UpdateGoodsMulticolumnById(good *Goods) { goods := Goods{} tools.GetDB().Where(" id = ?", good.Id).Take(&goods) fmt.Println(goods) result := tools.GetDB().Model(&goods).Updates(good) fmt.Println("修改成功条数:", result.RowsAffected) if result.Error != nil { log.Println("update fail : ", result.Error) } } //更新选定字段 func UpdateGoodsSelectById(good *Goods) { goods := Goods{} tools.GetDB().Where(" id = ?", good.Id).Take(&goods) fmt.Println(goods) result := tools.GetDB().Model(&goods).Select("TITLE").Updates(good) fmt.Println("修改成功条数:", result.RowsAffected) if result.Error != nil { log.Println("update fail : ", result.Error) } } // 排除要更新的字段 // 注意:Select和Omit可以组合使用 示例: Select("*").Omit("title") func UpdateGoodsOmitById(good *Goods) { goods := Goods{} tools.GetDB().Where(" id = ?", good.Id).Take(&goods) fmt.Println(goods) result := tools.GetDB().Model(&goods).Omit("TITLE").Updates(good) fmt.Println("修改成功条数:", result.RowsAffected) if result.Error != nil { log.Println("update fail : ", result.Error) } } //表达式使用 func UpdateGoodsExpr(id int64) { goods := Goods{} tools.GetDB().Where(" id = ?", id).Take(&goods) fmt.Println(goods) //result := tools.GetDB().Model(&goods).Update("stock", gorm.Expr("stock + 1")) //通过Map对象使用表达式进行更新操作 result := tools.GetDB().Model(&goods).Updates(map[string]interface{}{"stock": gorm.Expr("stock + 1")}) fmt.Println("修改成功条数:", result.RowsAffected) if result.Error != nil { log.Println("update fail : ", result.Error) } } //子查询更新 func UpdateSubquery(gid int64, uid int64) { goods := Goods{} tools.GetDB().Where(" id = ?", gid).Take(&goods) fmt.Println(goods) result := tools.GetDB().Model(&goods).Update("title", tools.GetDB().Model(&GormUser{}).Select("username").Where(" id = ?", uid)) fmt.Println("修改成功条数:", result.RowsAffected) if result.Error != nil { log.Println("update fail : ", result.Error) } } ``` ##### 3.3 测试示例 ```go package test import ( "gorm_dm_demo/models/mysql" "testing" ) // -------------------------------- 更新语句 ---------------------------------------------- //通过保持数据进行修改 func TestUpdateGoodsById(t *testing.T) { mysql.UpdateGoodsById(3, "Goods") } //更新单个列 func TestUpdateGoodsSingleColumnById(t *testing.T) { mysql.UpdateGoodsSingleColumnById(2, "Hello") } //更新多个列 func TestUpdateGoodsMulticolumnById(t *testing.T) { good := &mysql.Goods{ Id: 3, Title: "SUN_2", Price: 100.44, } //mysql.UpdateGoodsMulticolumnById(good) //更新多列 //mysql.UpdateGoodsSelectById(good) //更新选定字段 mysql.UpdateGoodsOmitById(good) //排除要更新的字段 } //使用表达式更新列 func TestUpdateGoodsExpr(t *testing.T) { mysql.UpdateGoodsExpr(2) //排除要更新的字段 } //子查询更新 func TestUpdateSubquery(t *testing.T) { mysql.UpdateSubquery(5, 11) //排除要更新的字段 } ``` ### 四、查询操作 ##### 4.1 查询操作示例 ```go // -------------------------------- 查询语句 ---------------------------------------------- //Take:查询一条记录 func FindGoodsTake() Goods { goods := Goods{} //goods.Id = 3 goods.Title = "Hello" result := tools.GetDB().Take(&goods) //result := tools.GetDB().Where(" id = ? and title = ?", 3, "Hello").Take(&goods) if result.Error != nil { log.Println("find fail : ", result.Error) } return goods } //First: 根据主键正序排序后,查询第一条数据 func FindGoodsFirst() Goods { goods := Goods{} result := tools.GetDB().First(&goods) if result.Error != nil { log.Println("find fail : ", result.Error) } return goods } //Last:根据主键倒序排序后,查询最后一条记录 func FindGoodsLast() Goods { goods := Goods{} result := tools.GetDB().Last(&goods) if result.Error != nil { log.Println("find fail : ", result.Error) } return goods } //Find:查询多条记录 func FindGoodsAll() []Goods { //goods := []Goods{} var goods []Goods //result := tools.GetDB().Find(&goods) result := tools.GetDB().Raw("select * from goods").Scan(&goods) if result.Error != nil { log.Println("find fail : ", result.Error) } return goods } //Pluck:查询一列值 func FindGoodsByTitles() []string { var titles []string result := tools.GetDB().Model(&Goods{}).Pluck("title", &titles) if result.Error != nil { log.Println("find fail : ", result.Error) } return titles } // ----------------------------- 通过where条件查询 ----------------------------- func FindGoodsWhereByIds(ids []int) []Goods { goods := []Goods{} tools.GetDB().Where(" id in (?)", ids).Find(&goods) //tools.GetDB().Find(&goods) return goods } func FindGoodsWhereById(id int) Goods { goods := Goods{} goods.Id = 2 //goods.Title = "Hello" tools.GetDB().Find(&goods) return goods } // ----------------------------- 通过Select条件查询 ----------------------------- func FindGoodsSelectResultIdAndTitle() []Goods { var goods []Goods result := tools.GetDB().Select("id", "title").Find(&goods) if result.Error != nil { log.Println("find fail : ", result.Error) } return goods } // 聚合函数 func FindGoodsSelectResultCount() int { var total int result := tools.GetDB().Model(&Goods{}).Select("count(*) as total").Pluck("total", &total) if result.Error != nil { log.Println("find fail : ", result.Error) } return total } // ----------------------------- 通过order排序 ----------------------------- func FindGoodsOrder() []Goods { var goods []Goods result := tools.GetDB().Order(" id desc").Find(&goods) if result.Error != nil { log.Println("find fail : ", result.Error) } return goods } // ----------------------------- 分页 ----------------------------- //通过limit和Offset实现 func FindGoodsPage(size int, page int) []Goods { var goods []Goods result := tools.GetDB().Order("create_time desc").Limit(size).Offset(page).Find(&goods) if result.Error != nil { log.Println("find fail : ", result.Error) } return goods } // count func FindGoodsCount() int64 { var total int64 = 0 result := tools.GetDB().Model(Goods{}).Count(&total) if result.Error != nil { log.Println("find fail : ", result.Error) } return total } //分组 //统计每个商品分类下面有多少个商品 //定一个Result结构体类型,用来保存查询结果 type Result struct { Type int Total int } //scan类似Find都是用于执行查询语句,然后把查询结果赋值给结构体变量,区别在于scan不会从传递进来的结构体变量提取表名. //这里因为我们重新定义了一个结构体用于保存结果,但是这个结构体并没有绑定goods表,所以这里只能使用scan查询函数。 // 注意:Group函数必须搭配Select函数一起使用 func FindGoodsByType() []Result { var results []Result //等价于: SELECT type, count(*) as total FROM `goods` GROUP BY type HAVING (total > 0) result := tools.GetDB().Model(Goods{}).Select("type, count(*) as total").Group("type").Having("total > 0").Scan(&results) if result.Error != nil { log.Println("find fail : ", result.Error) } return results } //原生SQL查询 func FindGoodsScan(t string) []Result { var results []Result //sql := "SELECT type, count(*) as total FROM goods where create_time > ? GROUP BY type HAVING (total > 0)" sql := "SELECT type, count(*) as total FROM goods where create_time > ? GROUP BY type" //因为sql语句使用了一个问号(?)作为绑定参数, 所以需要传递一个绑定参数(Raw第二个参数). //Raw函数支持绑定多个参数 result := tools.GetDB().Raw(sql).Scan(&results) if result.Error != nil { log.Println("find fail : ", result.Error) } return results } ``` ##### 4.2 测试示例 ```go import ( "fmt" "gorm_dm_demo/models/mysql" "testing" ) // -------------------------------- 查询语句 ---------------------------------------------- // Take:查询一条记录 func TestFindGoodsTake(t *testing.T) { good := mysql.FindGoodsTake() fmt.Println(good) } func TestFindGoodsFirst(t *testing.T) { good := mysql.FindGoodsFirst() fmt.Println(good) } func TestFindGoodsLast(t *testing.T) { good := mysql.FindGoodsLast() fmt.Println(good) } func TestFindGoodsAll(t *testing.T) { goods := mysql.FindGoodsAll() for i, good := range goods { fmt.Println("index = ", i, ", good = ", good) } } func TestFindGoodsByTitles(t *testing.T) { titles := mysql.FindGoodsByTitles() for i, title := range titles { fmt.Println("index = ", i, ", title = ", title) } } // ----------------------------- 通过where条件查询 ----------------------------- func TestFindGoodsWhereByIds(t *testing.T) { ids := []int{1, 4} goods := mysql.FindGoodsWhereByIds(ids) /*for i, good := range goods { fmt.Println("index = ", i, ", good = ", good) }*/ fmt.Println(goods) } //设置select子句, 指定返回的字段 func TestFindGoodsSelectResultIdAndTitle(t *testing.T) { goods := mysql.FindGoodsSelectResultIdAndTitle() for _, good := range goods { fmt.Println(good) } } //select + 聚合函数 func TestFindGoodsSelectResultCount(t *testing.T) { total := mysql.FindGoodsSelectResultCount() fmt.Println(total) } //排序 func TestFindGoodsOrder(t *testing.T) { goods := mysql.FindGoodsOrder() for _, good := range goods { fmt.Println(good) } } //分页 func TestFindGoodsPage(t *testing.T) { goods := mysql.FindGoodsPage(2, 0) for _, good := range goods { fmt.Println(good) } } //count func TestFindGoodsCount(t *testing.T) { total := mysql.FindGoodsCount() fmt.Println(total) } func TestFindGoodsByType(t *testing.T) { //result := mysql.FindGoodsByType() result := mysql.FindGoodsScan("2023-01-11 16:55:44") // 通过原生SQL查询 for _, value := range result { fmt.Println(value) } } ``` ### 五、删除操作 ##### 5.1 删除操作示例 ```go // -------------------------------- 删除语句 ---------------------------------------------- // 根据id删除 func DeleteGoodsById(id int64) { tools.GetDB().Delete(&Goods{}, id) } //根据条件删除 func DeleteGoodsByTitle(title string) { goods := Goods{} tools.GetDB().Where(" title = ?", title).Take(&goods) result := tools.GetDB().Delete(&goods) if result.Error != nil { log.Println("delete fail : ", result.Error) } } ``` ##### 5.2 测试示例 ```go // -------------------------------- 删除语句 ---------------------------------------------- // 根据id删除 func TestDeleteGoodsById(t *testing.T) { mysql.DeleteGoodsById(7) } // 根据条件删除 func TestDeleteGoodsByTitle(t *testing.T) { mysql.DeleteGoodsByTitle("SUN_1") } ``` ### 六、字段标签 Gorm中的字段标签是指结构体字段上的附加属性。通过设置标签,开发人员可以对字段进行一些额外的配置,例如指定字段名、设置约束条件等。 + 以下是字段标签的示例 ```go type User struct { ID uint `gorm:"primaryKey"` Name string `gorm:"column:username;size:100;not null"` Age int `gorm:"not null"` Email string `gorm:"unique;not null"` Avatar []byte CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoUpdateTime"` } ``` + gorm中支持的标签 | 标签名 | 说明 | | :--------------------- | :----------------------------------------------------------- | | column | 指定 db 列名 | | type | 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:`not null`、`size`, `autoIncrement`… 像 `varbinary(8)` 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:`MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT` | | serializer | 指定将数据序列化或反序列化到数据库中的序列化器, 例如: `serializer:json/gob/unixtime` | | size | 定义列数据类型的大小或长度,例如 `size: 256` | | primaryKey | 将列定义为主键 | | unique | 将列定义为唯一键 | | default | 定义列的默认值 | | precision | 指定列的精度 | | scale | 指定列大小 | | not null | 指定列为 NOT NULL | | autoIncrement | 指定列为自动增长 | | autoIncrementIncrement | 自动步长,控制连续记录之间的间隔 | | embedded | 嵌套字段 | | embeddedPrefix | 嵌入字段的列名前缀 | | autoCreateTime | 创建时追踪当前时间,对于 `int` 字段,它会追踪时间戳秒数,您可以使用 `nano`/`milli` 来追踪纳秒、毫秒时间戳,例如:`autoCreateTime:nano` | | autoUpdateTime | 创建/更新时追踪当前时间,对于 `int` 字段,它会追踪时间戳秒数,您可以使用 `nano`/`milli` 来追踪纳秒、毫秒时间戳,例如:`autoUpdateTime:milli` | | index | 根据参数创建索引,多个字段使用相同的名称则创建复合索引 | | uniqueIndex | 与 `index` 相同,但创建的是唯一索引 | | check | 创建检查约束,例如 `check:age > 13` | | <- | 设置字段写入的权限, `<-:create` 只创建、`<-:update` 只更新、`<-:false` 无写入权限、`<-` 创建和更新权限 | | -> | 设置字段读的权限,`->:false` 无读权限 | | - | 忽略该字段,`-` 表示无读写,`-:migration` 表示无迁移权限,`-:all` 表示无读写迁移权限 | | comment | 迁移时为字段添加注释 | ### 七、问题处理 ##### 7.1 达梦数据库插入语句获取不到自增ID ```go import ( "gorm.io/gorm" ) func DataCreate(o *gorm.DB, obj interface{}) (int32, error) { var newId int32 // 插入数据并获取自增主键 if err := o.Transaction(func(tx *gorm.DB) error { //执行插入操作 result := tx.Create(obj) err := result.Error if err != nil { return err } //获取自增id if err = tx.Raw("SELECT @@identity").Scan(&newId).Error; err != nil { return err } return nil }); err != nil { return newId, err } return newId, nil } ``` 通过以上代码进行插入操作时会将自增id赋值给`newId`作为参数返回 ##### 7.2 驱动中`dmSchema.Clob`类型兼容性问题 可以通过结构体继承的方式来进行解决,具体实现代码因涉及到隐私不能贴出。 ##### 7.3 查询语句时间格式兼容性问题 ```go package utils import ( "errors" "fmt" reflect "reflect" "strings" "time" ) var ( LOCAL_TIME = "LocalTime" DO_TAG = "do_tag" TIME = "Time" RECEIVE_TIME = "2006-01-02T15:04:05+08:00" RECEIVE_TIME_LOCAL = "2006-01-02 15:04:05" ) /** data支持两种参数类型:结构体指针类型和切片类型 parTime支持三种传参方式: 第一种:将需要进行日期转换的字段对应的结构体名称以字符串的形式依次传入 第二种:调用方法时不进行传参,直接匹配到结构体名称末尾为"Time"的字段进行自动转换 第三种:在结构体中加上标签"do_tag:LocalTime",则会进行自动匹配转换 支持结构体嵌套时间类型转换、结构体Time和String类型嵌套时间转换 */ func SetDataTime(data interface{}, parTime ...string) error { v := reflect.ValueOf(data) // 获取对象所属类型种类 switch v.Kind() { case reflect.Slice: l := v.Len() for i := 0; i < l; i++ { value := v.Index(i) paramTime := MatchFieldToTimeEnd(value.Type()) //err := TranValue(value, paramTime) err := getNestStruct(value, paramTime) if err != nil { return err } } case reflect.Ptr: err := UtcToLocal(data, parTime...) if err != nil { return err } default: } return nil } func UtcToLocal(value interface{}, parTime ...string) error { t := reflect.TypeOf(value) v := reflect.ValueOf(value) t = t.Elem() v = v.Elem() if len(parTime) < 1 { // 遍历字段,匹配所有以Time结尾的字符 parTime = MatchFieldToTimeEnd(t) } var err error //err = TranValue(v, parTime) err = getNestStruct(v, parTime) if err != nil { return err } return nil } /** 遍历Time集合类型字段,进行格式转换 */ func TranValue(v reflect.Value, parTime []string) error { var err error if v.CanSet() { for _, tm := range parTime { fieldTime := v.FieldByName(tm) if fieldTime.Kind() == reflect.String { theTime, err := StringToTime(fieldTime.Interface().(string)) if err != nil { return err } fieldTime.SetString(theTime) } //fmt.Println("time:", fieldTime) //fmt.Println("Kind:", fieldTime.Kind()) } } else { err = errors.New(fmt.Sprintf("传入参数不支持数据修改操作:%v", v.Type())) } return err } // 获取嵌套结构体 func getNestStruct(v reflect.Value, parTime []string) error { err := TranValue(v, parTime) if err != nil { return err } // 遍历结构体中的每一个元素 for k := 0; k < v.NumField(); k++ { // 判断是是否是结构体类型 if kind := v.Field(k).Kind(); kind == reflect.Struct { if v.Field(k).NumField() != 0 { getNestStruct(v.Field(k), parTime) } } } return nil } /** 获取需要修改的结构体中Time字段名称 */ func MatchFieldToTimeEnd(t reflect.Type) []string { paramTime := make([]string, 0, 10) // 获取结构体属性 for i := 0; i < t.NumField(); i++ { // 获取每个字段 field := t.Field(i) tagLocal := field.Tag.Get(DO_TAG) if tagLocal == LOCAL_TIME { paramTime = append(paramTime, field.Name) } else { flagTime := strings.HasSuffix(field.Name, TIME) if flagTime && (field.Type.Kind() == reflect.String) { paramTime = append(paramTime, field.Name) } } //fmt.Printf("%s : %v", field.Name, field.Type) } return paramTime } /** 将UTC时间格式转换成本地格式 */ func StringToTime(sTime string) (string, error) { // 进行时间格式校验 if !strings.Contains(sTime, "T") || !strings.Contains(sTime, "+") { return sTime, nil } // 解析 UTC 时间字符串 var LOC, _ = time.LoadLocation("Asia/Shanghai") showTime, err := time.ParseInLocation(RECEIVE_TIME, sTime, LOC) //showTime, err := time.Parse(RECEIVE_TIME, sTime) if err != nil { return sTime, err } // 将 UTC 时间转换为本地时间 localTime := showTime.Local() // 格式化本地时间 localTimeStr := localTime.Format(RECEIVE_TIME_LOCAL) return localTimeStr, nil } ``` 另外小编测试过`gorm`对人大金仓数据库的基本语句兼容性也是没有问题的,如果需要使用到项目中希望大家进行更深层次的测试。
标签:
Golang
,
gorm
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2878.html
上一篇
golang基础使用示例
下一篇
golang算法篇之kmp算法介绍
取消回复
评论啦~
提交评论
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
48
NLP
8
标签云
Ubuntu
Map
FastDFS
数学
nginx
DataWarehouse
稀疏数组
HDFS
Elastisearch
Spring
数据结构和算法
数据结构
ClickHouse
锁
SpringCloudAlibaba
随笔
Tomcat
Elasticsearch
Flink
递归
Netty
Redis
Quartz
CentOS
Livy
Golang
FileBeat
MyBatis-Plus
Spark Core
正则表达式
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞