ORM教程 ---以GORM为例&go的数据库操作

GORM是Go语言中常用的ORM框架,它简化了数据库操作,提供了对象关系映射功能。本文从安装、连接数据库、模型定义、单表操作、查询、事务、一对多关系等方面全面解析GORM的使用,并展示了如何通过预加载、事务处理、自定义日志等高级特性来提高开发效率。此外,还讨论了GORM在处理SQL注入、性能优化和事务一致性等方面的优势和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考资料:
【GORM简明教程】关于GORM你看这一个就够了

【gorm文档】

【golang最简单的gorm教程】

【官方中文文档】

gorm v2中文文档

一对一、多对多、自定义类型的 目前懒得写 == 下次补上吧 https://docs.fengfengzhidao.com/#/docs/gorm%E6%96%87%E6%A1%A3/1.%E8%BF%9E%E6%8E%A5

介绍

其实官方中文文档已经很全了,但是顺序比较奇怪,所以按照【golang最简单的gorm教程】整理如下
在这里插入图片描述

什么是 ORM

ORM 是 Object Relational Mapping 的缩写,译为“对象关系映射”,它解决了对象和关系型数据库之间的数据交互问题。

简单说就是 使用一个类表示一张表,类中的属性表示表的字段,类的实例化对象表示一条记录,我们可以通过使用对象的方法操作数据库

特别是对强类型语言而言

demo-直观印象

orm操作

在这里插入图片描述
相当于sql语句的insert into
这样 整个编程就是面向对象的思维了。而且不要求程序员既会sql语句又会语言本身。很多程序员也不爱写sql语句
在这里插入图片描述

在进行下面的学习之前 我们先看看原生的方法有多么麻烦!

《golang 非orm数据库的操作与对比 database/sql、sqlx 和 sqlc》

本文后面的 原生方法对比orm 一节也有所展示

优缺点

和自动生成 SQL语句相比,手动编写 SQL语句的缺点是非常明显的,主要体现在以下几个方面:

  • 对象的属性名和数据表的字段名往往不一致,我们在编写 SQL语句时需要非常小心,要逐一核对属性名和字段名,确保它们不会出错,而且彼此之间要一一对应。
  • 此外,当SQL语句出错时,数把库的提示信息往往也不精准,这给排错带来了不小的困难
  • 不同的数据库,对应的 sql语句也不太一样
  • sql注入问题
  • orm可以提高开发效率

当然,使用 orm 也不全是优点,ORM 缺点:

  • ORM 增加了大家的学习成本,为了使用 ORM 技术,您至少需要掌握一种 ORM 框架
  • 自动生成 SQL语句会消耗计算资源,这势必会对程序性能造成一定的影响。
  • 对于复杂的教据障操作,ORM 通常难以处理,即使能处理,自动生成的SQL语句在性能方面也不如手写的原生 SQL。
  • 弱化sql能力

每一门语言都有对应的 ORM 柜架

python: SQLAlchemy DjangoORM
Java: Hibernate Mybatis
Golang: GORM

版本区别

新出了个V2版 部分和v1可能不兼容 比如v2版取消了close方法

插播–如何找资料

如果搜索引擎查不到 去github搜 然后里面会有介绍、官网之类的信息

连接

需要下载mysql的驱动

go get gorm.io/driver/mysql
go get gorm.io/gorm

简单连接

username := "root"  //账号
password := "root"  //密码
host := "127.0.0.1" //数据库地址,可以是Ip或者域名
port := 3306        //数据库端口
Dbname := "gorm"   //数据库名
timeout := "10s"    //连接超时,10秒

// root:root@tcp(127.0.0.1:3306)/gorm?
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, Dbname, timeout)
//连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
db, err := gorm.Open(mysql.Open(dsn))
if err != nil {
   
   
  panic("连接数据库失败, error=" + err.Error())
}
// 连接成功
fmt.Println(db)

高级配置

close

GORM决定在1.20版中取消Close()方法,因为GORM支持连接池,因此正确的用法是打开连接并在应用程序中共享它。

如果您的特定用例仍然需要使用Close()方法,GORM提供了返回db generic_interface的方法DB,您可以在其中使用它。

示例

sqlDB, err := db.DB()
// Close
defer sqlDB.Close()

跳过默认事务

为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,您可以在初始化时禁用它,这样可以获得60%的性能提升

db, err := gorm.Open(mysql.Open(dsn_str), &gorm.Config{
   
   
  SkipDefaultTransaction: true,
})

命名策略与自动建表

gorm采用的命名策略是,表名是蛇形复数,字段名是蛇形单数
结构体名 大驼峰->小蛇形复数
字段名 大驼峰->小蛇形单数

例如

type Student struct {
   
   
  Name      string
  Age       int
  MyStudent string
}

gorm会为我们这样生成表结构

CREATE TABLE `students` (`name` longtext,`age` bigint,`my_student` longtext)
CopyErrorOK!

我们也可以修改这些策略

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
   
   
  NamingStrategy: schema.NamingStrategy{
   
   
    TablePrefix:   "f_",  // 表名前缀
    SingularTable: false, // 单数表名
    NoLowerCase:   false, // 关闭小写转换
  },
})
//例如 
DB.AutoMigrate(&Student{
   
   })

相当于把结构体变成数据库中的表,如果数据库中不存在表就创建;如果存在就更新;同时还会自动调整约束索引之类
gorm帮你创建表的时候 比如UserInfo 会自动变成 user_infos 下划线连接和加一个s。你也可以自己设置规则。还会自动将id设置为主键
在这里插入图片描述

更详细的看后面的 自动生成表结构AutoMigrate 一节

显示日志

gorm的默认日志是只打印错误和慢SQL

我们可以自己设置

var mysqlLogger logger.Interface
// 要显示的日志等级
mysqlLogger = logger.Default.LogMode(logger.Info)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
   
   
  Logger: mysqlLogger,
})

如果你想自定义日志的显示

那么可以使用如下代码

newLogger := logger.New(
  log.New(os.Stdout, "\r\n", log.LstdFlags), // (日志输出的目标,前缀和日志包含的内容)
  logger.Config{
   
   
    SlowThreshold:             time.Second, // 慢 SQL 阈值
    LogLevel:                  logger.Info, // 日志级别
    IgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误
    Colorful:                  true,        // 使用彩色打印
  },
)

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
   
   
  Logger: newLogger,
})

部分展示日志

var model Student
session := DB.Session(&gorm.Session{
   
   Logger: newLogger})
session.First(&model)
// SELECT * FROM `students` ORDER BY `students`.`name` LIMIT 1

如果只想某些语句显示日志

DB.Debug().First(&model)

模型定义

模型是标准的 struct,由 Go 的基本数据类型、实现了 Scanner 和 Valuer 接口的自定义类型及其指针或别名组成

定义一张表

type Student struct {
   
   
  ID    uint // 默认使用ID作为主键
  Name  string
  Email *string // 使用指针是为了存空值
}

常识:小写属性是不会生成字段的

自动生成表结构AutoMigrate

结合连接高级配置那里看

// 可以放多个
DB.AutoMigrate(&Student{
   
   })

AutoMigrate的逻辑是只新增,不删除,不修改(大小会修改)

例如将Name修改为Name1,进行迁移,会多出一个name1的字段

生成的表结构如下

CREATE TABLE `f_students` (`id` bigint unsigned AUTO_INCREMENT,`name` longtext,`email` longtext,PRIMARY KEY (`id`))

默认的类型太大了

修改字段大小

我们可以使用gorm的标签进行修改

有两种方式

Name  string  `gorm:"type:varchar(12)"`
Name  string  `gorm:"size:2"`

字段标签

type 定义字段类型
size 定义字段大小
column 自定义列名
primaryKey 将列定义为主键
unique 将列定义为唯一键
default 定义列的默认值
not null 不可为空
embedded 嵌套字段
embeddedPrefix 嵌套字段前缀
comment 注释

多个标签之前用 ; 连接

type StudentInfo struct {
   
   
  Email  *string `gorm:"size:32"` // 使用指针是为了存空值
  Addr   string  `gorm:"column:y_addr;size:16"`
  Gender bool    `gorm:"default:true"`
}
type Student struct {
   
   
  Name string      `gorm:"type:varchar(12);not null;comment:用户名"`
  UUID string      `gorm:"primaryKey;unique;comment:主键"`
  Info StudentInfo `gorm:"embedded;embeddedPrefix:s_"`
}

// 建表语句
CREATE TABLE `students` (
    `name` varchar(12) NOT NULL COMMENT '用户名',
    `uuid` varchar(191) UNIQUE COMMENT '主键',
    `s_email` varchar(32),
    `s_y_addr` varchar(16),
    `s_gender` boolean DEFAULT true,
    PRIMARY KEY (`uuid`)
)

内置的默认model

在这里插入图片描述
模型定义示例

type User struct {
   
   
  gorm.Model
  Name         string
  Age          sql.NullInt64
  Birthday     *time.Time
  Email        string  `gorm:"type:varchar(100);unique_index"`
  Role         string  `gorm:"size:255"` // 设置字段大小为255
  MemberNumber *string `gorm:"unique;not null"` // 设置会员号(member number)唯一并且不为空
  Num          int     `gorm:"AUTO_INCREMENT"` // 设置 num 为自增类型
  Address      string  `gorm:"index:addr"` // 给address字段创建名为addr的索引
  IgnoreMe     int     `gorm:"-"` // 忽略本字段
}

在这里插入图片描述

单表操作

先使用gorm对单张表进行增删改查

表结构

type Student struct {
   
   
  ID     uint   `gorm:"size:3"`
  Name   string `gorm:"size:8"`
  Age    int    `gorm:"size:3"`
  Gender bool
  Email  *string `gorm:"size:32"`
}

添加记录

email := "[email protected]"
// 创建记录
student := Student{
   
   
  Name:   "枫枫",
  Age:    21,
  Gender: true,
  Email:  &email,
}
DB.Create(&student)

有两个地方需要注意

  • 指针类型是为了更好的存null类型,但是传值的时候,也记得传指针
  • Create接收的是一个指针,而不是值

由于我们传递的是一个指针,调用完Create之后,student这个对象上面就有该记录的信息了,如创建的id

DB.Create(&student)
fmt.Printf("%#v\n", student)  
// main.Student{ID:0x2, Name:"zhangsan", Age:23, Gender:false, Email:(*string)(0x11d40980)}

批量插入

Create方法还可以用于插入多条记录

var studentList []Student
for i := 0; i < 100; i++ {
   
   
  studentList = append(studentList, Student{
   
   
    Name:   fmt.Sprintf("机器人%d号", i+1),
    Age:    21,
    Gender: true,
    Email:  &email,
  })
}
DB.Create(&studentList)

查询单条记录Take、First、Last

不推荐使用find(sql同名方法)

如果你想避免ErrRecordNotFound错误(后文会详细讲),你可以使用Find,比如db.Limit(1).Find(&user),Find方法可以接受struct和slice的数据。

对单个对象使用Find而不带limit,db.Find(&user)将会查询整个表并且只返回第一个对象,这是性能不高并且不确定的。

感觉设计的不太好

简单示例

获取单条记录的方法很多,我们对比sql就很直观了

DB = DB.Session(&gorm.Session{
   
   Logger: Log})
var student Student
DB.Take(&student)  
// SELECT * FROM `students` LIMIT 1
DB.First(&student) 
// SELECT * FROM `students` ORDER BY `students`.`id` LIMIT 1
DB.Last(&student)  
// SELECT * FROM `students` ORDER BY `students`.`id` DESC LIMIT 1

根据主键查询

var student Student
DB.Take(&student, 2)
fmt.Println(student)

student = Student{
   
   } // 重新赋值
DB.Take(&student, "4")
fmt.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值