gorm数据库操作

发布时间 2023-04-14 22:03:00作者: yangphp

前言:

Object-Relationl Mapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。

GORMgolang写的ORM

网址:https://gorm.io/

正文:

GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server

安装gorm

go get -u  gorm.io/gorm

go get -u gorm.io/driver/mysql    //mysql 驱动

 

链接mysql实例:

var db *gorm.DB
var dbErr error

func init() {
    dsn := "root:root123456@tcp(127.0.0.1:3306)/yangtest?charset=utf8"
    db, dbErr = gorm.Open(mysql.Open(dsn))
    if dbErr != nil {
        log.Fatal(dbErr.Error())
    }
    fmt.Println("mysql 连接成功")
}

 

模型定义自动迁移tag标签

根据标签自动生成表示例:

type User struct {
    //gorm.Model
    Id       int    `gorm:"type:int;primary key"`
    Name     string `gorm:"type:varchar(20);not null;index"`
    Email    string `gorm:"type:varchar(20)"`
    Age      int    `gorm:"type:tinyint(20);unsigned"`
    CreateAt int64  `gorm:"type:int(20);unsigned;not null;autoCreateTime"`
    UpdateAt int64  `gorm:"type:int(20);unsigned;not null;autoUpdateTime;default:0"`
}


var db *gorm.DB
var dbErr error

func init() {
    dsn := "root:root123456@tcp(127.0.0.1:3306)/yangtest?charset=utf8"
    db, dbErr = gorm.Open(mysql.Open(dsn))
    if dbErr != nil {
        log.Fatal(dbErr.Error())
    }
    fmt.Println("mysql 连接成功")
}

func main() {
    db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
}

运行程序,即可看到数据库生成了 users表,表结构如下:

 

CREATE TABLE `users` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `email` varchar(20) DEFAULT NULL,
  `age` tinyint(20) DEFAULT NULL,
  `create_at` int(20) NOT NULL,
  `update_at` int(20) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `idx_users_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

 

 

tag标记列表:

 

 

 

配置表名:

默认生成的表名是 是结构体名称的复数形式,如果自定义其他表名,

就需要使用  TableName方法进行配置表名

 

//根据结构体自定义表名
func (u *User) TableName() string {
    return "user"
}

 

全局配置表前缀及表名:

如果需要全局表名不加复数,以及全局增加表前缀,就在连接数据库的时候进行全局配置

 

func init() {
    dsn := "root:root123456@tcp(127.0.0.1:3306)/yangtest?charset=utf8"
    db, dbErr = gorm.Open(mysql.Open(dsn), &gorm.Config{
        //全局配置
        NamingStrategy: schema.NamingStrategy{
            SingularTable: true,    //表名不增加复数
            TablePrefix:   "shop_", //增加表前缀
        },
    })
    if dbErr != nil {
        log.Fatal(dbErr.Error())
    }
}

 

 

gorm操作数据库增删改查

插入数据:

func InsertData()  {
    user := User{Name: "lampol44", Age: 23, Email: "3333333@qq.com"}
    //res := db.Create(&user)
    //fmt.Println(res.RowsAffected, res.Error, user.Id) //输出1 <nil> 1

    //插入字段插入
    //res := db.Select("name", "age").Create(&user)
    //fmt.Println(res.RowsAffected, res.Error, user.Id)

    //排除某字段插入
    res := db.Omit("email").Create(&user)
    fmt.Println(res.RowsAffected, res.Error, user.Id)
}

 

批量插入数据:

func InsertDataMul()  {

//定义一个结构体数组
    user := []User{
        {Name: "lampol3", Age: 23, Email: "333333333@qq.com"},
        {Name: "lampol4", Age: 24, Email: "444444444@qq.com"},
        {Name: "lampol5", Age: 25, Email: "555555555@qq.com"},
        {Name: "lampol6", Age: 26, Email: "666666666@qq.com"},
    }
    res := db.Create(&user)
    fmt.Println(res.RowsAffected, res.Error)
}

使用map单条插入数据:

使用map的话,因为没有标签,不会自动生成 create_at 字段,也无法获取到插入的id

所以不建议使用map作为插入数据的参数

 

func InsertData()  {
    var user map[string]interface{}
    user = make(map[string]interface{})

    user["name"] = "lampol8"
    user["age"] = 55
    user["email"] = "8888888@qq.com"
    user["create_at"] = time.Now().Unix()

    //res := db.Model(&User{}).Create(&user)
    res := db.Table("users").Create(&user)

    fmt.Println(user)
    fmt.Println(res.RowsAffected, res.Error)
}

 

 

 

如果不写入 create_at 会报错

 

 

 

查询数据

First,Last,Take

使用结构体接收数据:

var user User
    //获取第一条数据
    db.First(&user)
    fmt.Println(user)

    //获取第一条数据
    db.Take(&user)
    fmt.Println(user)

    //获取最后一条数据
    db.Last(&user)
    fmt.Println(user)

使用map接收数据:

var data = map[string]interface{}{}
    db.Model(&User{}).First(&data)
    fmt.Println(data)

    db.Model(&User{}).Take(&data)
    fmt.Println(data)

    db.Model(&User{}).Last(&data)
    fmt.Println(data)
    
    fmt.Println(data["id"])

 

 

使用take获取ID6的数据

    var user User
    db.Take(&user, 6)
    fmt.Println(user)

 

使用take获取namelampol6的数据

    //查询 name 为 lampol5的记录
    var user User
    db.Take(&user, "name", "lampol6")
    fmt.Println(user)

查询多条数据

使用find查询全部数据

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

使用find查询多条ID的数据

    var users []User
    db.Find(&users, []int{3, 4, 5, 6, 7})
    fmt.Println(users)

使用scan查询多条数据

    var users []User
    db.Model(&User{}).Scan(&users)
    fmt.Println(users)

 

使用find根据条件查询多条

var users []User
    //一个条件
    db.Find(&users, "age=?", 26)
    fmt.Println(users)
    //多个条件
    db.Find(&users, User{
        Name: "lampol6",
        Age:  26,
    })
    fmt.Println(users)

使用where查询多条

var users []User
    //单个where查询
    db.Where("age=?", 26).Find(&users)
    fmt.Println(users)
    //多个where查询
    db.Where("age=?", 26).Where("name=?", "lampol6").Find(&users)
    fmt.Println(users)

    //不等于查询
    db.Where("age<>?", 26).Find(&users)
    fmt.Println(users)
    //in查询
    db.Where("name in ?", []string{"lampol4", "lampol5"}).Find(&users)
    fmt.Println(users)

    //select查询指定字段
    db.Select([]string{"name", "age"}).Where("name in ?", []string{"lampol4", "lampol5"}).Find(&users)
    fmt.Println(users)

    //排序 order 和 limit
    db.Where("age=?", 26).Order("id DESC").Limit(2).Find(&users)
    fmt.Println(users)

多表,连表查询:

type OrderList struct {
        Name      string
        GoodsName string
    }

    var userOrder []OrderList

    db.Table("shop_user").Select("shop_user.name", "shop_order_list.goods_name").
        Joins("join shop_order_list on shop_user.id=shop_order_list.user_id").Find(&userOrder)
    fmt.Println(userOrder)

查询数量:

var count int64
    //db.Model(&User{}).Where("age=?", 26).Count(&count)
    db.Table("shop_user").Where("age=?", 26).Count(&count)
    fmt.Println(count)

 

更新数据:

//更新数据
    res := db.Model(&User{}).Where("id=?", 5).Update("age", 45)
    fmt.Println(res.RowsAffected)

    //更新多个字段
    res := db.Model(&User{}).Where("id=?", 6).Updates(&User{Name: "yangphp", Age: 32})
    fmt.Println(res.RowsAffected)

//查询出来,然后使用save更新
    var user User
    db.First(&user)
    user.Age = 44
    user.Name = "lampol_88"
    db.Save(&user)

//原生sql更新
db.Exec("UPDATE shop_user SET name=?", "lampol")

//删除
//db.Delete(&User{}, 5)       //删除一条
db.Delete(&User{}, []int{6, 7}) //删除多条

原生sql增删查改:

//插入数据
    db.Exec("INSERT INTO shop_user (name,age,email,is_sex) VALUES(?,?,?,?)", "lampol_4", 55, "123@qq.com", 1)
    //更新
    res := db.Exec("UPDATE shop_user SET name=? WHERE id=?", "lampol_2", 5)
    //更新, expr做计算
    db.Exec("UPDATE shop_user SET age=? WHERE id=?", gorm.Expr("age+?", 5), 5)
    //删除
    res := db.Exec("DELETE FROM shop_user  WHERE id=?", 7)

//查询数据:
type Result struct {
        Name string
        Age  int
    }
    var result []Result
    db.Raw("SELECT name,age FROM shop_user WHERE id=? ", 8).Scan(&result)
    db.Raw("SELECT name,age FROM shop_user WHERE id IN ? ", []int{5, 6, 8}).Scan(&result)

    var age int  //查询单个字段
    db.Raw("SELECT age FROM shop_user WHERE id=? ", 6).Scan(&age)
    fmt.Println(result)

 

GORM  DryRun模式

生成 SQL 但不执行。 它可以用于准备或测试生成的 SQL

 

dsn := “root:123456@tcp(127.0.0.1:3306)/shop?charset=utf8mb4&parseTime=True&loc=Local”
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
   NamingStrategy: schema.NamingStrategy{
      Table: true,
      TablePrefix:   “shop_”,
   },
   DryRun: true, //全局打开  sql语句不会执行
})

user := User{Name: "lampol", Age: 23, Email: "123@qq.com", Sex: 1}
stmt := db.Create(&user).Statement
fmt.Println(db.Dialector.Explain(stmt.SQL.String(), stmt.Vars...))

 

 

 

不在全局生效的DryRun模式:

var user User
res := db.Session(&gorm.Session{DryRun: true}).First(&user).Statement.SQL.String()
fmt.Println(res)

 

gorm钩子

Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数

钩子方法的函数签名应该是 func(*gorm.DB) error

插入   BeforeSave   BeforeCreate    AfterCreate     AfterSave

更新    BeforeSave    BeforeUpdate   AfterUpdate    AfterSave

删除    BeforeDelete     AfterDelete

查询   AfterFind

钩子代码示例1

 

// 定义钩子
func (u *User) BeforeSave(tx *gorm.DB) (err error) {
    fmt.Println("BeforeSave....")
    return
}
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
    fmt.Println("BeforeCreate....")
    return
}
func (u *User) AfterSave(tx *gorm.DB) (err error) {
    fmt.Println("AfterSave....")
    return
}
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
    fmt.Println("AfterCreate....")
    return
}


func main() {
    user := User{Name: "lampol33", Age: 33, Email: "32423@qq.com"}
    db.Create(&user)
    fmt.Println("create ....")

    //更新
    db.Model(&User{}).Where("id=?", 11).Update("name", "lampol_9")
    fmt.Println("update ....")

    //使用 table,未使用Model,无法使用钩子
    //db.Table("shop_user").Where("id=?", 11).Update("name", "lampol_9")}

 

打印结果:

 

 

 

GORM  事务

数据表引擎需要 innodb类型才可以使用事务

show create table  shop_user;

alter table shop_user  engine="innodb";

// 开始事务
tx := db.Begin()
// 遇到错误时回滚事务
tx.Rollback()
// 提交事务
tx.Commit()

事务示例1

 

tx := db.Begin()
    user := User{Name: "lampol44", Age: 44, Email: "asdfasdf@qq.com"}
    delnum := tx.Delete(&User{}, 100).RowsAffected

    insNum := tx.Create(&user).RowsAffected
    fmt.Println(delnum, insNum)

    if delnum != 1 || insNum != 1 {
        //失败 回滚
        fmt.Println("rollback ....")
        tx.Rollback()
        return
    }
//未失败,提交
    tx.Commit()
    fmt.Println("commit ....")

 

 

 

完结