📦 GORMv2

🏆 GORM

⭐️ GORM是一个使用 GO编写的 ORM框架,它让你的数据库里面的表结构变成代码定义的数据结构,从而做到,代码结构体即为数据结构,即为数据行为

如果学过 JavaWeb可以把它理解为 Mybatis

🌟 安装 GORM并连接数据库

同样也是使用 go get

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql //下载驱动

安装完毕之后就可以连接数据库了,GORM 官方支持的数据库类型有:MySQL, PostgreSQL, SQLite, SQL Server TiDB,这里我们使用 Mysql,它的连接方法和原生 sql库类似

package main

import (
	"fmt"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

func main() {
	database := "root:000000@tcp(localhost:3306)/gotest?charset=utf8mb4&parseTime=True&loc=Local"
	db,err := gorm.Open(mysql.Open(database),&gorm.Config{
        SkipDefaultTransaction: false, // 禁用默认事务
		NamingStrategy: schema.NamingStrategy{ // 命名策略
			TablePrefix:   "GORM", //默认创建表的前缀
			SingularTable: true,   // 禁用表名复数,如果是users表,则创建的表名就是user
		},
		DisableForeignKeyConstraintWhenMigrating: true, // 禁用外键约束
    })
	if err != nil {
	  fmt.Println("连接错误",err)
	}
	fmt.Println(name)
}

⭕️ 那么开始解读这段代码: 首先先是创建了一个字符串,这个连接格式就是 sql原生库的连接格式,建议在看看 GoWeb中的 sql原生库,如何使用 gorm.Open()连接数据库,可以看一下 Open的函数签名,它接接收一个 Dialector;类对象和多个 Option对象,是 gorm配置,它返回了一个DB对象和错误

func Open(dialector Dialector, opts ...Option) (db *DB, err error)

使用 mysql.Opent它接收一个字符串,用来连接数据库,它返回的就是一个 Dialector对象

func Open(dsn string) gorm.Dialector {
	dsnConf, _ := mysql.ParseDSN(dsn)
	return &Dialector{Config: &Config{DSN: dsn, DSNConfig: dsnConf}}
}

上面只是一种简单的连接方法,还有一种高级配置

func main() {
	database := "root:000000@tcp(localhost:3306)/gotest?charset=utf8mb4&parseTime=True&loc=Local"
	db,err := gorm.Open(mysql.New(mysql.Config{
		DSN: database, // DSN data source name
		DefaultStringSize: 256, // string 类型字段的默认长度
		DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
		DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
		DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
		SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
	}),&gorm.Config{
         SkipDefaultTransaction: false, // 禁用默认事务
		NamingStrategy: schema.NamingStrategy{ // 命名策略
			TablePrefix:   "GORM", //默认创建表的前缀
			SingularTable: true,   // 禁用表名复数,如果是users表,则创建的表名就是user
		},
		DisableForeignKeyConstraintWhenMigrating: true, // 禁用外键约束
    })
	if err != nil {
	  fmt.Println("连接错误",err)
	}
	fmt.Println(db)
}

使用的是 mysql.New它接收的是一个 config,用于配置连接时候的配置

// mysql.Config可配置项
type Config struct {
	DriverName string // 驱动名称,指定使用哪个数据库驱动。
	ServerVersion string // 服务器版本,用于指定连接的数据库服务器版本。
	DSN string // 数据源名称,包含了连接数据库所需的详细信息。
	DSNConfig *mysql.Config // 数据源配置,提供了一个配置对象,用于设置连接参数。
	Conn gorm.ConnPool // 连接池,用于管理数据库连接。
	SkipInitializeWithVersion bool // 是否跳过使用版本信息初始化。
	DefaultStringSize uint // 默认字符串大小,用于设置默认的字符串字段长度。
	DefaultDatetimePrecision *int // 默认日期时间精度,用于设置日期时间字段的精度。
	DisableWithReturning bool // 是否禁用 MySQL 8.0+ 的 WITH RETURNING 功能。
	DisableDatetimePrecision bool // 是否禁用日期时间字段的自动精度设置。
	DontSupportRenameIndex bool // 是否不支持重命名索引。
	DontSupportRenameColumn bool // 是否不支持重命名列。
	DontSupportForShareClause bool // 是否不支持 FOR SHARE 子句,用于行锁。
	DontSupportNullAsDefaultValue bool // 是否不支持将 NULL 作为默认值。
	DontSupportRenameColumnUnique bool // 是否不支持将重命名的列设置为唯一。
	// MySQL 8.0.19 版本开始,ALTER TABLE 允许使用更通用(且符合 SQL 标准)的语法
	// 来删除和修改任何类型的现有约束。
	// 详见 https://dev.mysql.com/doc/refman/8.0/en/alter-table.html 
	DontSupportDropConstraint bool // 是否不支持删除约束。
}

&gorm.Config{} 内配置 gorm的配置https://gorm.io/zh_CN/docs/gorm_config.html

type Config struct {
  SkipDefaultTransaction   bool
  NamingStrategy           schema.Namer
  Logger                   logger.Interface
  NowFunc                  func() time.Time
  DryRun                   bool
  PrepareStmt              bool
  DisableNestedTransaction bool
  AllowGlobalUpdate        bool
  DisableAutomaticPing     bool
  DisableForeignKeyConstraintWhenMigrating bool
}

🌟 创建数据表

🍵 AutoMigrate

🔝 在 Grom中使用 AutoMigrate来自动创建数据表,自动比较应用程序中的数据模型(通常是用代码定义的模型类或结构体,代表数据库表的结构)与数据库当前的实际 schema。如果发现两者之间存在差异,如模型中新增了一个字段而数据库表中没有,AutoMigrate 会自动执行必要的 SQL 命令来更新数据库 schema,确保数据库结构与你的代码模型相匹配。这样可以简化开发过程,避免手动编写和执行复杂的 ALTER TABLESQL 语句来调整数据库结构,

⭐️AutoMigrate 方法进行表结构迁移时,默认情况下它只会添加新的字段和必要的索引,而不会自动删除不再使用的字段

⭐️schema是数据库的结构描述,包括表、列、索引、约束等数据库对象的定义。它定义了数据库中数据的组织方式和规则。

AutoMigrate可以接收多个类型

// AutoMigrate run auto migration for given models
func (db *DB) AutoMigrate(dst ...interface{}) error {
	return db.Migrator().AutoMigrate(dst...)
}

⚠️ 注意: AutoMigrate 会创建表、缺失的外键、约束、列和索引。 如果大小、精度、是否为空可以更改,则 AutoMigrate 会改变列的类型。 出于保护您数据的目的,它 不会 删除未使用的列

// User 创建结构体对应表
type User struct {
	Name     string
	Password string
}

func main() {
	....

	//创建数据表
	err = db.AutoMigrate(&User{})
	if err!= nil {
		fmt.Println("创建数据表错误",err)
	}
    fmt.Println("创建数据表成功!!")
}

测试运行,进入数据库查看是否创建了数据表,由于之前设置了创建数据表前缀 gorm,所以创建出来的数据表为 gormuser

MariaDB [gorm]> show tables;
+----------------+
| Tables_in_gorm |
+----------------+
| gormuser       |
+----------------+
1 row in set (0.001 sec)

MariaDB [gorm]> desc gormuser;
+----------+--------------+------+-----+---------+-------+
| Field    | Type         | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| name     | varchar(256) | YES  |     | NULL    |       |
| password | varchar(256) | YES  |     | NULL    |       |
+----------+--------------+------+-----+---------+-------+
2 rows in set (0.014 sec)

🍵 Migrator接口

如果之前看过 AutoMigrate的源代码就知道,AutoMigrate其实就是调用的 Migrator,Migrator是一个对数据库内其他操作的接口,

// Migrator 接口提供了数据库结构迁移的相关方法,包括表、列、索引、视图和约束的创建、修改、删除等操作。

type Migrator interface {
    // AutoMigrate 自动检测并迁移模型结构到数据库,例如新增字段、修改字段类型等。
    AutoMigrate(dst ...interface{}) error

    // CurrentDatabase 返回当前操作的数据库名称。
    CurrentDatabase() string

    // FullDataTypeOf 根据字段元数据生成其在数据库中的完整数据类型表达式。
    FullDataTypeOf(field *schema.Field) clause.Expr

    // CreateTable 根据提供的模型结构创建数据库表。
    CreateTable(dst ...interface{}) error

    // DropTable 根据模型删除对应的数据库表。
    DropTable(dst ...interface{}) error

    // HasTable 判断指定模型对应的数据库表是否存在。
    HasTable(dst interface{}) bool

    // RenameTable 重命名数据库中的表。
    RenameTable(oldName, newName interface{}) error

    // GetTables 获取数据库中的所有表名列表。
    GetTables() (tableList []string, err error)

    // AddColumn 向指定表添加一个新的列。
    AddColumn(dst interface{}, field string) error

    // DropColumn 从指定表中删除一个列。
    DropColumn(dst interface{}, field string) error

    // AlterColumn 修改指定表中列的定义。
    AlterColumn(dst interface{}, field string) error

    // MigrateColumn 迁移指定列的数据类型或属性至新的定义。
    MigrateColumn(dst interface{}, field *schema.Field, columnType ColumnType) error

    // HasColumn 检查指定表中是否存在某列。
    HasColumn(dst interface{}, field string) bool

    // RenameColumn 重命名指定表中的列。
    RenameColumn(dst interface{}, oldName, newName string) error

    // ColumnTypes 获取指定表中各列的数据类型。
    ColumnTypes(dst interface{}) ([]ColumnType, error)

    // CreateView 创建数据库视图。
    CreateView(name string, option ViewOption) error

    // DropView 删除指定的数据库视图。
    DropView(name string) error

    // CreateConstraint 创建数据库表的约束条件。
    CreateConstraint(dst interface{}, name string) error

    // DropConstraint 删除数据库表的约束条件。
    DropConstraint(dst interface{}, name string) error

    // HasConstraint 检查数据库表中是否存在特定的约束条件。
    HasConstraint(dst interface{}, name string) bool

    // CreateIndex 为指定表创建索引。
    CreateIndex(dst interface{}, name string) error

    // DropIndex 删除指定表中的索引。
    DropIndex(dst interface{}, name string) error

    // HasIndex 判断指定表中是否存在某个索引。
    HasIndex(dst interface{}, name string) bool

    // RenameIndex 重命名指定表中的索引。
    RenameIndex(dst interface{}, oldName, newName string) error
}
1️⃣ 对表操作
	//使用迁移器
	dbMigrate := db.Migrator()

	//检查是否存在表,如果已经存在则删除,也就是删除表
	dbMigrate.DropTable(&User{})

	//创建数据表
	err = dbMigrate.AutoMigrate(&User{})
	if err != nil {
		fmt.Println("创建数据表错误", err)
	}
	fmt.Println("创建数据表成功!!")

	//检查对应类型User的数据表是不是存在
	if dbMigrate.HasTable(&User{}) {
		//或者dbMigrate.HasTable("user")
		fmt.Println("数据表存在")
	} else {
		fmt.Println("数据表不存在")
	}

	// 重命名表,这里也可以填两个类型
	dbMigrate.RenameTable(&User{}, "GORM_user")
	//如: db.Migrator().RenameTable(&User{}, &UserInfo{})
2️⃣ 对列操作
	// User 创建结构体对应表
    type User struct {
        Name     string
        Password string
        Email    string
    }


	//添加列,注意这里需要在结构体中添加email字段
	err = dbMigrate.AddColumn(&User{}, "Email")
	if err != nil {
		fmt.Println("添加列错误", err)
		return
	}

	//删除列
	err = dbMigrate.DropColumn(&User{}, "email")
	if err != nil {
		fmt.Println("删除列错误", err)
	}
	//修改列
	dbMigrate.AlterColumn(&User{}, "email")

	//检查是否存在
	column := dbMigrate.HasColumn(&User{}, "email")
	if column {
		fmt.Println("存在")
	} else {
		fmt.Println("不存在")
	}

	//字段重命名
	err = dbMigrate.RenameColumn(&User{}, "email", "email_address")
	if err != nil {
		fmt.Println("字段重命名错误", err)
	}

	//获取字段的类型
	types, err := db.Migrator().ColumnTypes(&User{})
	if err != nil {
		fmt.Println("获取字段类型错误", err)
	}
	for _, v := range types {
		fmt.Println(v.Name(), v.ScanType(), v.DatabaseTypeName())
	}