Golang GORM 初始化/增、删、改、查

GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.

什么是ORM


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

  • Gorm 是 Go 语言中实现对象和数据库映射的框架,可以有效地提高开发数据库应用的效率。
  • Gorm 主要用途是把 struct类型和数据库表 进行映射,使用简单方便。因此,使用 Gorm 操作数据库的时候不需要直接手写 SQL 语句代码

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

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

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

  • ORM增加了大家的学习成本,为了使用ORM技术,您至少需要掌握一种ORM框架。
  • 自送生成SQL语句会消耗计算资源,这势必会对程序性能造成一定的影响。
  • 对于复杂的数据库操作,ORM通常难以处理,即使能处理,自动生成的SQL语句在性能方面也不如手写的原生SQL。
  • 生成SQL语句的过程是自动进行的,不能人工干预,这使得开发人员无法定制一些特殊的SQL语句。

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

  • python: SQLAlchemy  DjangoORM
  • Java: Hibernate  Mybatis
  • Golang:GORM

GORM入门


每个语言提供的orm是一个作用,就是将你的对象和数据库表建立映射关系。(比如一张表在go里面对应的可能是一个结构体,在对数据库做增删改查的时候是对这些结构体进行操作

使用golang的orm框架就是将golang当中的struct,就是结构体和数据库当中的表字段进行一个映射。也就是操作结构体不是直接操作数据库了,而是通过结构体了。当然这里要使用数据库相关的sdk了。

总而言之就是操作结构体就能够操作数据库,操作结构体,那么表里面的数据其实也就会跟着发生变化。

orm框架主要的作用就是,在go里面就是struct,在java里面其实就是class类,也即是让对象和表产生一个映射关系,我们就不需要操作表了,直接操作对象就行了。

什么是GORM

参考文档: https://gorm.io/zh_CN/docs/index.html   GORM是一个神奇的,对开发人员友好的 Golang ORM
  • 全特性 ORM (几乎包含所有特性)
  • 模型关联 (一对一, 一对多,一对多 (反向), 多对多, 多态关联)
  • 钩子 (Before/After Create/Save/Update/Delete/Find)
  • 预加载
  • 事务
  • 复合主键
  • SQL 构造器
  • 自动迁移
  • 日志
  • 基于GORM回调编写可扩展插件
  • 全特性测试覆盖
  • 开发者友好

1.3 GORM(v2)基本使用

1. 安装
go get -u gorm.io/gorm

2. 连接MySQL

先创建一个数据库

mysql> create database test_db charset utf8; # 创建数据库
mysql> use test_db; # 切换到数据库
mysql> show tables; # 查看是否生成表
+-------------------+
| Tables_in_test_db |
+-------------------+
| users |
+-------------------+
mysql> desc users; # 查看表的字段是否正常
+----------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| username | longtext | YES | | NULL | |
| password | longtext | YES | | NULL | |
+----------+------------+------+-----+---------+----------------+
创建 mysql 连接 ,参考文档: https://gorm.io/zh_CN/docs/connecting_to_the_database.html

Gin链接和使用MySQL数据库

安装MySQL驱动想要在Gin中操作MySQL,首先要有MySQL驱动程序。该驱动程序需要下载:

需要下载mysql的驱动,如果项目依赖包不使用 go mod 管理,那么就需要使用 go get 命令安装以上依赖包:

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

如果导入的包是不使用的,那么在前面加上_,比如

import _"gorm.io/driver/mysql"

不使用这个包为什么还要导入呢?当我导入进来不使用的时候,说明包里面init函数是要执行的,其实就是执行里面的init去做mysql驱动的初始化。

  • parseTime解析时间:gorm里面自带一些创建时间,更新时间,删除时间。在做映射关系的时候拿到的时候是一个时间对象,这里就会自动转化为时间对象。
    loc=Local:用本地环境的一个时区,也就是使用本地时间
    timeout:="10s" //连接超时,10秒

dsn就是拼接的一些信息,root:7PXjAkY!&nlR@tcp(192.168.11.128:3306)/test?后面是可以加上一些参数。

package main

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

var (
	username = "root"
	password = "sMo!G(LbU13E11"
	host     = "192.168.87.128"
	port     = 3306
	dbname   = "test"
	timeout  = "10s"
	DB       *gorm.DB
)

func init() {
	dns := 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(dns), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	DB = db
}

type User struct {
	ID       int64 `gorm:"primary_key"`
	Username string
	Password string
}

func main() {
	//连接成功
	fmt.Println(DB)
}

&{0xc00011a5a0 <nil> 0 0xc00021e000 1}

这个db其实就决定了要操作的是哪一个库,表使用结构体去区分,或者使用表名区分都是可以的。如果要去打开多个库,那么多写几个open就行了,你每次open的时候都是生成了db实例,db实例后面是不同的库,多初始化几个db实例就行了。 

	dsn := "root:7PXjAkY!&nlR@tcp(192.168.11.128:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	//这是固定写法
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

高级配置(对连接当中的配置做一些处理)


  • 跳过默认事务 一般都是打开的,这里只做演示

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

	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		SkipDefaultTransaction: true,
	})
  • 命名策略  约定

GORM 倾向于约定优于配置,默认情况下,GORM 使用 ID 作为主键,使用结构体名的蛇形复数作为表名,字段名的蛇形作为列名,并使用 CreatedAtUpdatedAt 字段追踪创建、更新时间

如果您遵循 GORM 的约定,您就可以少写的配置、代码。 如果约定不符合您的实际要求,GORM 允许你配置它们

gorm采用的命名策略是,表名是蛇形复数,字段名是蛇形单数例如
type Student struct {
 Name   string
 Age    int
 MyStudent string
}

type User struct {} // 默认的表名是 `users`

gorm会为我们这样生成表结构,默认表结构为小写字母+末尾复数s

CREATE TABLE 'students'('name' longtext,'age' bigint,'my_student' longtext)

注意这里传递的是结构体指针类型,而不是结构体类型

type Student struct {
	ID          uint
	StudentName string
	Age         int
}

DB.AutoMigrate(&Student{})


mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| credit_cards   |
| languages      |
| product        |
| students       |
| user_infos     |
| users          |
+----------------+

mysql> desc students;
+--------------+-----------------+------+-----+---------+----------------+
| Field        | Type            | Null | Key | Default | Extra          |
+--------------+-----------------+------+-----+---------+----------------+
| id           | bigint unsigned | NO   | PRI | NULL    | auto_increment |
| student_name | longtext        | YES  |     | NULL    |                |
| age          | bigint          | YES  |     | NULL    |                |
+--------------+-----------------+------+-----+---------+----------------+

我们也可以修改这些默认的命名策略,这些只是做演示,不建议修改默认配置。

	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   "f_", //表名前缀,都加上f_
			SingularTable: true, //单数表名
			NoLowerCase:   true, //不要小写转换
		},
		SkipDefaultTransaction: true,
	})

mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| credit_cards   |
| f_Student      |
| languages      |
| product        |
| user_infos     |
| users          |
+----------------+

mysql> desc f_Student;
+-------------+-----------------+------+-----+---------+----------------+
| Field       | Type            | Null | Key | Default | Extra          |
+-------------+-----------------+------+-----+---------+----------------+
| ID          | bigint unsigned | NO   | PRI | NULL    | auto_increment |
| StudentName | longtext        | YES  |     | NULL    |                |
| Age         | bigint          | YES  |     | NULL    |                |
+-------------+-----------------+------+-----+---------+----------------+

自动创建表


参考文档:https://gorm.io/zh_CN/docs/models.html

其实开发当中不会使用这种,而是提前先创建好表,然后自己在数据库里面创建索引这些,而不是通过结构体标签来完成。( 默认生成的表是结构体小写名字加上复数即users
primary_key是gorm的标签。
import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type User struct {
	ID      int64 `gorm:"primary_key"`
	Username string
	Password string
}

func main() {
	dsn := "root:7PXjAkY!&nlR@tcp(192.168.11.128:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	if err != nil {
		fmt.Println(err)
	}

	//自动迁移
	err = db.AutoMigrate(User{})
	if err != nil {
		fmt.Println(err)
	}

}

mysql> show tables;
+-------------------+
| Tables_in_test_db |
+-------------------+
| users             |
+-------------------+
1 row in set (0.00 sec)

mysql> desc users;
+----------+----------+------+-----+---------+----------------+
| Field    | Type     | Null | Key | Default | Extra          |
+----------+----------+------+-----+---------+----------------+
| id       | bigint   | NO   | PRI | NULL    | auto_increment |
| username | longtext | YES  |     | NULL    |                |
| password | longtext | YES  |     | NULL    |                |
+----------+----------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

自定义表名

package main

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

type User struct {
	Id       int64 `gorm:"primary_key"`
	Username string
	Password string
}

func (*User) TableName() string {
	return "user"
}

func main() {
	dsn := "root:7PXjAkY!&nlR@tcp(192.168.11.128:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	if err != nil {
		panic(err)
	} else {
		fmt.Println(db)
		db.AutoMigrate(&User{})
	}

}

 之后对于表的操作都是对user这张表进行操作

type TablerWithNamer interface {
	TableName(Namer) string
}

其实就是去重写了这个方法,其实也是接口的这个逻辑,在AutoMigrate的时候,被调用了,基于这个表名,去创建了这张表。

gorm.Model


GORM 定义一个 gorm.Model 结构体,其包括字段 IDCreatedAtUpdatedAtDeletedAt

// gorm.Model 的定义
type Model struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

 对于匿名字段,GORM 会将其字段包含在父结构体中,例如:

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
}

创建记录


user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}

result := db.Create(&user) // 通过数据的指针来创建

user.ID             // 返回插入数据的主键
result.Error        // 返回 error
result.RowsAffected // 返回插入记录的条数

我们还可以使用 Create() 创建多项记录:

users := []*User{
    User{Name: "Jinzhu", Age: 18, Birthday: time.Now()},
    User{Name: "Jackson", Age: 19, Birthday: time.Now()},
}

result := db.Create(users) // pass a slice to insert multiple row

result.Error        // returns error
result.RowsAffected // returns inserted records count

基本增删改查  


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


//自定义返回表名
func (* User) TableName() string {
	return "user"
}


// User 表的结构体ORM映射
type User struct {
    Id int64 `gorm:"primary_key"`
    Username string
    Password string
}

func main() {

// 0、连接数据库
    dsn := "root:1@tcp(127.0.0.1:3306)/test_db?
    charset=utf8mb4&parseTime=True&loc=Local"
    db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    db.AutoMigrate(User{})

    // 1、增
    db.Create(&User{
    //Id: 3,
    Username: "zhangsan",
    Password: "123456",
    })

[31.540ms] [rows:1] INSERT INTO `users` (`username`,`password`,`created_at`,`updated_at`,`deleted_at`) VALUES ('lucas','123456','2023-06-12 17:19:
34.776','2023-06-12 17:19:34.776',NULL)

    // 2、改
    db.Model(User{Id: 3,}).Update("username", "lisi")
    //db.Model(User{}).Where("id = 3").Update("username", "lisi")

[4.154ms] [rows:1] UPDATE `users` SET `username`='lisa',`updated_at`='2023-06-12 17:23:27.574' WHERE `users`.`deleted_at` IS NULL AND `id` = 1


    // 3、查
    // 3.1 过滤查询
    u := User{Id: 3}
    db.First(&u)
    fmt.Println(u)
    // 3.2 查询所有数据
    users := []User{}
    db.Find(&users)
    fmt.Println(users) // [{2 zhangsan 123456} {3 lisi 123456}]

[0.518ms] [rows:4] SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL

    // 4、删
    // 4.1 删除 id = 3 的用户
    db.Delete(&User{Id: 3})
    // 4.2 条件删除
    db.Where("username = ?", "zhangsan").Delete(&User{})
}

修改数据其实就是指定你的结构体,也就是你的表是哪个。model在修改数据的时候封装了where条件的功能,update就是修改某个字段。如果加上debug那么整条语句执行转化为sql打印出来。(model主要两个功能 1 找到对应的表 2 填入条件字段)(无论在做数据库的什么操作,你都需要有一个地方去指定这个映射关系让其找到这张表)

db.Debug().Model(&User{Id: 1}).Update("username", "lisi")

[1.638ms] [rows:0] UPDATE `user` SET `username`='lisi' WHERE `id` = 1

或者使用下面这种方法,这里table就直接指定你的表名了,而model是指定你的结构体,这里是有映射关系的。

db.Debug().Table("user").Where("id=?", 1).Update("username", "wan")
[3.300ms] [rows:1] UPDATE `user` SET `username`='wan' WHERE id=1 

上面所有使用到结构体的都和那张表有关系。

查询某条数据使用first 查询唯一值

	u := &User{Id: 1}
	db.Debug().First(u)
	fmt.Println(u)

	db.Where("id=?",u.Id).First(u)

查询所有的数据 

	var users []User
	db.Find(&users)
	fmt.Println(users)

删除

db.Where("username =? and id =?","wangwu",1).Delete(&User{})

 官方网站 快速入门示例: 

package main

import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)

type Product struct {
  gorm.Model
  Code  string
  Price uint
}

func main() {
  db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  if err != nil {
    panic("failed to connect database")
  }

  // 迁移 schema
  db.AutoMigrate(&Product{})

  // Create
  db.Create(&Product{Code: "D42", Price: 100})

  // Read
  var product Product
  db.First(&product, 1) // 根据整型主键查找
  db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录

  // Update - 将 product 的 price 更新为 200
  db.Model(&product).Update("Price", 200)
  // Update - 更新多个字段
  db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段
  db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})

  // Delete - 删除 product
  db.Delete(&product, 1)
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值