go work init
。go mod init test.com/gormtest
。go work use ./gormtest
。go get -u gorm.io/driver/mysql
。go get -u gorm.io/gorm
。go get -u github.com/gin-gonic/gin
。package main
import (
"github.com/gin-gonic/gin"
"log"
"test.com/gormtest/router"
)
/* 主函数 */
func main() {
r := gin.Default() // 初始化一个 Engine 实例
router.InitRouter(r) // 初始化 路由器
err := r.Run(":8080") // 运行
if err != nil {
log.Fatalln("运行 引擎 失败", err)
}
}
package router
import (
"github.com/gin-gonic/gin"
"test.com/gormtest/api"
)
/* 初始化路由器 */
func InitRouter(r *gin.Engine) { // 初始化路由
api.RegisterRouter(r) // 注册路由器, 在 api/router 中实现.
}
package api
import "github.com/gin-gonic/gin"
/* 注册路由器 */
func RegisterRouter(r *gin.Engine) {
r.GET("/save", SaveUser)
r.GET("/get", GetUser)
r.GET("/update", UpdateUser)
r.GET("/delete", DeleteUser)
}
package api
import (
"github.com/gin-gonic/gin"
"test.com/gormtest/dao"
"time"
)
/* 保存数据 */
func SaveUser(c *gin.Context) {
user := &dao.User{Username: "tom", Password: "123456", CreateTime: time.Now().Unix()}
dao.SaveUser(user)
c.JSON(200, user)
}
/* 获取数据 */
func GetUser(c *gin.Context) {
user := dao.GetByID(1)
c.JSON(200, user)
}
/* 更新数据 */
func UpdateUser(c *gin.Context) {
dao.UpdateUser(1)
user := dao.GetByID(1)
c.JSON(200, user)
}
/* 删除数据 */
func DeleteUser(c *gin.Context) {
dao.DeleteUser(1)
user := dao.GetByID(1)
c.JSON(200, user)
}
package dao
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
)
/* 数据库 */
var DB *gorm.DB
/* 初始化连接数据库 */
func init() {
// 配置 Mysql 连接参数
username := "root" // 账号
password := "123456" // 密码
host := "127.0.0.1" // ip
port := 3306 // 端口
Dbname := "itcase" // 数据库
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, Dbname)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalln("db connected error", err)
}
DB = db
}
package dao
import "log"
/* 数据库结构体 */
type User struct {
ID int64 // 主键
Username string `gorm:"column:username"`
Password string `gorm:"column:password"`
CreateTime int64 `gorm:"column:createtime"`
}
/* 获取表名 */
func (u User) TableName() string {
return "user"
}
/* 保存数据 */
func SaveUser(user *User) {
err := DB.Create(user).Error
if err != nil {
log.Println("insert user error", err)
}
}
/* 获取数据 */
func GetByID(id int64) User {
var user User
err := DB.Where("id=?", id).First(&user).Error
if err != nil {
log.Println("get user by id error", err)
}
return user
}
/* 获取所有数据 */
func GetAll() []User {
var user []User
err := DB.Find(&user).Error
if err != nil {
log.Println("get user all", err)
}
return user
}
/* 更新数据 */
func UpdateUser(id int64) {
err := DB.Model(&User{}).Where("id=?", id).Update("username", "lisi")
if err != nil {
log.Println("update user by id error", err)
}
}
/* 删除数据 */
func DeleteUser(id int64) {
err := DB.Where("id=?", id).Delete(&User{})
if err != nil {
log.Println("update user by id error", err)
}
}
假设有一个商品表
CREATE TABLE goods (
id int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID,商品Id',
name varchar(30) NOT NULL COMMENT '商品名',
price decimal(10,2) unsigned NOT NULL COMMENT '商品价格',
type_id int(10) unsigned NOT NULL COMMENT '商品类型Id',
createtime int(10) NOT NULL DEFAULT 0 COMMENT '创建时间',
PRIMARY KEY (id)
) COMMENT '商品表' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
将上述表翻译为模型后
type Good struct {
Id int //表字段名为:id
Name string //表字段名为:name
Price float64 //表字段名为:price
TypeId int //表字段名为:type_id
CreateTime int64 `gorm:"column:createtime"` //表字段名为:createtime
}
标签定义:
`gorm:"标签内容"`
标签定义部分,多个标签定义可以使用分号(;)分隔
gorm常用标签如下:
标签 | 说明 | 例子 |
---|---|---|
column | 指定列名 | `gorm:“column:createtime”` |
primaryKey | 指定主键 | `gorm:“column:id; PRIMARY_KEY”` |
- | 忽略字段 | `gorm:“-”` 可以忽略 struct 字段,被忽略的字段不参与gorm的读写操作 |
type Tabler interface {
TableName() string
}
// TableName 会将 User 的表名重写为 `profiles`
func (User) TableName() string {
return "profiles"
}
func UserTable(user User) func (tx *gorm.DB) *gorm.DB {
return func (tx *gorm.DB) *gorm.DB {
if user.Admin {
return tx.Table("admin_users")
}
return tx.Table("users")
}
}
db.Scopes(UserTable(user)).Create(&user)
db.Table("deleted_users")
GORM 定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt
// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
GORM 约定使用 CreatedAt、UpdatedAt 追踪创建/更新时间。如果定义了这种字段,GORM 在创建、更新时会自动填充当前时间。
要使用不同名称的字段,您可以配置 autoCreateTime、autoUpdateTime 标签
如果想要保存 UNIX(毫/纳)秒时间戳,而不是 time,只需简单地将 time.Time 修改为 int 即可。
type User struct {
CreatedAt time.Time // 默认创建时间字段, 在创建时,如果该字段值为零值,则使用当前时间填充
UpdatedAt int // 默认更新时间字段, 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
Updated int64 `gorm:"autoUpdateTime:nano"` // 自定义字段, 使用时间戳填纳秒数充更新时间
Updated int64 `gorm:"autoUpdateTime:milli"` //自定义字段, 使用时间戳毫秒数填充更新时间
Created int64 `gorm:"autoCreateTime"` //自定义字段, 使用时间戳秒数填充创建时间
}
可以将它嵌入到您的结构体中,以包含这几个字段,比如
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
}
对于正常的结构体字段,你也可以通过标签 embedded 将其嵌入,例如:
type Author struct {
Name string
Email string
}
type Blog struct {
ID int
Author Author `gorm:"embedded"`
Upvotes int32
}
// 等效于
type Blog struct {
ID int64
Name string
Email string
Upvotes int32
}
可以使用标签 embeddedPrefix 来为 db 中的字段名添加前缀,例如:
type Blog struct {
ID int
Author Author `gorm:"embedded;embeddedPrefix:author_"`
Upvotes int32
}
// 等效于
type Blog struct {
ID int64
AuthorName string
AuthorEmail string
Upvotes int32
}
GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server。
连接数据库主要是两个步骤:
gorm 库使用 dsn 作为连接数据库的参数,dsn 翻译过来就叫数据源名称,用来描述数据库连接信息。一般都包含数据库连接地址,账号,密码之类的信息。
格式:
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
示例:
// mysql dsn格式
// 涉及参数:
// username 数据库账号
// password 数据库密码
// host 数据库连接地址,可以是Ip或者域名
// port 数据库端口
// Dbname 数据库名
username:password@tcp(host:port)/Dbname?charset=utf8mb4&parseTime=True&loc=Local
// 填上参数后的例子
// username = root
// password = 123456
// host = "127.0.0.1"
// port = 3306
// Dbname = "itcase"
// 后面 K/V 键值对参数含义为:
// charset=utf8mb4 客户端字符集为 utf8mb4
// parseTime=true 支持把数据库 datetime 和 date 类型转换为 golang 的 time.Time 类型
// loc=Local 使用系统本地时区
root:123456@tcp(127.0.0.1:3306)/itcase?charset=utf8mb4&parseTime=True&loc=Local
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/itcase?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
MySQL 驱动程序提供了 一些高级配置 可以在初始化过程中使用,例如:
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "root:123456@tcp(127.0.0.1:3306)/itcase?charset=utf8mb4&parseTime=True&loc=Local", // DSN
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{})
db.Debug()
sqlDB, _ := db.DB()
// 设置数据库连接池参数
sqlDB.SetMaxOpenConns(100) //设置数据库连接池最大连接数
sqlDB.SetMaxIdleConns(20) //连接池最大允许的空闲连接数,如果没有sql任务需要执行的连接数大于20,超过的连接会被连接池关闭
user := User{
Username:"zhangsan",
Password:"123456",
CreateTime:time.Now().Unix(),
}
db.Create(&user)
user.ID // 返回插入数据的主键
result.Error // 返回 error
result.RowsAffected // 返回插入记录的条数
用指定的字段创建
db.Select("username","password").Create(&user)
忽略字段
db.Omit("username").Create(&user)
批量插入
var users = []User{{Username: "jinzhu1"}, {Username: "jinzhu2"}, {Username: "jinzhu3"}}
db.Create(&users)
for _, user := range users {
user.ID // 1,2,3
}
使用 map 创建
db.Model(&User{}).Create(map[string]interface{}{
"Name": "jinzhu", "Age": 18,
})
// batch insert from `[]map[string]interface{}{}`
db.Model(&User{}).Create([]map[string]interface{}{
{"Name": "jinzhu_1", "Age": 18},
{"Name": "jinzhu_2", "Age": 20},
})
使用原生 sql 创建
db.Exec("insert into users (username,password,createtime) values (?,?,?)", user.Username, user.Password, user.CreateTime)
创建一个商品表:
CREATE TABLE `goods` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品id',
`title` varchar(100) NOT NULL COMMENT '商品名',
`price` decimal(10, 2) NULL DEFAULT 0.00 COMMENT '商品价格',
`stock` int(11) DEFAULT '0' COMMENT '商品库存',
`type` int(11) DEFAULT '0' COMMENT '商品类型',
`create_time` datetime NOT NULL COMMENT '商品创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
package dao
import "time"
type Goods struct {
Id int
Title string
Price float64
Stock int
Type int
CreateTime time.Time
}
func (v Goods) TableName() string {
return "goods"
}
func SaveGoods(goods Goods) {
DB.Create(&goods)
}
package dao
import (
"testing"
"time"
)
func TestSaveGoods(t *testing.T) {
goods := Goods{
Title: "毛巾",
Price: 25,
Stock: 100,
Type: 0,
CreateTime: time.Now(),
}
SaveGoods(goods)
}
保存数据:
goods := Goods{}
DB.Where("id = ?", 1).Take(&goods)
goods.Price = 100
//UPDATE `goods` SET `title`='毛巾',`price`=100.000000,`stock`=100,`type`=0,`create_time `='2022-11-25 13:03:48' WHERE `id` = 1
DB.Save(&goods)
更新单个列:
goods := Goods{}
DB.Where("id = ?", 2).Take(&goods)
DB.Model(&goods).Update("title", "hello")
更新多个列:
goods := Goods{}
DB.Where("id = ?", 2).Take(&goods)
//更新非零值的字段 也可以使用map
DB.Model(&goods).Updates(Goods{
Title: "hello",
Stock: 200,
})
更新选定字段:
goods := Goods{}
DB.Where("id = ?", 2).Take(&goods)
DB.Model(&goods).Select("title").Updates(Goods{
Title: "hello",
Stock: 200,
})
表达式:
db.Model(&goods).Update("stock", gorm.Expr("stock + 1"))
db.Model(&goods).Update(map[string]interface{}{"stock": gorm.Expr("stock + 1")})
子查询更新:
goods := Goods{}
DB.Where("id = ?", 2).Take(&goods)
DB.Model(&goods).Update("title", DB.Model(&User{}).Select("username").Where("id=?", 2))
gorm 更新:
db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")
goods := Goods{}
DB.Where("id = ?", 2).Take(&goods)
DB.Delete(&goods)
根据主键删除:
DB.Delete(&Goods{}, 1)
gorm 删除:
db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})
查询函数:
db.Take(&goods)
db.First(&goods)
db.Last(&goods)
db.Find(&goods)
var titles []string
db.Model(&Goods{}).Pluck("title", &titles)
where:
通过 db.Where 函数设置条件
函数说明:db.Where(query interface{}, args ...interface{})
db.Where("id in (?)", []int{1,2,5,6}).Take(&goods)
select:
设置select子句, 指定返回的字段
var goods Goods
DB.Select("id", "title").Find(&goods)
order:
var goods []Goods
DB.Order("id desc").Find(&goods)