ORM

对象关系映射(Object Relational Mapping,ORM)。指的是对象和关系之间的映射,使用面向对象的 方式操作数据库。

关系模型和Go对象之间的映射
table => struct   ,表映射为结构体
row   => object   ,行映射为实例
column => property ,字段映射为属性

可以认为ORM是一种高级抽象,对象的操作最终还是会转换成对应关系数据库操作的SQL语句,数据库 操作的结构会被封装成对象。

GORM

GORM是一个友好的、功能全面的、性能不错的基于Go语言实现的ORM库。

GitHub:https://github.com/go-gorm/gorm

官方文档:https://gorm.io/zh_CN/docs/connecting_to_the_database.html

连接

 

package main

import (
    "fmt"
    "os"
    "time"
    "github.com/rs/zerolog/log"
    "gorm.io/driver/mysql" // 二次封装,底层依然依赖mysql驱动
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
)

var db0 *gorm.DB

func init() {
    var err error
    dsn := "gopher:123456@tcp(172.***:3306)/gopher?charset=utf8&parseTime=True&loc=Local"
    db0, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
        Logger: logger.Default.LogMode(logger.Info),
    })
    if err != nil {
        log.Err(err).Send()
    }
}

在"gorm.io/driver/mysql/mysql.go"中, import了 "github.com/go-sql-driver/mysql",也就是说驱动也导入了,Dialector的Initialize方法中使用了sql.Open

模型定义

GORM倾向于约定优于配置

  • 约定使用名为ID或Id的属性会作为主键
  • 约定使用snake_cases作为表名
  • 结构体命名为employee,那么数据库表名就是employees
  • 约定使用snake_case作为字段名,字段首字母大写采用大驼峰
  • 属性名为FirstName,默认对应数据库表的字段名为first_name

如果不遵从以上约定就要自定义配置

// 不符合约定的定义,很多都需要配置,直接用不行
type Emp struct { // 默认表名emps
 emp_no     int    // 不是ID为主键,需要配置
 first_name string // 首字母未大写,也需要配置
 last_name  string
 gender     byte
 birth_date string
}
// 符合约定的定义如下
type student struct { // 默认表名students
 ID   int    // Id也可以
 Name string // 字段首字母要大写
 Age  int
}
// 表明配置,表名并没有遵守约定
func (Emp) TableName() string {
return "employees"
}

使用 gorm:"primaryKey" 来指定字段为主键,默认使用名为ID的属性作为主键。primaryKey是tag名, 大小写不敏感,但建议小驼峰。如果未按照约定定义字段,需要定义结构体属性时指定数据库字段名称是什么。

type Emp struct {
    // 约定ID、Id为主键,整数自增
    Em_num    int       `gorm:"primaryKey;column:emp_no"` // 非约定,自定义
    Birth_day time.Time `gorm:"column:birth_date"`
    FirstName string // 首字母大写,对应字段first_name
    LastName  string
    Gender    uint8
    HireDate  time.Time
}

// 表名约定
func (Emp) TableName() string {
    return "employees"
}

迁移

下面是新建一个students表,结构体中属性类型和数据库表中字段类型的对应关系

// 迁移后,主键默认不为空,其他字段默认都是能为空的
type Student struct {
 ID       int       // 缺省主键bigint AUTO_INCREMENT
 Name     string    `gorm:"not null;type:varchar(48);comment:姓名"`
 Age      byte      // byte=>tinyint unsigned
 Birthday time.Time // datetime
 Gender   byte      `gorm:"type:tinyint"`
}
// db.Migrator().DropTable(&Student{})
db.Migrator().CreateTable(&Student{})
CREATE TABLE `students` (
    `id` bigint AUTO_INCREMENT,
    `name` varchar(48) NOT NULL COMMENT '姓名',
    `age` tinyint unsigned,
    `birthday` datetime(3) NULL,
    `gender` tinyint,
    PRIMARY KEY (`id`)
)

由于int => bigint、string => longtext,这些默认转换不符合我们的要求,所以,在tag中使用type指定 字段类型。 属性是用来构建结构体实例的,生成的SQL语句也要使用这些数据。而tag是用来生成迁移

Name     string     `gorm:"size:48"` 定义为varchar(48)
Age     int       `gorm:"size:32"` 定义为4字节的int
Age     int       `gorm:"size:64"` 定义为8字节的bigint

结构体属性类型用来封装实例的属性数据,Tag中类型指定迁移到数据库表中字段的类型。

type Student struct {
    Id        int       `gorm:"not null;autoIncrement"`
    Name      string    `gorm:"not null;type:varbinary(24);comment:姓名"`
    Age       uint8     `gorm:"not null;default 0"`
    Birth_day time.Time `gorm:"type time"`
    Address   string    `gorm:"size:255"`
}
func main() {
    // 打开日志文件
    fi, err := os.OpenFile("logs/info.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        log.Err(err).Msg("info日志文件打开失败")
    }
    li := logg.Olog(fi)
    defer fi.Close()
    fe, err := os.OpenFile("logs/error.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        log.Err(err).Msg("error日志文件打开失败")
    }
    defer fe.Close()
    le := logg.Olog(fe)

    // 创建表
    err = db0.Migrator().CreateTable(Student{})
    if err != nil {
        le.Err(err).Send()
    } else {
        msg := fmt.Sprintf("%v表创建成功", "students")
        li.Info().Msg(msg)
    }
}
示例

执行结果:

 

posted on 2023-07-24 21:39  自然洒脱  阅读(78)  评论(0编辑  收藏  举报