Go开源世界主流成熟ORM框架gorm实践分享
@
概述
定义
GORM 官网地址 https://gorm.io/ 最新版本v1.25.1
GORM 官网文档地址 https://gorm.io/docs/
GORM 源码地址 https://github.com/go-gorm/gorm
GORM 是Golang语言中一个功能齐全的优秀的ORM 框架,对开发者友好,支持多种数据库,并提供了丰富的功能和 API,可以让开发者更加方便地进行数据库操作。
核心功能
- ORM功能丰富、完整
- 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
- Create,Save,Update,Delete,Find 中钩子方法
- 支持 Preload、Joins的预加载
- 事务,嵌套事务,Save Point,Rollback To Saved Point
- Context、预编译模式、DryRun 模式
- 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
- SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
- 复合主键,索引,约束
- Auto Migration
- 自定义 Logger
- 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
- 每个特性都经过了测试的重重考验
- 开发者友好
声明模型与约定
模型是标准的 struct,由 Go 的基本数据类型、实现了 Scanner和 Valuer接口的自定义类型及其指针或别名组成,示例如:
type User struct {
  ID           uint
  Name         string
  Email        *string
  Age          uint8
  Birthday     *time.Time
  MemberNumber sql.NullString
  ActivatedAt  sql.NullTime
  CreatedAt    time.Time
  UpdatedAt    time.Time
}
GORM 倾向于约定优于配置 默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间。如果遵循GORM采用的约定,则只需编写很少的配置/代码。如果约定不符合需求,GORM也允许指定配置。
gorm.Model
GORM定义了gorm.Model,其中包括字段ID, CreatedAt, UpdatedAt, DeletedAt,可以将其嵌入到结构中以包含这些字段

字段级权限
可导出的字段在使用 GORM 进行 CRUD 时拥有全部的权限,此外,GORM 允许您用标签控制字段级别的权限。这样就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略
type User struct {
  Name string `gorm:"<-:create"` // 允许读和创建
  Name string `gorm:"<-:update"` // 允许读和更新
  Name string `gorm:"<-"`        // 允许读和写(创建和更新)
  Name string `gorm:"<-:false"`  // 允许读,禁止写
  Name string `gorm:"->"`        // 只读(除非有自定义配置,否则禁止写)
  Name string `gorm:"->;<-:create"` // 允许读和写
  Name string `gorm:"->:false;<-:create"` // 仅创建(禁止从 db 读)
  Name string `gorm:"-"`  // 通过 struct 读写会忽略该字段
  Name string `gorm:"-:all"`        // 通过 struct 读写、迁移会忽略该字段
  Name string `gorm:"-:migration"`  // 通过 struct 迁移会忽略该字段
}
时间惯例
GORM按惯例使用CreatedAt、UpdatedAt来跟踪创建/更新时间,如果定义了字段,GORM将在创建/更新时设置当前时间。要使用具有不同名称的字段,可以使用标签autoCreateTime、autoUpdateTime来配置这些字段。如果希望节省存储不采用时间格式改为采用UNIX(毫/纳)秒,可以简单地更改字段的数据类型。
type User struct {
  CreatedAt time.Time // 如果创建时为零,则设置为当前时间
  UpdatedAt int       // 在更新时设置为当前unix秒数,或者在创建时设置为零
  Updated   int64 `gorm:"autoUpdateTime:nano"` // 使用unix纳秒作为更新时间
  Updated   int64 `gorm:"autoUpdateTime:milli"`// 使用unix毫秒作为更新时间
  Created   int64 `gorm:"autoCreateTime"`      // 使用unix seconds作为创建时间
}
嵌入结构
例如将Go内置gorm.Model结构体嵌入到User结构体里
type User struct {
  gorm.Model
  Name string
}
// 这个定义等价于上面
type User struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
  Name string
}
但对于普通的struct字段,可以将其嵌入标签,例如:
type Author struct {
  Name  string
  Email string
}
type Blog struct {
  ID      int
  Author  Author `gorm:"embedded"`
  Upvotes int32
}
// 这个定义等价于上面Blog内嵌Author
type Blog struct {
  ID    int64
  Name  string
  Email string
  Upvotes  int32
}
可以使用标签embeddedPrefix为嵌入字段的数据库名称添加前缀,例如:
type Blog struct {
  ID      int
  Author  Author `gorm:"embedded;embeddedPrefix:author_"`
  Upvotes int32
}
// 这个定义等价于上面Blog内嵌Author
type Blog struct {
  ID          int64
  AuthorName  string
  AuthorEmail string
  Upvotes     int32
}
字段标签
| Tag Name | Description | 
|---|---|
| column | 数据库表列名 | 
| type | 列数据类型,例如:bool, int, uint, float, string, time, bytes,这适用于所有数据库,并且可以与其他标签一起使用,如' not null ', ' size ', ' autoIncrement '…指定的数据库数据类型,如' varbinary(8) '也支持,当使用指定的数据库数据类型时,它需要是一个完整的数据库数据类型,例如: MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT | 
| serializer | 指定如何将数据序列化和反序列化到db的序列化器, e.g: serializer:json/gob/unixtime | 
| size | 指定列数据大小/长度, e.g: size:256 | 
| primaryKey | 指定列为主键 | 
| unique | 将列指定为唯一约束 | 
| default | 指定列默认值 | 
| precision | 指定列精度 | 
| scale | 指定列的比例 | 
| not null | 指定列为NOT NULL | 
| autoIncrement | 指定列可自动递增 | 
| autoIncrementIncrement | 自动递增步长,控制连续列值之间的间隔 | 
| embedded | 嵌入字段 | 
| embeddedPrefix | 嵌入字段的列名前缀 | 
| autoCreateTime | 创建时跟踪当前时间,对于' int '字段,它将跟踪Unix秒,使用值' nano ' / ' milli '来跟踪Unix纳米/毫秒, e.g: autoCreateTime:nano | 
| autoUpdateTime | 在创建/更新时跟踪当前时间,对于' int '字段,它将跟踪Unix秒,使用值' nano ' / ' milli '来跟踪Unix纳米/毫秒, e.g: autoUpdateTime:milli | 
| index | 对多个字段使用相同的名称创建复合索引 | 
| uniqueIndex | 与' index '相同,但创建唯一的索引 | 
| check | 创建检查约束, eg: check:age > 13 | 
| <- | 设置字段的写权限,' <-:create ' create-only字段,' <-:update ' update-only字段,' <-:false '无写权限,' <- '创建和更新权限 | 
| -> | 设置字段的读权限,' ->:false '没有读权限 | 
| - | 忽略该字段,' - '无读写权限,' -:migration '无迁移权限,' -:all '无读写迁移权限 | 
| comment | 在迁移时为字段添加注释 | 
使用
安装
# 引入gorm
go get -u gorm.io/gorm
# 引入mySQL驱动
go get -u gorm.io/driver/mysql
# 引入sqlite驱动
go get -u gorm.io/driver/sqlite
数据库链接
import (
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)
func main() {
  dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
MySQL Driver提供了一些可以在初始化时使用的高级配置,例如:
db, err := gorm.Open(mysql.New(mysql.Config{
  DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // data source name
  DefaultStringSize: 256, // default size for string fields
  DisableDatetimePrecision: true, // disable datetime precision, which not supported before MySQL 5.6
  DontSupportRenameIndex: true, // drop & create when rename index, rename index not supported before MySQL 5.7, MariaDB
  DontSupportRenameColumn: true, // `change` when rename column, rename column not supported before MySQL 8, MariaDB
  SkipInitializeWithVersion: false, // auto configure based on currently MySQL version
}), &gorm.Config{})
连接池
GORM使用数据库/sql维护连接池
sqlDB, err := db.DB()// SetMaxIdleConns设置空闲连接池中的最大连接数。sqlDB.SetMaxIdleConns(10)// SetMaxOpenConns设置数据库的最大打开连接数。sqlDB.SetMaxOpenConns(100)// SetConnMaxLifetime设置连接可能被重用的最大时间。sqlDB.SetConnMaxLifetime(time.Hour)
CRUD 接口
创建
package main
import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)
type User struct {
	gorm.Model
	Name string
	Age  int8
}
func main() {
	dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println("data error")
	}
	db.AutoMigrate(&User{})
	// 单个插入创建
	db.Create(&User{Name: "zhangsan", Age: 20})
	users := []User{
		{Name: "lisi", Age: 25},
		{Name: "wangwu", Age: 26},
	}
	// 多个插入并演示返回值
	result := db.Create(users)
	if result != nil {
		fmt.Println(result.RowsAffected)
		fmt.Println(result.Error)
	}
	var user User
    // 根据主键ID查询主键值为1的记录并返回数据
	db.First(&user, 1)
	fmt.Println(user)
}

MySQL的test数据库及对应表users信息如下:

// 指定批处理大小
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
db.CreateInBatches(users, 100)
// 初始化GORM时使用CreateBatchSize选项,所有INSERT在创建记录和关联时都将遵循此选项
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  CreateBatchSize: 1000,
})
db := db.Session(&gorm.Session{CreateBatchSize: 1000})
users = [5000]User{{Name: "jinzhu", Pets: []Pet{pet1, pet2, pet3}}...}
db.Create(&users)
钩子函数示例
package main
import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)
type User struct {
	gorm.Model
	Name string
	Age  int8
}
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
	if u.Name == "admin" {
		fmt.Println("before create hint admin")
	}
	return
}
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
	if u.Name == "admin" {
		fmt.Println("after create hint admin")
	}
	return
}
func (u *User) BeforeSave(tx *gorm.DB) (err error) {
	if u.Name == "admin" {
		fmt.Println("before save hint admin")
	}
	return
}
func (u *User) AfterSave(tx *gorm.DB) (err error) {
	if u.Name == "admin" {
		fmt.Println("after save hint admin")
	}
	return
}
func main() {
	dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println("data error")
	}
	db.Create(&User{Name: "admin", Age: 40})
}

如果你想跳过Hooks方法,可以使用SkipHooks会话模式
DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)
DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)
DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)
查询
GORM提供了First、Take、Last方法来从数据库中检索单个对象,它在查询数据库时添加了LIMIT 1条件,如果没有找到记录,它将返回错误ErrRecordNotFound。
First和Last方法将按主键顺序分别查找第一个和最后一个记录。只有当指向目标结构的指针作为参数传递给方法时,或者使用db.Model()指定模型时,它们才有效。此外,如果没有为相关模型定义主键,则模型将按第一个字段排序。
package main
import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)
type User struct {
	gorm.Model
	Name string
	Age  int8
}
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
	if u.Name == "admin" {
		fmt.Println("before create hint admin")
	}
	return
}
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
	if u.Name == "admin" {
		fmt.Println("after create hint admin")
	}
	return
}
func (u *User) BeforeSave(tx *gorm.DB) (err error) {
	if u.Name == "admin" {
		fmt.Println("before save hint admin")
	}
	return
}
func (u *User) AfterSave(tx *gorm.DB) (err error) {
	if u.Name == "admin" {
		fmt.Println("after save hint admin")
	}
	return
}
func main() {
	dsn := "root:123456@tcp(192.168.50.95:3306)/test?charset=utf8&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println("data error")
	}
	var user, user1, user2, user3 User
	// 获取一条记录,下面相当于SELECT * FROM users ORDER BY id LIMIT 1;
	db.First(&user)
	fmt.Println("user=", user)
	// 获取一条记录, 下面相当于SELECT * FROM users LIMIT 1;
	db.Take(&user1)
	fmt.Println("user1=", user1)
	// 获取一条记录, 下面相当于SELECT * FROM users ORDER BY id DESC LIMIT 1;
	db.Last(&user2)
	fmt.Println(user2)
	// 指定表获取数据放入map
	result := map[string]interface{}{}
	db.Table("users").Take(&result)
	fmt.Println("result=", result)
	db.First(&user3, "id = ?", 3)
	fmt.Println("user3=", user3)
	var users, users1 []User
	db.Find(&users, []int{1, 2, 3})
	fmt.Println("users=", users)
	// 查询指定字段和条件
	db.Select("name").Where("id > ?", 2).Find(&users1)
	fmt.Println("users1=", users1)
}

其他详细查看官网
// limit ,SELECT * FROM users LIMIT 3;
db.Limit(3).Find(&users)
// OFFSET ,SELECT * FROM users OFFSET 3;
db.Offset(3).Find(&users)
// group by ,SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
// distinct
db.Distinct("name", "age").Order("name, age desc").Find(&results)
// join ,SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
// scan,将结果扫描到结构体中的工作方式类似于我们使用Find的方式
var result Result
db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)
// 原始SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)
高级查询
- 智能选择字段:GORM允许通过Select选择特定的字段,如果你经常在你的应用程序中使用这个,也许你想为API定义一个更小的结构体,它可以自动选择特定的字段。
type User struct {
  ID     uint
  Name   string
  Age    int
  Gender string
  // hundreds of fields
}
type APIUser struct {
  ID   uint
  Name string
}
// 查询时自动选择“id”、“name”,SELECT `id`, `name` FROM `users` LIMIT 10
db.Model(&User{}).Limit(10).Find(&APIUser{})
- 锁:GORM支持不同类型的锁。
// SELECT * FROM `users` FOR UPDATE
db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users)
// SELECT * FROM `users` FOR SHARE OF `users`
db.Clauses(clause.Locking{
  Strength: "SHARE",
  Table: clause.Table{Name: clause.CurrentTable},
}).Find(&users)
// SELECT * FROM `users` FOR UPDATE NOWAIT
db.Clauses(clause.Locking{
  Strength: "UPDATE",
  Options: "NOWAIT",
}).Find(&users)
- SubQuery:子查询可以嵌套在查询中,使用* GORM . db对象作为参数时,GORM可以生成子查询。
// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)
// SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%")
subQuery := db.Select("AVG(age)").Where("name LIKE ?", "name%").Table("users")
db.Select("AVG(age) as avgage").Group("name").Having("AVG(age) > (?)", subQuery).Find(&results)
- COUNT:获取匹配记录计数。
// SELECT count(1) FROM users WHERE name = 'jinzhu'; (count)
db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
修改
- 保存所有字段
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
- 更新字段
db.First(&user, "age = ?", 25)
db.Model(&user).Updates(User{Name: "lisinew1", Age: 44})
db.Model(&user).Updates(map[string]interface{}{"Name": "lisinew2", "Age": 35})
db.Model(&user).Update("age", 30)
删除
如果您的模型包含一个gorm。DeletedAt字段(包含在gorm.Model中),它将自动获得软删除能力!
// GORM允许使用带有内联条件的主键删除对象
db.Delete(&user, 1)
// 假如Email's ID is `10`,DELETE from emails where id = 10;
db.Delete(&email)
// 带附加条件删除
db.Where("name = ?", "jinzhu").Delete(&email)
// 永久删除
db.Unscoped().Delete(&order)
原始SQL
- 使用Scan查询Raw SQL
type Result struct {
  ID   int
  Name string
  Age  int
}
var result Result
db.Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Scan(&result)
var age int
db.Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Scan(&age)
var users []User
db.Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Scan(&users)
- 执行原始sql
// 执行删除表数据
db.Exec("DROP TABLE users")
// 执行带有表达式的更新
db.Exec("UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")
- DryRun模式
// Session Configuration
type Session struct {
  DryRun                   bool
  PrepareStmt              bool
  NewDB                    bool
  Initialized              bool
  SkipHooks                bool
  SkipDefaultTransaction   bool
  DisableNestedTransaction bool
  AllowGlobalUpdate        bool
  FullSaveAssociations     bool
  QueryFields              bool
  Context                  context.Context
  Logger                   logger.Interface
  NowFunc                  func() time.Time
  CreateBatchSize          int
}
// session mode
stmt := db.Session(&Session{DryRun: true}).First(&user, 1).Statement
println(stmt.SQL.String())
println(stmt.Vars)

- Row & Rows
// row
row := db.Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row()
row.Scan(&name, &age)
row := db.Raw("select name, age, email from users where name = ?", "jinzhu").Row()
row.Scan(&name, &age, &email)
// rows
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows()
defer rows.Close()
for rows.Next() {
  rows.Scan(&name, &age, &email)
  // do something
}
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows()
defer rows.Close()
for rows.Next() {
  rows.Scan(&name, &age, &email)
  // do something
}
事务
GORM在事务内部执行写(创建/更新/删除)操作以确保数据一致性,如果不需要,可以在初始化时禁用它,之后将获得大约30%以上的性能提升。流程如下:
func CreateAnimals(db *gorm.DB) error {
  // 注意,在事务中使用tx作为数据库句柄
  tx := db.Begin()
  defer func() {
    if r := recover(); r != nil {
      tx.Rollback()
    }
  }()
  if err := tx.Error; err != nil {
    return err
  }
  if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
     tx.Rollback()
     return err
  }
  if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
     tx.Rollback()
     return err
  }
  return tx.Commit().Error
}
转换
- ID为主键:默认情况下,GORM使用名称为ID的字段作为表的主键。可以通过实现Tabler接口来更改默认表名,例如:
// 可以使用标记primaryKey将其他字段设置为主键
type Animal struct {
  ID     int64
  UUID   string `gorm:"primaryKey"`
  Name   string
  Age    int64
}
- 复数表名:GORM将结构名复数化为snake_cases作为表名,对于结构User,它的表名按照约定是users
// 转换表名为my_users
func (User) TableName() string {
	return "my_users"
}
- GORM允许用户通过覆盖默认的NamingStrategy来更改默认的命名约定,该策略用于构建TableName, ColumnName, JoinTableName, RelationshipFKName, CheckerName, IndexName
type Animal struct {
  AnimalID int64     `gorm:"column:beast_id"`         // 将列名设置为 `beast_id`
  Birthday time.Time `gorm:"column:day_of_the_beast"` // 将列名设置为 `day_of_the_beast`
  Age      int64     `gorm:"column:age_of_the_beast"` // 将列名设置为 `age_of_the_beast`
}
分片
Sharding 是一个高性能的 Gorm 分表中间件。它基于 Conn 层做 SQL 拦截、AST 解析、分表路由、自增主键填充,带来的额外开销极小。对开发者友好、透明,使用上与普通 SQL、Gorm 查询无差别,只需要额外注意一下分表键条件。 为您提供高性能的数据库访问。
功能特点
- 非侵入式设计, 加载插件,指定配置,既可实现分表。
- 轻快, 非基于网络层的中间件,像 Go 一样快
- 支持多种数据库。 PostgreSQL 已通过测试,MySQL 和 SQLite 也在路上。
- 多种主键生成方式支持(Snowflake, PostgreSQL Sequence, 以及自定义支持)Snowflake 支持从主键中确定分表键。
配置分片中间件,注册想要分片的表
import (
  "fmt"
  "gorm.io/driver/postgres"
  "gorm.io/gorm"
  "gorm.io/sharding"
)
dsn := "postgres://localhost:5432/sharding-db?sslmode=disable"
db, err := gorm.Open(postgres.New(postgres.Config{DSN: dsn}))
db.Use(sharding.Register(sharding.Config{
    ShardingKey:         "user_id",
    NumberOfShards:      64,
    PrimaryKeyGenerator: sharding.PKSnowflake,
}, "orders").Register(sharding.Config{
    ShardingKey:         "user_id",
    NumberOfShards:      256,
    PrimaryKeyGenerator: sharding.PKSnowflake,
    // 对于show up give notifications, audit_logs表使用相同的分片规则。
}, Notification{}, AuditLog{}))
序列化
序列化器是一个可扩展的接口,允许自定义如何使用databasae序列化和反序列化数据。
package main
import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)
type Profile struct {
	Email  string `json:"email"`
	Mobile string `json:"mobile"`
}
type User struct {
	gorm.Model
	Name    string  `json:"name"`
	Age     int8    `json:"age"`
	Profile Profile `json:"profile" gorm:"type:json;comment:'个人信息'"`
}
// 转换表名
func (User) TableName() string {
	return "new_users"
}
// Value 存储数据的时候转换为字符串
func (t Profile) Value() (driver.Value, error) {
	return json.Marshal(t)
}
// Scan 读取数据的时候转换为json
func (t *Profile) Scan(value interface{}) error {
	return json.Unmarshal(value.([]byte), &t)
}
func main() {
	dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println("data error")
	}
	db.AutoMigrate(&User{})
	user := User{
		Name: "刘海",
		Age:  23,
		Profile: Profile{
			Email:  "test1@qq.com",
			Mobile: "18822334455",
		},
	}
	db.Create(&user)
	var user1 User
	db.Debug().Where("profile->'$.mobile'=(?)", "18822334455").First(&user1)
	fmt.Println(user1)
}

查看mysql已有新创建的new_users数据库和对应的数据

- 本人博客网站IT小神 www.itxiaoshen.com
Go开源世界主流成熟ORM框架gorm实践分享的更多相关文章
- 目前的.NET(C#)世界里,主流的ORM框架
		推荐一些常用的asp.net ORM框架 SqlSugar (国内) Dos.ORM (国内) Chloe (国内) StackExchange/Dapper (国外) Entity Framewor ... 
- .NET(C#)主流的ORM框架
		.NET(C#)主流ORM总揽 SqlSugar (国内) Dos.ORM (国内) Chloe (国内) StackExchange/Dapper (国外) Entity Framework (EF ... 
- .NET(C#)有哪些主流的ORM框架,SqlSugar,Dapper,EF还是...
		前言 在以前的一篇文章中,为大家分享了<什么是ORM?为什么用ORM?浅析ORM的使用及利弊>.那么,在目前的.NET(C#)的世界里,有哪些主流的ORM,SqlSugar,Dapper, ... 
- Golang 入门系列(十二)ORM框架gorm
		之前在已经介绍了用的github.com/go-sql-driver/mysql 访问数据库,不太了解的可以看看之前的文章 https://www.cnblogs.com/zhangweizhong/ ... 
- Go ORM框架 - GORM 踩坑指南
		今天聊聊目前业界使用比较多的 ORM 框架:GORM.GORM 相关的文档原作者已经写得非常的详细,具体可以看这里,这一篇主要做一些 GORM 使用过程中关键功能的介绍,GORM 约定的一些配置信息说 ... 
- Node.js ORM 框架 sequelize 实践
		最近在做团队的一个内部系统,这次使用的nodejs web框架是团队统一的hapi.js,而数据库依然是mysql,ORM 框架选用有着6000+ stars 的 sequelize.js,hapi- ... 
- Go orm框架gorm学习
		之前咱们学习过原生的Go连接MYSQL的方法,使用Go自带的"database/sql"数据库连接api,"github.com/go-sql-driver/mysql& ... 
- 《Andorid开源》greenDao 数据库orm框架
		一 前言:以前没用框架写Andorid的Sqlite的时候就是用SQLiteDatabase ,SQLiteOpenHelper ,SQL语句等一些东西,特别在写SQL语句来进行 数据库操作的时 ... 
- Chloe.ORM框架应用实践
		Chloe.ORM 是国人开发的一款数据库访问组件,很是简单易用.目前支持四种主流数据库:SqlServer.MySQL.Oracle,以及Sqlite,作者为这四种数据库划分出了各自对应的组件程序集 ... 
- [Android]Android端ORM框架——RapidORM(v1.0)
		以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4748077.html Android上主流的ORM框架有很多 ... 
随机推荐
- What is UDS Service 0x10 - Diagnostic Session Control ?
			Why need the UDS Service 0x10? ECU在正常工作时会处于某一个会话模式下,上电后会自动进入默认会话模式,所以ECU启动后我们不需要输入0x10 01来进入该会话模式.EC ... 
- Python查找存储区0KB文件并记录下地址
			查找存储区域中0KB大小文件,可以根据需要变更指定大小. #-*- coding: utf-8 -*- #!/usr/bin/python from os.path import isdir,absp ... 
- 【转载】谈谈GIS三维渲染引擎
			> 原文地址:https://zhuanlan.zhihu.com/p/419667971 三维引擎 minemap: 是我们公司的产品,主要以earth的形态展示,支持矢量切片+倾斜数据(这一 ... 
- 基于深度学习的农作物叶片病害检测系统(UI界面+YOLOv5+训练数据集)
			摘要:农作物叶片病害检测系统用于智能检测常见农作物叶片病害情况,自动化标注.记录和保存病害位置和类型,辅助作物病害防治以增加产值.本文详细介绍基于YOLOv5深度学习模型的农作物叶片病害检测系统,在介 ... 
- Cesium案例(六) Time  Dynamic Wheels
			Cesium.Ion.defaultAccessToken = "token"; const viewer = new Cesium.Viewer( ... 
- group_concat_max_len设置过小致group_concat()排列不全
			问题描述:group_concat_max_len默认值1024导致group_concat函数排列显示不足 group_concat_max_len长度显示跟group_concat排列长度息息相关 ... 
- 使用Go语言操作HDFS
			HDFS(Hadoop分布式文件系统)是Hadoop生态系统的一部分,它是一个可扩展的分布式文件系统,被设计用于在大规模数据集上运行的应用程序 安装相关package: $ go get github ... 
- 基于DotNetCoreNPOI封装特性通用导出excel
			基于DotNetCoreNPOI封装特性通用导出excel 目前根据项目中的要求,支持列名定义,列索引排序,行合并单元格,EXCEL单元格的格式也是随着数据的类型做对应的调整. 效果图: 调用方式 可 ... 
- 通过重构来加深理解——DDD
			上部分模型驱动设计的构造块为维护模型和实现之间的关系打下了基础.在开发过程中使用一系列成熟的基本构造块并运用一致的语言,能够使开发工作更加清晰而有条理. 我们面临的真正挑战是找到深层次的模型,这个模型 ... 
- C# 自定义并动态切换光标
			系统有很多光标类型 :Cursors 类 (System.Windows.Input) | Microsoft Docs 本章介绍如何自定义光标.并动态切换光标类型. 动态切换光标类型 以白板书写为例 ... 
