设计模式之 Database/SQL 与 GORM 实践|青训营笔记

105 阅读4分钟

设计模式之 Database/SQL 与 GORM 实践

概述

学员手册:juejin.cn/post/709597…

课程ppt bytedance.feishu.cn/file/boxcnc…

QQ图片20220610161827.png

  • 理解 Database/SQL

    • Database/SQL 的基本用法

    • 设计原理

QQ图片20220610161542.png - 基础概念

  • GORM 的使用简介: 是当今比较热门的 golang 的 orm 操作数据库的工具,使用上主要是把 struct 类和数据库表进行映射,操作数据库时无需手写 sql。本质就是提供一组函数来帮助我们快速拼接 sql 语句,尽量减少 sql 的编写

github 地址:github.com/go-gorm/gor…

取数据逻辑:

  • 从数据库读取的数据会先保存到预先定义的模型对象,然后我们就可以从模型对象得到我们想要的数据

存数据逻辑:

  • 存数据到数据库也是先新建一个模型对象,然后把数据保存到模型对象,最后把模型对象保存到数据库

全功能 ORM

关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)

Create,Save,Update,Delete,Find 中钩子方法

支持 Preload、Joins 的预加载

事务,嵌套事务,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…

每个特性都经过了测试的重重考验

开发者友好

先码一下gorm的官方文档:文档地址

-   GORM 的基本用法

安装:
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

连接数据库 :

dsn := "username:passwd@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.Println(err)
	}
	fmt.Println(db)

自动创建表


package main

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

type User struct {
	Id       int64 `gorm:"primary_key" json:"id"` // 设置id为主键
	Username string
	Password string
}

func main() {
	dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(db)
	db.AutoMigrate(User{}) // 自动创建表结构

QQ图片20220610163304.png

QQ图片20220610163451.png

QQ图片20220610163605.png

Model 定义:

QQ图片20220610164207.png

惯例约定:

QQ图片20220610164340.png

注:Column 指定列的名称

Type 指定列的类型

Size 指定列的大小,默认是 255

PRIMARY_KEY 指定一个列作为主键

UNIQUE 指定一个唯一的列

DEFAULT 指定一个列的默认值

PRECISION 指定列的数据的精度

NOT NULL 指定列的数据不为空

AUTO_INCREMENT 指定一个列的数据是否自增

INDEX 创建带或不带名称的索引,同名创建复合索引

UNIQUE_INDEX 类似 索引 ,创建一个唯一的索引

EMBEDDED 将 struct 设置为 embedded

EMBEDDED_PREFIX 设置嵌入式结构的前缀名称

  • 忽略这些字段

关联介绍:

QQ图片20220610164633.png

package models

import (
	"github.com/jinzhu/gorm"
	"testing"
)
import _ "github.com/jinzhu/gorm/dialects/sqlite"

func TestAsso(t *testing.T) {

	db, err := gorm.Open("sqlite3", "./gorm.db")
	if err != nil {
		t.Error(err)
	}
	defer db.Close()
	db.LogMode(true)
	db.AutoMigrate(&User{}, &Address{}, &Email{}, &Language{})
	addr := Address{Address1: "My addr"}
	db.Save(&addr)
	user := User{
		Name:            "jinzhu",
		BillingAddress:  addr,
		ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
		Emails: []Email{
			{Email: "jinzhu@example.com"},
			{Email: "jinzhu-2@example.com"},
		},
		Languages: []Language{
			{Name: "ZH"},
			{Name: "EN"},
		},
	}

	// 对于主键为空的item,不去创建
	//db.Debug().Set("gorm:association_autocreate", false).Create(&user)
	// 对于主键非空的item,不去更新,即:只保存外键关联那个字段,不更新关联对象的值
	//db.Debug().Set("gorm:association_autoupdate", false).Create(&user)
	// 不更新外健
	//db.Debug().Set("gorm:association_save_reference", false).Create(&user)
	// 默认上述开关全打开
	db.Create(&user)

	//db.Debug().Set("gorm:association_autocreate", false).
	//	Set("gorm:association_autoupdate", false).
	//	Set("gorm:association_save_reference", false).
	//	Create(&user)


	langs := []Language{}
	// 查找关联
	db.Model(&user).Association("Languages").Find(&langs)

	emails := []Email{}
	db.Model(&user).Association("Emails").Find(&emails)
	// 添加关联
	db.Model(&user).Association("Emails").Append(Email{Email: "abc@qq.com"})

	// 替换关联, 如果item主键为空,默认会新建
	email := emails[0]
	db.Model(&user).Association("Emails").Replace([]Email{email, {Email: "def@qq.com"}})

	// 删除关联,item主键非空才生效
	def := Email{}
	db.First(&def, "Email = ?", "def@qq.com")
	db.Model(&user).Association("Emails").Delete(def)

	// 清空关联,无论是替换,删除,还是清空,只会删除关联的引用,不会删除关联本身
	db.Model(&user).Association("Emails").Clear()

	print("ok")

}

使用orm框架进行CRUD

   // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
   dsn := "root:12345678@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
   if err != nil {
      panic(err)
   }
   //ORM操作
   GormCreate(db)
}

func GormCreate(db *gorm.DB) {
   //create
   student := Student{Name: "sss", Age: 10, Address: "Guangzhou"}
   res := db.Create(&student)
   fmt.Println(student.Id)
   fmt.Println(res.Error)
}


 原因:

操作对象,就相当于直接操作数据库表,不需要占位符

代码量少不易出错

级联删除:

QQ图片20220610171502.png

  • GORM 的设计原理:

QQ图片20220610173709.png

SQL 生成的机制:

QQ图片20220610174400.png

QQ图片20220610174529.png

插件扩展机制

QQ图片20220610174712.png

QQ图片20220610174815.png

QQ图片20220610175209.png

QQ图片20220610175239.png

ConnPool 扩展机制

QQ图片20220610175357.png

QQ图片20220610175535.png

Dialector 扩展机制

image.png

  • GORM 最佳实践

QQ图片20220610180034.png