Go开源世界主流成熟ORM框架gorm实践分享

Posted itxiaoshen博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go开源世界主流成熟ORM框架gorm实践分享相关的知识,希望对你有一定的参考价值。

在GoLang世界中有很多优秀的ORM库,今天就介绍主流之一的GORM三方库,了解其核心功能,然后从其支撑数据类型模型和约定开始巩固其基础,接着以安装gorm库及其mysql驱动为基础编写常见CRUD代码示例,进入了解其事务、转换、分片等知识,最后演示其Json序列化示例。

@

概述

定义

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 中钩子方法
  • 支持 PreloadJoins 的预加载
  • 事务,嵌套事务,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 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAtUpdatedAt 字段追踪创建、更新时间。如果遵循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(&UserName: "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 = []UserName: "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.SessionCreateBatchSize: 1000)
users = [5000]UserName: "jinzhu", Pets: []Petpet1, 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(&UserName: "admin", Age: 40)


如果你想跳过Hooks方法,可以使用SkipHooks会话模式

DB.Session(&gorm.SessionSkipHooks: true).Create(&user)
DB.Session(&gorm.SessionSkipHooks: true).Create(&users)
DB.Session(&gorm.SessionSkipHooks: 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, []int1, 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.LockingStrength: "UPDATE").Find(&users)
// SELECT * FROM `users` FOR SHARE OF `users`
db.Clauses(clause.Locking
  Strength: "SHARE",
  Table: clause.TableName: 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(UserName: "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(&SessionDryRun: 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(&AnimalName: "Giraffe").Error; err != nil 
     tx.Rollback()
     return err
  

  if err := tx.Create(&AnimalName: "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.ConfigDSN: 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

从java到Go搭建Go的ORM框架Gorm

【提问】

如何使用Goland软件,搭建一个ORM框架GORM?

【解答】

具体步骤如下:

1、检查Go的安装

在任意目录执行如下命令:

go version

若有如下返回,则安装成功;如果报异常,则重新安装golang

go version go1.19.1 darwin/arm64

2、安装Gorm

在你的golang工程目录下,执行如下命令安装grom:

go get -u gorm.io/gorm

3、安装对应数据库的驱动

根据你的数据库,在任意目录执行如下命令安装驱动:

//mysql
go get -u gorm.io/driver/mysql
//sqlite
go get -u gorm.io/driver/sqlite
//sqlserver
go get -u gorm.io/driver/sqlserver
//clickhouse
go get -u gorm.io/driver/clickhouse

4、编写「数据库连接」代码

这里以连接mysql为例,在工程中创建用于数据库连接的go文件,其中dsn中内容需要根据自己数据库的信息进行替换,如下:

package db

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DB *gorm.DB

func InitGormDB() (err error) 
	dsn := "user:pwd@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config)
	if err != nil 
		fmt.Printf("数据库连接失败:%v\\n", err)
	 else 
		fmt.Printf("数据库连接成功\\n")
		DB = db
	
	return err


同时在main.go中增加对其的调用,在服务启动时就进行数据库连接:

package main

import (
	"fullstars/src/common/db"
)

func main() 

	db.InitGormDB()


启动服务后,可以看到连接成功的日志:

5、编写「数据Model」代码

数据库连接后,我们就对这个数据库进行一些表映射和简单查询操作。
首先,看一下我们需要操作的数据库表:

CREATE TABLE `config`  (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '类型',
  `key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `value` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `createtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `modifytime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleteflag` tinyint(3) NOT NULL DEFAULT 0 COMMENT '逻辑删除标识',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

然后在工程中,新建Model文件:configModel.go,如下:

type Config struct 
	ID         int
	TYPE       string
	KEY        string
	VALUE      string
	CREATETIME time.Time
	MODIFYTIME time.Time
	DELETEFLAG int


func (Config) TableName() string 
	return "config"

需要说明的,gorm中的Model应与数据表一一对应,gorm会进行自动转换映射。

6、编写「数据查询」代码

在工程中,新建文件:configService.go,用于操作数据库和逻辑处理,如下:
其中「.Debug()」是为了在日志中打印Gorm真实拿去数据库执行的sql,方便我们查看和核对。

package config

import (
	"fullstars/src/common/db"
)

func ConfigService() 
	var configs []Config
	db.DB.Debug().Find(&configs)

	var config Config
	db.DB.Debug().Where("id = ?", 2).Find(&config)


同样在main.go中增加对其的调用:

package main

import (
	"fullstars/src/common/db"
)

func main() 
	db.InitGormDB()
	config.ConfigService()


调试启动后,我们可以看一下变量的结果,数据已经被查询出来了:

以上是关于Go开源世界主流成熟ORM框架gorm实践分享的主要内容,如果未能解决你的问题,请参考以下文章

012-Go ORM框架之Gorm测试

从java到Go搭建Go的ORM框架Gorm

从java到Go搭建Go的ORM框架Gorm

从java到Go搭建Go的ORM框架Gorm

从java到Go搭建Go的ORM框架Gorm

Go语言使用gorm对MySQL进行性能测试