linux
go env -w GOPROXY=https://goproxy.cn,direct 或 export GOPROXY=https://goproxy.cn echo "export GOPROXY=https://goproxy.cn" >> ~/.profile && source ~/.profile
下载所有依赖包
go mod tidy
go mod init github/caoxiaojin/gorm_class
GOPROXY=https://goproxy.cn
https://gorm.cn/zh_CN/docs
https://learnku.com/docs/gorm/v2/update/9734
go get -u gorm.io/gorm go get -u gorm.io/driver/mysql
一、表操作
package main import ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/schema" "time" ) func main() { //dns := "db1:db123456@tcp(192.168.85.128:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local" //db,err := gorm.Open(mysql.Open(dns),&gorm.Config{}) db,err :=gorm.Open(mysql.New(mysql.Config{ DSN: "db1:db123456@tcp(192.168.85.128:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local", DefaultStringSize: 171, }),&gorm.Config{ SkipDefaultTransaction: false, NamingStrategy: schema.NamingStrategy{ TablePrefix: "t_", //表明前缀 `User` 的表名 应该是 `t_users` SingularTable: false, // 使用单数表名,启用 表示 `t_user` }, DisableForeignKeyConstraintWhenMigrating: true, // 逻辑外键 }) type User struct { Name string } sqlDB,_ := db.DB() sqlDB.SetMaxIdleConns(10) // 连接池最大的空闲连接数 sqlDB.SetMaxOpenConns(100) // 连接池最多容纳的链接数量 sqlDB.SetConnMaxLifetime(time.Hour) // 链接池中链接的最大可复用时间 // 建表一 _ =db.AutoMigrate(&User{}) // 建表二 M := db.Migrator() //M.CreateTable(&User{}) // 查看表是否存在 fmt.Println(M.HasTable(&User{})) fmt.Println(M.HasTable("t_users")) // 删除表 //fmt.Println(M.DropTable(&User{})) // 重命名表 M.RenameTable(&User{},"t_users_old") fmt.Println(db,err) }
二、常规表信息设计
package main import ( "database/sql" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/schema" "time" ) var GLOBL_DB *gorm.DB func main() { db,_ :=gorm.Open(mysql.New(mysql.Config{ DSN: "db1:db123456@tcp(192.168.85.128:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local", DefaultStringSize: 171, }),&gorm.Config{ SkipDefaultTransaction: false, NamingStrategy: schema.NamingStrategy{ TablePrefix: "t_", //表明前缀 `User` 的表名 应该是 `t_users` SingularTable: false, // 使用单数表名,启用 表示 `t_user` }, DisableForeignKeyConstraintWhenMigrating: true, // 逻辑外键 }) sqlDB,_ := db.DB() sqlDB.SetMaxIdleConns(10) // 连接池最大的空闲连接数 sqlDB.SetMaxOpenConns(100) // 连接池最多容纳的链接数量 sqlDB.SetConnMaxLifetime(time.Hour) // 链接池中链接的最大可复用时间 GLOBL_DB = db Testusercreate() } func Testusercreate() { GLOBL_DB.AutoMigrate(&Testuser{}) } type Model struct { UUID uint `gorm:"primaryKey"` Time time.Time `gorm:"column:my_time"` } type Testuser struct { Model Model `gorm:"embedded;embeddedPrefix:qm_"` Name string `gorm:"default:qm"` // 设置默认值 Email *string `gorm:"not null"` // 不能为空 Age uint8 `gorm:"comment:年龄"` Birthday *time.Time ActiveAT sql.NullTime CreateAt time.Time UpdateAt time.Time }
三、单表的 增删改查
1)增
type Testuser struct { gorm.Model Name string `gorm:"default:qm"` // 设置默认值 Age uint8 `gorm:"comment:年龄"` } func ActionCreate() { GLOBL_DB.Create(&Testuser{Name: "张三",Age: 18}) GLOBL_DB.Select("Name").Create(&Testuser{Name: "里斯",Age: 19}) // 只创建 Name 字段 GLOBL_DB.Omit("Name").Create(&Testuser{Name: "里斯",Age: 19}) // 除了 Name 字段 GLOBL_DB.Create(&[]TestUser{ {Name: "张三",Age: 18}, {Name: "张三",Age: 18}, {Name: "张三",Age: 18}, {Name: "张三",Age: 18}, }) }
2)查
func ActionSelect() { var result = make(map[string]interface{}) GLOBL_DB.Model(&Testuser{}).First(&result) fmt.Println(result) var User Testuser GLOBL_DB.Model(&Testuser{}).First(&User) fmt.Println(User) GLOBL_DB.Model(&Testuser{}).Take(&User) fmt.Println(User) GLOBL_DB.Model(&Testuser{}).Last(&User) fmt.Println(User) // 主键检索 dbRes := GLOBL_DB.Model(&Testuser{}).First(&User,10) fmt.Println(errors.Is(dbRes.Error,gorm.ErrRecordNotFound)) // 条件查询 GLOBL_DB.Where("name = ?","qm").First(&User) GLOBL_DB.Where("name = ? AND age = 21","gm").First(&User) GLOBL_DB.Where(map[string]interface{}{ "name":"gm", }).First(&User) GLOBL_DB.Where("name = ?","gm").Or("age = ?",11).First(&User) GLOBL_DB.Where(Testuser{Name: "gm"}).First(&User) GLOBL_DB.First(&User,"name = ?","gm") GLOBL_DB.First(&User,map[string]interface{}{"name":"gm"}) GLOBL_DB.First(&User,Testuser{Name: "gm"}) // 查询多条数据 var Users []Testuser GLOBL_DB.Find(&Users) GLOBL_DB.Where("name LIKE ?","%gm%").Find(&Users) GLOBL_DB.Select("name").Where("name LIKE ?","%gm%").Find(&Users) // 只要name字段 GLOBL_DB.Omit("name").Where("name LIKE ?","%gm%").Find(&Users) // 不要name字段 // 提前定义接收的字段 var u []Userinfo GLOBL_DB.Model(&Testuser{}).Where("name LIKE ?","%gm%").Find(&u) } type Userinfo struct { NAME string Age uint8 }
3)更新
func ActionUpdate () { // Update 只更新选择的字段 // Updates 更新所有字段,此时有2种形式,一种为Map,一种为结构体,结构体零值不参与更新 // save 无论如何都更新,所有内容包括 0 值 GLOBL_DB.Model(&Testuser{}).Where("name LIKE ?","%gm%").Update("name","lisi") var users []Testuser dbRes := GLOBL_DB.Model(&Testuser{}).Where("name LIKE ?","%gm%").Find(&users) for k:= range users { users[k].Age = 18 } dbRes.Save(&users) var user Testuser GLOBL_DB.First(&user).Updates(Testuser{Name: "",Age: 0}) // 不参与更新 GLOBL_DB.First(&user).Updates(map[string]interface{}{"Name":"","Age":0}) // 参与更新 // 批量更新 GLOBL_DB.Find(&users).Updates(map[string]interface{}{"Name":"","Age":0}) }
删除
func Actiondel() { var users []Testuser GLOBL_DB.Where("name = ?","gm").Delete(&users) // 软删除 GLOBL_DB.Unscoped().Where("name = ?","gm").Delete(&users) // 硬删除 }
原生sql
func ActionSql() { var users []Testuser GLOBL_DB.Raw("SELECT * FROM users WHERE name = ?","qm").Scan(&users) }
或查询
db.Where("name = ? OR age > ?", "Alice", 18).Find(&users) db.Where("name = ?", "Alice").Or("age > ?", 18).Find(&users) db.Where("name = ?", "Alice").Or(db.Where("age > ?", 18).Where("gender = ?", "female")).Find(&users)
四、关联查询之 一对一关联
1)一对一关联:
注意初始化表一张表就可以了。关联数据会被关联创建
// belongsTO func One2one() { GLOBL_DB.AutoMigrate(&Dog{}) g:=GirlGod{ Model:gorm.Model{ ID: 1, }, Name: "琪琪", } d := Dog{ Model:gorm.Model{ ID: 1, }, Name: "张三", GirlGod: g, } GLOBL_DB.Create(&d) } type Dog struct { gorm.Model Name string GirlGodID uint GirlGod GirlGod } type GirlGod struct { gorm.Model Name string }
1.1)关联查询。Preload
func Find() { var dog Dog GLOBL_DB.Preload("GirlGod").Find(&dog,2) fmt.Println(dog) } type Dog struct { gorm.Model Name string GirlGodID uint GirlGod GirlGod } type GirlGod struct { gorm.Model Name string }
1.2)关联操作
func One2one () { d:=Dog{ Model:gorm.Model{ ID: 1, }, } g:=GirlGod{ Model:gorm.Model{ ID: 1, }, } g2:=GirlGod{ Model:gorm.Model{ ID: 1, }, } //创建关联 GLOBL_DB.Model(&d).Association("GirlGod").Append(&g) //删除关联 GLOBL_DB.Model(&d).Association("GirlGod").Delete(&g) //更改关联 GLOBL_DB.Model(&d).Association("GirlGod").Replace(&g,&g2) // 清理所有的关系 GLOBL_DB.Model(&d).Association("GirlGod").Clear() }
2)一对一关联的另一种写法。初始化2张表。并创建数据

package main import ( "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/schema" "time" ) var GLOBL_DB *gorm.DB func main() { db,_ :=gorm.Open(mysql.New(mysql.Config{ DSN: "db1:db123456@tcp(192.168.85.128:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local", DefaultStringSize: 171, }),&gorm.Config{ SkipDefaultTransaction: false, NamingStrategy: schema.NamingStrategy{ TablePrefix: "t_", //表明前缀 `User` 的表名 应该是 `t_users` SingularTable: false, // 使用单数表名,启用 表示 `t_user` }, DisableForeignKeyConstraintWhenMigrating: true, // 逻辑外键 }) sqlDB,_ := db.DB() sqlDB.SetMaxIdleConns(10) // 连接池最大的空闲连接数 sqlDB.SetMaxOpenConns(100) // 连接池最多容纳的链接数量 sqlDB.SetConnMaxLifetime(time.Hour) // 链接池中链接的最大可复用时间 GLOBL_DB = db One2one() } // belongsTO func One2one() { GLOBL_DB.AutoMigrate(&GirlGod{},&Dog{}) d := Dog{ Name: "李四", } g:=GirlGod{ Name: "美眉", Dog: d, } GLOBL_DB.Create(&g) } type Dog struct { gorm.Model Name string GirlGodID uint } type GirlGod struct { gorm.Model Name string Dog Dog }
2.1) 查询。通过Preload 预加载把关联数据拿出来
func Find() { var girl GirlGod GLOBL_DB.Preload("Dog").Find(&girl,2) fmt.Println(girl) }
五、一对多关联
1)数据创建
type Dog struct { gorm.Model Name string GirlGodID uint } type GirlGod struct { gorm.Model Name string Dogs []Dog } func One2one () { GLOBL_DB.AutoMigrate(&Dog{},&GirlGod{}) d1 := Dog{ Model:gorm.Model{ ID: 1, }, Name: "1号", } d2 := Dog{ Model:gorm.Model{ ID: 2, }, Name: "2号", } g := GirlGod{ Model:gorm.Model{ ID: 1, }, Name: "美女", Dogs: []Dog{d1,d2}, } GLOBL_DB.Create(&g) }
1.1) 查询
func Find() { var girl GirlGod // 查询所有的关系数据 GLOBL_DB.Preload("Dogs").First(&girl) // 查询关系中携带条件 GLOBL_DB.Preload("Dogs","name = ?","1号").First(&girl) // 查询关系中携带条件,条件可以是函数 GLOBL_DB.Preload("Dogs", func(db *gorm.DB) *gorm.DB { return db.Where("name = ?","1号") }).First(&girl) fmt.Println(girl) }
2)3张表,一对多,多对一关系查询
type Info struct { gorm.Model Money int DogID uint } type Dog struct { gorm.Model Name string GirlGodID uint Info Info } type GirlGod struct { gorm.Model Name string Dogs []Dog } func Find() { var girl GirlGod // 链式关联查询 GLOBL_DB.Preload("Dogs.Info").First(&girl) // 链式关联查询 加条件查询 GLOBL_DB.Preload("Dogs.Info").Preload("Dogs","name = ?","1号").First(&girl) GLOBL_DB.Preload("Dogs.Info","money > 100").Preload("Dogs","name = ?","1号").First(&girl) // joins的用法 GLOBL_DB.Preload("Dogs", func(db *gorm.DB) *gorm.DB { return db.Joins("Info").Where("money > 200") }).First(&girl) }
六、多对多关联
1)创建多个数据
type Info struct { gorm.Model Money int DogID uint } type Dog struct { gorm.Model Name string Info Info GirlGods []GirlGod `gorm:"many2many:dog_girl_god"` } type GirlGod struct { gorm.Model Name string Dogs []Dog `gorm:"many2many:dog_girl_god"` } func Many2many () { GLOBL_DB.AutoMigrate(&Dog{},&GirlGod{},&Info{}) i := Info{ Money: 2000, } g1 := GirlGod{ Name: "美女1号", } g2 := GirlGod{ Name: "美女2号", } d:= Dog{ Name: "狗子1号", GirlGods: []GirlGod{g1,g2}, Info: i, } GLOBL_DB.Create(&d) }
2) 查询与关系维护
func Find() { d:=Dog{ Model:gorm.Model{ ID: 1, }, } // 查看 Dog 包含下面的GirlGods信息 GLOBL_DB.Preload("GirlGods").Find(&d) fmt.Println(d) // 只需要 Dog下面的GirlGods信息 var girls []GirlGod GLOBL_DB.Model(&d).Association("GirlGods").Find(&girls) fmt.Println(girls) // 查看 girls包含下面的Dog信息 GLOBL_DB.Model(&d).Preload("Dogs").Association("GirlGods").Find(&girls) GLOBL_DB.Model(&d).Preload("Dogs.Info").Association("GirlGods").Find(&girls) } func guanxi() { GLOBL_DB.Model(&d).Association("GirlGods").Append(&g1,&g2) GLOBL_DB.Model(&d).Association("GirlGods").Delete(&g2) GLOBL_DB.Model(&d).Association("GirlGods").Replace(&g2) GLOBL_DB.Model(&d).Association("GirlGods").Replace(&g2,g1) GLOBL_DB.Model(&d).Association("GirlGods").Clear() }
七、多态的使用
1)构建数据
type Jiazi struct { ID uint Name string Xiaofengche Xiaofengche `gorm:"polymorphic:Owner"` } type Yujie struct { ID uint Name string Xiaofengche Xiaofengche `gorm:"polymorphic:Owner"` } type Xiaofengche struct { ID uint Name string OwnerType string OwnerID uint } func Many2many () { GLOBL_DB.AutoMigrate(&Jiazi{},&Yujie{},&Xiaofengche{}) GLOBL_DB.Create(&Jiazi{Name: "夹子",Xiaofengche: Xiaofengche{Name: "小风车"}}) GLOBL_DB.Create(&Yujie{Name: "御姐",Xiaofengche: Xiaofengche{Name: "大风车"}}) }
可自定义类型字段
列如
1 2 3 4 5 | type Jiazi struct { ID uint Name string Xiaofengche Xiaofengche `gorm: "polymorphic:Owner;polymorphicValue:huhu" ` } |
2)多态也支持一对多
type Jiazi struct { ID uint Name string Xiaofengche []Xiaofengche `gorm:"polymorphic:Owner;polymorphicValue:huhu"` } type Xiaofengche struct { ID uint Name string OwnerType string OwnerID uint } func Many2many () { GLOBL_DB.Create(&Jiazi{Name: "夹子",Xiaofengche: []Xiaofengche{ {Name: "小风车1"}, {Name: "小风车2"}, }}) }
八、外键关联

// foreignKey 默认引用id // references 重置引用 type Jiazi struct { ID uint Name string Xiaofengche []Xiaofengche `gorm:"foreignKey:JiaziName;references:Name"` } type Xiaofengche struct { ID uint Name string JiaziName string } func Many2many () { GLOBL_DB.AutoMigrate(&Jiazi{},&Xiaofengche{}) GLOBL_DB.Create(&Jiazi{Name: "夹子",Xiaofengche: []Xiaofengche{ {Name: "小风车1"}, {Name: "小风车2"}, }}) }
九、many2many的外键关联
// foreignKey 默认引用id // references 重置引用 type Jiazi struct { ID uint Name string Xiaofengche []Xiaofengche `gorm:"many2many:jiazi_fengche;foreignKey:Name;references:FCName"` } type Xiaofengche struct { ID uint FCName string JiaziName string } func Many2many () { GLOBL_DB.AutoMigrate(&Jiazi{},&Xiaofengche{}) GLOBL_DB.Create(&Jiazi{Name: "夹子",Xiaofengche: []Xiaofengche{ {FCName: "小风车1"}, {FCName: "小风车2"}, }}) }
扩展

// foreignKey 默认引用id // references 重置引用 type Jiazi struct { ID uint Name string Xiaofengche []Xiaofengche `gorm:"many2many:jiazi_fengche;foreignKey:Name;references:FCName;joinReferences:fengche"` } type Xiaofengche struct { ID uint FCName string JiaziName string } func Many2many () { GLOBL_DB.AutoMigrate(&Jiazi{},&Xiaofengche{}) GLOBL_DB.Create(&Jiazi{Name: "夹子",Xiaofengche: []Xiaofengche{ {FCName: "小风车1"}, {FCName: "小风车2"}, }}) }
标签
many2many:jiazi_fengche;
foreignKey:Name;
references:FCName;
joinReferences:fengche
八、事务的使用
1)使用 Transaction 来提交事务
func TestTransaction(){ flag := false GLOBL_DB.AutoMigrate(&TMG{}) GLOBL_DB.Transaction(func(tx *gorm.DB) error { tx.Create(&TMG{Name: "汉字"}) tx.Create(&TMG{Name: "汉字"}) tx.Create(&TMG{Name: "汉字"}) if flag { return nil } else { return errors.New("有汉字") } }) }
备注
1)使用 Transaction 开启事务 2)内部需要使用func函数来写执行的sql 3)func函数需要有返回值,nil 返回则执行sql语句生成在数据库,errors不会生成数据库数据
2)事务的嵌套使用
func TestTransaction1(){ GLOBL_DB.AutoMigrate(&TMG{}) GLOBL_DB.Transaction(func(tx *gorm.DB) error { tx.Create(&TMG{Name: "一"}) tx.Create(&TMG{Name: "二"}) tx.Transaction(func(tx *gorm.DB) error { tx.Create(&TMG{Name: "三"}) return errors.New("有汉字") }) return nil }) }
3)Begin 开启事务,Commit提交事务,Rollback 回滚,提交的事务不会被执行
func TestTransaction2(){ GLOBL_DB.AutoMigrate(&TMG{}) tx := GLOBL_DB.Begin() tx.Create(&TMG{Name: "一"}) tx.Create(&TMG{Name: "aa"}) if true { // 回滚 tx.Rollback() } // 有了 Rollback。Commit已经没有用了 tx.Commit() }
4)SavePoint 与 RollbackTo的使用
func TestTransaction3(){ GLOBL_DB.AutoMigrate(&TMG{}) tx := GLOBL_DB.Begin() tx.Create(&TMG{Name: "一"}) tx.Create(&TMG{Name: "aa"}) // 忽略 SavePoint 到 RollbackTo 中间的代码 tx.SavePoint("duo") tx.Create(&TMG{Name: "bb"}) tx.RollbackTo("dop") tx.Commit() }
九、自定义数据类型
1)数据库存json。通过 value存到数据库,通过Scan从数据库解析出来
type CInfo struct { Name string Age int } func (c CInfo)Value() (driver.Value,error) { str,err := json.Marshal(c) if err != nil { return nil, err } return string(str),nil } func (c *CInfo)Scan(value interface{}) (error) { str,ok := value.([]byte) if !ok { return errors.New("不匹配的数据类型") } json.Unmarshal(str,c) return nil } type Cuser struct { ID int Info CInfo } func Make(){ GLOBL_DB.AutoMigrate(&Cuser{}) GLOBL_DB.Create(&Cuser{Info:CInfo{Name: "哈哈",Age: 22}}) } func Find() { var u Cuser GLOBL_DB.First(&u) fmt.Println(u) }
9.1) 字符串拼接

package main import ( "database/sql/driver" "errors" "strings" ) type CInfo struct { Name string Age int } type CUser struct { ID uint Info CInfo `gorm:"type:text"` Args Args } type Args []string //存储 1,2,3,4 func (self Args) Value() (driver.Value, error) { if len(self) > 0 { var str string = self[0] for _, v := range self[1:] { str += "," + v } return str, nil } else { return "", nil } } func (self *Args) Scan(value interface{}) error { str, ok := value.([]byte) if !ok { return errors.New("数据类型无法解析") } *self = strings.Split(string(str), ",") return nil } func main() { }
十、导入sql

package main import ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" "io/ioutil" "strings" ) var ( db_cmdb *gorm.DB ) func gormMysql(tableDb string) { connstr := fmt.Sprintf("root:123456@tcp(192.168.85.123:3306)/%s?charset=utf8mb4&parseTime=true", tableDb) mysqlConfig := mysql.Config{ DSN: connstr, // DSN data source name DefaultStringSize: 191, // string 类型字段的默认长度 SkipInitializeWithVersion: false, // 根据版本自动配置 } if db, err := gorm.Open(mysql.New(mysqlConfig), &gorm.Config{ SkipDefaultTransaction: false, DisableForeignKeyConstraintWhenMigrating: true, // 逻辑外键 }); err != nil { return } else { sqlDB, _ := db.DB() sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) db_cmdb = db } } } func readfile() { sqls, _ := ioutil.ReadFile("futong_cmp_cmdb.sql") sqlArr := strings.Split(string(sqls), ";") for _, sql := range sqlArr { sql = strings.TrimSpace(sql) if sql == "" { continue } err := db_cmdb.Exec(sql).Error if err != nil { fmt.Println(err) return } else { fmt.Println(sql, " success") } } } func main() { gormMysql("cmdb") readfile() }
查询
func (self *modelDataRepository) mysqlData(tableDB string, modelData common.ModelDataList) (schema []map[string]interface{}, count int64, err error) { db := func(db *gorm.DB) *gorm.DB { for key, value := range modelData.SearchMap { db.Where(fmt.Sprintf("%s LIKE ?", key), fmt.Sprintf("%%%s%%", value)) } return db } tx := global.MapDB[tableDB].Begin() tx.Scopes(db).Table(modelData.ModelName).Offset(int(modelData.Current - 1)).Limit(int(modelData.Size)).Find(&schema) tx.Scopes(db).Table(modelData.ModelName).Count(&count) return schema, count, err }
begin查询更新
tx := DB.Begin() tx = tx.Where("project_code = ?",config.ProjectCode) tx = tx.Where("environment = ?",config.Environment) tx = tx.Where("service = ?",config.Service) tx = tx.Where("filename = ?",config.Filename) tx.Model(model.KerriganConfig{}).Update("content",config.Content) tx.Commit()
十一、json字段内部查询,待验证测试
type Book struct { ID uint `gorm:"primary_key"` Title string `gorm:"size:100;not null"` Author string `gorm:"size:100;not null"` Metadata map[string]interface{} `gorm:"type:jsonb;not null"` } 在 tag 中,我们使用了 "type:jsonb",表示这是一个 json 字段。我们需要在查询时指定 json 中的 key,可以通过下面的方式来实现: db.Where("metadata->>'$.language'=?", "en").Find(&books) 上面的代码使用了 "->>" 操作符,表示从 json 中获取一个字符串值。其中,".language" 是 json 中的 key 名称,"en" 是需要匹配的字符串值。 ----------------------------------------------------------- 如果需要匹配的不是字符串,而是数字或布尔值,可以使用 "->" 操作符: db.Where("metadata->'$.year' BETWEEN ? and ?", 1950, 2000).Find(&books) 上面的代码可以查询出 metadata 中 "year" key 字段的值在 1950 到 2000 之间的 Book 记录。
json 字段
package resource import ( "fiy/common/models" "gorm.io/datatypes" ) /* @Author : lanyulei */ // 字段数据 type Data struct { Id int `gorm:"column:id; primary_key;AUTO_INCREMENT" json:"id"` // 字段分组ID Uuid string `gorm:"column:uuid; type:varchar(45); unique;" json:"uuid" binding:"required"` // 设备唯一ID InfoId int `gorm:"column:info_id; type:int(11); index;" json:"info_id" binding:"required"` // 对应的模型ID InfoName string `gorm:"column:info_name; type:varchar(128);" json:"info_name" binding:"required"` // 对应的模型名称 Status int `gorm:"column:status; type:int(11); default:1" json:"status"` // 0 没有状态,1 空闲,2 故障,3 待回收,4 正在使用 Data datatypes.JSON `gorm:"column:data; type:json" json:"data" binding:"required"` // 数据 models.BaseModel } func (Data) TableName() string { return "cmdb_resource_data" }
json字段已验证

package main import ( "database/sql/driver" "encoding/json" "fmt" "gorm.io/datatypes" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/schema" "time" ) type XTime struct { time.Time } // 1. 为 Xtime 重写 MarshaJSON 方法,在此方法中实现自定义格式的转换; func (t XTime) MarshalJSON() ([]byte, error) { output := fmt.Sprintf("\"%s\"", t.Format("2006-01-02 15:04:05")) return []byte(output), nil } // 2. 为 Xtime 实现 Value 方法,写入数据库时会调用该方法将自定义时间类型转换并写入数据库; func (t XTime) Value() (driver.Value, error) { var zeroTime time.Time if t.Time.UnixNano() == zeroTime.UnixNano() { return nil, nil } return t.Time, nil } // 3. 为 Xtime 实现 Scan 方法,读取数据库时会调用该方法将时间数据转换成自定义时间类型; func (t *XTime) Scan(v interface{}) error { value, ok := v.(time.Time) if ok { *t = XTime{Time: value} return nil } return fmt.Errorf("can not convert %v to timestamp", v) } type ServicePrototype struct { Id int `gorm:"column:id; primary_key;AUTO_INCREMENT;comment:唯一id" json:"id"` Name string `gorm:"column:name; type:varchar(128);unique;comment:名称" json:"name" v:"required"` UserName string `gorm:"column:username; type:varchar(128);comment:创建人" json:"username"` Data datatypes.JSON `gorm:"column:data; type:json" json:"data" binding:"required"` } var DB *gorm.DB func init() { if db, err := gorm.Open(mysql.New(mysql.Config{ DSN: "root:m9uSFL7duAVXfeAwGUSG@tcp(192.168.85.123:3306)/ft_cmdb?charset=utf8mb4&parseTime=True&loc=Local", DefaultStringSize: 171, }), &gorm.Config{ SkipDefaultTransaction: false, NamingStrategy: schema.NamingStrategy{ TablePrefix: "t_", //表明前缀 `User` 的表名 应该是 `t_users` SingularTable: false, // 使用单数表名,启用 表示 `t_user` }, DisableForeignKeyConstraintWhenMigrating: true, // 逻辑外键 }); err != nil { panic(err.Error()) } else { DB = db sqlDB, _ := DB.DB() sqlDB.SetMaxIdleConns(10) // 连接池最大的空闲连接数 sqlDB.SetMaxOpenConns(100) // 连接池最多容纳的链接数量 sqlDB.SetConnMaxLifetime(time.Hour) // 链接池中链接的最大可复用时间 } if err := DB.AutoMigrate(&ServicePrototype{}); err != nil { fmt.Println(err) } } func insert() { data := make(map[string]interface{}) data["age"] = 13 data["city"] = "上海" newdata, _ := json.Marshal(&data) servicePrototype := ServicePrototype{Name: "zhangsan1", UserName: "admin1", Data: newdata} DB.Create(&servicePrototype) } func find() { var servicePrototype ServicePrototype DB.Where("data->>'$.age'=?", 13).Find(&servicePrototype) fmt.Println(servicePrototype.Data) fmt.Println(servicePrototype.Name) } func main() { find() }
存储切片
var servicePrototype ServicePrototype DB.Where("name = ?", "zhangsan2").Where("data->>'$[0].age'=?", 13).Find(&servicePrototype)
切片查询
MySQL [ft_cmdb]> select * from t_service_prototypes where JSON_CONTAINS(data,JSON_OBJECT('age', 14)); +----+-----------+----------+----------------------------------------------------------------+ | id | name | username | data | +----+-----------+----------+----------------------------------------------------------------+ | 1 | zhangsan | admin | [{"age": 12, "city": "北京"}, {"age": 14, "city": "上海"}] | | 3 | zhangsan1 | admin1 | [{"age": 15, "city": "杭州"}, {"age": 14, "city": "重庆"}] | +----+-----------+----------+----------------------------------------------------------------+
对应的go代码
var servicePrototype []ServicePrototype DB.Where("JSON_CONTAINS(data, JSON_OBJECT('age', ?))", 14).Find(&servicePrototype)
设置表名描述

type User struct { gorm.Model Name string `gorm:"comment:用户姓名"` Age int `gorm:"comment:用户年龄"` Email string `gorm:"comment:用户邮箱"` } type Product struct { gorm.Model Name string `gorm:"comment:产品名称"` Price float64 `gorm:"comment:产品价格"` } func main() { db, err := gorm.Open("mysql", "your-database-connection-string") if err != nil { // 处理数据库连接错误 } defer db.Close() // 设置表描述信息 db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表'").AutoMigrate(&User{}) db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品信息表'").AutoMigrate(&Product{}) }
设置表名描述优化

import ( "gorm.io/gorm" "gorm.io/gorm/clause" ) type User struct { gorm.Model Name string `gorm:"comment:用户姓名"` Age int `gorm:"comment:用户年龄"` Email string `gorm:"comment:用户邮箱"` } type Product struct { gorm.Model Name string `gorm:"comment:产品名称"` Price float64 `gorm:"comment:产品价格"` } func main() { db, err := gorm.Open("mysql", "your-database-connection-string") if err != nil { // 处理数据库连接错误 } defer db.Close() // 定义表描述信息 userTableOptions := clause.TableOption{ Option: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表'", } productTableOptions := clause.TableOption{ Option: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品信息表'", } // 迁移模型并设置表描述信息 db.AutoMigrate(&User{}, &Product{}, &gorm.Model{}, &clause.Table{Name: "users", TableOption: userTableOptions}, &clause.Table{Name: "products", TableOption: productTableOptions}, ) }
表名优化
func (Order) TableOptions() string { return "engine=innodb comment='订单表'" } if t, ok := t.(database.TableOptions); ok { defTableOpts = t.TableOptions() } 循环执行 .Set("gorm:table_options", defTableOpts)
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步