1、安装gorm
go get -u github.com/jinzhu/gorm
2、Golang Beego中使用 Gorm 连接数据库
在 models 下面新建 db.go ,建立数据库链接
package models
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
var DB *gorm.DB
func init() {
DB,_=gorm.Open("mysql","root:123456@/beegodbcharset=utf8&parseTime=True&loc=L ocal")
}
3、关闭数据库链接
找到 main.go 引入 models 模块, 在 main 方法执行完毕后关闭数据库链
package main
import (
"demo/models"
"github.com/astaxie/beego"
)
func main() {
beego.Run()
defer models.DB.Close()
}
4、定义操作数据库的模型
Gorm 官方给我们提供了详细的说明:https://v1.gorm.io/zh_CN/docs/models.html
虽然在 gorm 中可以指定字段的类型以及自动生成数据表,但是在实际的项目开发中,我们 是先设计数据库表,然后去实现编码的。
在实际项目中定义数据库模型注意以下几点:
1、结构体的名称必须首字母大写 ,并和数据库表名称对应。
例如:表名称为 user 结构体 名称定义成 User,表名称为 article_cate 结构体名称定义成 ArticleCate
2、结构体中的字段名称首字母必须大写,并和数据库表中的字段一一对应。
例如:下面结 构体中的 Id 和数据库中的 id 对应,Username 和数据库中的 username 对应,Age 和数据库中 的 age 对应,Email 和数据库中的 email 对应,AddTime 和数据库中的 add_time 字段对应
package models
type User struct {
Id int
Phone string
Password string
AddTime int
LastIp string
Email string
Status int
}
func (User) TableName() string {
return "user"
}
3、默认情况表名是结构体名称的复数形式。
如果我们的结构体名称定义成 User,表示这个 模型默认操作的是 users 表。
4、我们可以使用结构体中的自定义方法 TableName 改变结构体的默认表名称
func (User) TableName() string {
return "user"
}
表示把 User 结构体默认操作的表改为数据库的 user 表
三、Beego GORM CURD1、创建
增加成功后会返回刚才增加的记录
user := models.User{
Username: "gorm",
Age: 18,
Email: "gorm@qq.com",
}
models.DB.Create(&user)
2、查询
根据主键查询第一条记录
user := models.User{}
db.First(&user)
根据主键查询最后一条记录
user := models.User{}
db.Last(&user)
查询所有的记录
users := []models.User{}
db.Find(&users)
查询指定的某条记录(仅当主键为整型时可用)
user := models.User{}
db.First(&user, 10)
或者
user := models.User{Id:10}
db.Find(&user)
Where 条件
// 获取第一个匹配的记录
db.Where("name = ?", "jinzhu").First(&user)
SELECT * FROM users WHERE name = 'jinzhu' limit 1;
// 获取所有匹配的记录
db.Where("name = ?", "jinzhu").Find(&users)
SELECT * FROM users WHERE name = 'jinzhu';
// <>
db.Where("name <> ?", "jinzhu").Find(&users)
SELECT * FROM users WHERE name <> 'jinzhu';
// IN
db.Where("name IN (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
SELECT * FROM users WHERE name in ('jinzhu','jinzhu 2');
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
SELECT * FROM users WHERE name LIKE '%jin%';
// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
3、更新
更新所有字段
Save会更新所有字段,即使你没有赋值
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
更新修改字段
如果你只希望更新指定字段,可以使用Update或者Updates
// 更新单个属性,如果它有变化
db.Model(&user).Update("name", "hello")
UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// 根据给定的条件更新单个属性
db.Model(&user).Where("active = ?", true).Update("name", "hello")
UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
// 使用 map 更新多个属性,只会更新其中有变化的属性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
// 使用 struct 更新多个属性,只会更新其中有变化且为非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18})
UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
// 警告:当使用 struct 更新时,GORM只会更新那些非零值的字段
// 对于下面的操作,不会发生任何更新,"", 0, false 都是其类型的零值
db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})
4、删除
删除记录
警告: 删除记录时,请确保主键字段有值,GORM 会通过主键去删除记录,如果主键为空,GORM 会删除该 model 的所有记录。
批量删除
删除全部匹配的记录
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
DELETE from emails where email LIKE "%jinzhu%";
db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
DELETE from emails where email LIKE "%jinzhu%";
四、beego 中使用 GORM 关联查询
1、一对一
如上图所示,一个文章只有一个分类,article 和 article_cate 之间是 1 对 1 的关系。 文章表中的 cate_id 保存着文章分类的 id。 如果我们想查询文章的时候同时获取文章分类,就涉及到 1 对 1 的关联查询。
foreignkey 指定外键、association_foreignkey指定当前表中和外键关联的字段(不写的话表 示 Id)
//文章分类的结构体
type ArticleCate struct {
Id int `json:"id"`
Title string `json:"title"`
State int `json:"state"`
}
func (ArticleCate) TableName() string {
return "article_cate"
}
//文章的结构体
type Article struct {
Id int `json:"id"`
Title string `json:"title"`
Description int `json:"description"`
CateId string `json:"cate_id"`
State int `json:"state"`
ArticleCate ArticleCate `gorm:"foreignkey:Id;association_foreignkey:CateId"`
}
func (Article) TableName() string {
return "article"
}
1、查询所有文章以及文章对应的分类信息:
var list []models.Article
models.DB.Preload("ArticleCate").Find(&list)
2、查询所有文章以及文章对应的分类信息 指定条件:
var list []models.Article
models.DB.Preload("ArticleCate").Where("id>=?", 4).Find(&list)
2、一对多
1 对多在实际项目中用的非常多
比如一个点餐系统:有菜品分类、有菜品。
菜品分类和菜品之间就是一对多的关系
订单表和订单商品表:订单表和订单商品表之间也是一对多的关系
如上图所示,一个分类下面有很多个文章,article_cate 和 article 之间是 1 对多的关系。
文章表中的 cate_id 保存着文章分类的 id。 如果我们想查询文章分类的时候获取分类下面的文章,这个时候就涉及到一对多的关联查询。
//文章的结构体
type Article struct {
Id int `json:"id"`
Title string `json:"title"`
Description int `json:"description"`
CateId string `json:"cate_id"`
State int `json:"state"`
}
func (Article) TableName() string {
return "article"
}
//文章分类结构体
type ArticleCate struct {
Id int `json:"id"`
Title string `json:"title"`
State int `json:"state"`
Article []Article `gorm:"FOREIGNKEY:CateID;ASSOCIATION_FOREIGNKEY:ID"` //1 对多
}
func (ArticleCate) TableName() string {
return "article_cate"
}
1、查找所有分类以及分类下面的文章信息:
var list []models.ArticleCate
models.DB.Preload("Article").Find(&list)
2、查找所有分类以及分类下面的文章信息 指定条件:
var list []models.ArticleCate
models.DB.Preload("Article").Where("id>0").Find(&list)
3、查找所有分类以及分类下面的文章信息 指定文章条件:
var list []models.ArticleCate
models.DB.Preload("Article",
func(db *gorm.DB) *gorm.DB {
return db.Order("sort DESC")
}).Order("sort desc").Find(&access)
3、多对多
如果想根据课程获取选学本门课程的学生,这个时候就在 Lesson 里面关联 Student
Lesson 课程模型
import (
_ "github.com/jinzhu/gorm"
)
//Lesson 的结构体
type Lesson struct {
Id int `json:"id"`
Name string `json:"name"`
Student []*Student `gorm:"many2many:lesson_student"`
}
func (Lesson) TableName() string {
return "lesson"
}
如果想根学生信息获取当前学生选修的课程,这个时候就在 Student 中关联 Lesson
Student 模型
import (
_ "github.com/jinzhu/gorm"
)
//Student的结构体
type Student struct {
Id int
Number string
Password string
ClassId int
Name string
Lesson []*Lesson `gorm:"many2many:lesson_student"`
}
func (Student) TableName() string {
return "student"
}
LessonStudent 模型 (多对多的中间件表不需要进行额外设置)
//LessonStudent 的结构体
type LessonStudent struct {
LessonId int
StudentId int
}
func (LessonStudent) TableName() string {
return "lesson_student"
}
1、 查询所有学生选修课程的信息
student := []models.Student{}
models.DB.Preload("Lesson").Find(&student)
2、 查询 Id=2 的学生选修课程的信息
student := models.Student{}
models.DB.Preload("Lesson").Where("Id=?",2).Find(&student)
3、 查询课程被哪些学生选修
lession := []models.Lesson{}
models.DB.Preload("Student").Find(&lession)
4、关联查询指定子集的筛选条件
access := []models.Access{}
models.DB.Preload("AccessItem", "status=1").Order("sort desc").Where("module_id=?", 0).Fin d(&access)
5、自定义预加载 SQL
你可以通过传入 func(db *gorm.DB) *gorm.DB 来自定义预加载
models.DB.Preload("Orders",
func(db *gorm.DB) *gorm.DB {
return db.Order("orders.amount DESC")
}).Find(&users)
五、原生 SQL
1、使用原生 sql 删除 user 表中的一条数据
user := models.DB.Exec("delete from user where id=?", 3)
fmt.Println(user.RowsAffected)
2、使用原生 sql 修改 user 表中的一条数据
user := models.DB.Exec("update user set username=? where id=2", "哈哈")
fmt.Println(user.RowsAffected)
3、查询 uid=2 的数据
var user models.User
models.DB.Raw("SELECT * FROM user WHERE id = ?", 2).Scan(&user)
4、查询 User 表中所有的数据
var users []models.User
models.DB.Raw("SELECT * FROM user").Scan(&users)
5、统计 user 表的数量
var count int
row := models.DB.Raw("SELECT count(1) FROM user").Row()
row.Scan(&count)
六、GORM 中使用事务
GORM 默认会将单个的 create, update, delete 操作封装在事务内进行处理,以确保数据的完 整性。
如果你想把多个 create, update, delete 操作作为一个原子操作,Transaction 就是用来完成 这个的。
1、事务
要在事务中执行一系列操作,通常您可以参照下面的流程来执行。
func CreateAnimals(db *gorm.DB) error {
return db.Transaction(func(tx *gorm.DB) error {
// 在事务中做一些数据库操作 (这里应该使用 'tx' ,而不是 'db')
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
// 返回任意 err ,整个事务都会 rollback
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
return err
}
// 返回 nil ,事务会 commit
return nil
})
}
2、手动控制的事务
// 开始事务
tx := db.Begin()
// 在事务中做一些数据库操作 (这里应该使用 'tx' ,而不是 'db')
tx.Create(...)
// ...
// 有错误时,手动调用事务的 Rollback()
tx.Rollback()
// 无错误时,手动调用事务的 Commit()
tx.Commit()
3、事务具体的例子
type Bank struct {
Id int
Balance int
}
func TransferAccounts(db *gorm.DB) error {
return db.Transaction(func(tx *gorm.DB) error {
// 在事务中做一些数据库操作 (这里应该使用 'tx' ,而不是 'db')
//A账户减去 100
u1 := Bank{Id: 1}
tx.Find(&u1)
u1.Balance = u1.Balance - 100
if err := tx.Save(&u1).Error; err != nil {
return err
}
// B账户增加 100
u2 := Bank{Id: 2}
tx.Find(&u2)
u2.Balance = u2.Balance + 100
if err := tx.Save(&u2).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
}