赞
踩
Object-Relationl Mapping,即对象关系映射,这里的Relationl指的是关系型数据库
它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。
1.Golang写的,GitHub上活跃度很高的orm库
2.特点:
3.安装
go get github.com/jinzhu/gorm
4.官方文档:http://gorm.book.jasperxu.com/
对数据库,表,字段等的操作,这里指的DDl不包括数据库
// 引入gorm及驱动包
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
db,db_err := gorm.Open("mysql","root:Qazwsx123@[tcp](localhost:3306)/test_gorm?charset=utf8&parseTime=True&loc=Local")
if err!= nil{
panic(err)
}
defer db.Close() // 关闭连接
// loc=Local:表示根据本地时区走
// parseTime:处理time.Time
1.创建表
type User struct {
Id int
Name string
Age int
}
// 创建表
db.Table("user").CreateTable(&User{}) // 指定表名
db.CreateTable(&User{}) // 不指定表名,模型后面会加个s
2.删除表
db.DropTable(&User{}) // 使用模型名
db.DropTable("users") // 直接使用表名
db.DropTableIfExists(&User{}) // 先判断是否存在再删除,可以接受多个参数,模型和字符串都可以
3.检查表是否存在
is_has := db.HasTable(&User{}) // 使用模型
is_has := db.HasTable("users") // 使用表名
fmt.Println(is_has)
1.修改列
// 修改模型`User`的description列的数据类型为`text`
db.Model(&User{}).ModifyColumn("description", "text")
2.删除列
// 删除模型`User`的description列
db.Model(&User{}).DropColumn("description")
3.添加外键
db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT")
// 第一个参数: 外键字段
// 第二个参数 : 外键表(字段)
// 第三个参数 : ONDELETE
// 第四个参数 : ONUPDATE
4.索引
// 为`name`, `age`列添加索引`idx_user_name_age`
db.Model(&User{}).AddIndex("idx_user_name_age", "name", "age")
// 为多列添加唯一索引
db.Model(&User{}).AddUniqueIndex("idx_user_name_age", "name", "age")
1.自动迁移
自动迁移仅仅会创建表,添加缺少列和索引,并且不会改变现有列的类型或删除未使用的列以保护数据
db.AutoMigrate(&User{})
db.AutoMigrate(&User{}, &Product{}, &Order{})
// 创建表时添加表后缀
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
是对数据的操作
1.增
db.Create(&User{Id:1,Name:"hallen",Age:18})
2.查
var user User
db.First(&user,1) // 默认使用id字段
fmt.Println(user)
db.First(&user,"name=?","hallen") // 查询指定字段
fmt.Println(user)
3.改:先查再改
var user User
db.First(&user,1) // 默认使用id字段
db.Model(&user).Update("age",20) // 这里的Model是查询出来的结构体对象user,不是模型
4.删:先查再改
var user User
db.First(&user,1) // 默认使用id字段
db.Delete(&user)
用作数据库数据转换和自动建表
// 在默认表名前加sys_前缀
gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string {
return "sys_" + defaultTableName;
}
自定义表名:
func (模型) TableName() string{
return "新的表名"
}
gorm:"column:beast_id"
基本模型定义gorm.Model,包括字段ID,CreatedAt,UpdatedAt,DeletedAt
只需要在自己的模型中指定gorm.Model匿名字段,即可使用上面的四个字段
// 添加字段 `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`
type User struct {
gorm.Model
Name string
}
ID:主键自增长
CreatedAt:用于存储记录的创建时间
UpdatedAt:用于存储记录的修改时间
DeletedAt:用于存储记录的删除时间
type UserInfo struct {
Id int `gorm:"primary_key"`
Name string `gorm:"index"`
Age int
}
gorm:"-"
gorm:"primary_key"
gorm:"AUTO_INCREMENT"
gorm:"not null"
gorm:"index"
gorm:"index:idx_name_code"
gorm:"unique_index"
gorm:"unique"
gorm:"column:user_name"
gorm:"size:64"
gorm:"type:varchar(100)"
// 不推荐直接改类型default:'galeone'
默认值多个属性值之间用分号分隔(英文的;):gorm:"size:64;not null"
1.属于
// UserProfile属于User,外键是在UserProfile模型,外键字段为:UId type User struct { Id int Name string Age int Addr string } type UserProfile struct { Id int Pic string CPic string Phone string User User `gorm:"ForeignKey:UId;AssociationForeignKey:Id"` // 关联关系 //UserID int // 默认关联字段为Id UId int // uid } 注: // 外键默认使用UserID,如果不指定外键,则使用默认的外键字段, // 默认关联ID,通过AssociationForeignKey指定关联字段
2.包含
// UserProfile 包含一个 User, 外键在User模型,外键字段为:PId type User struct { Id int Name string Age int Addr string PId int } type UserProfile struct { Id int Pic string CPic string Phone string User User `gorm:"ForeignKey:PId;AssociationForeignKey:Id"` // 关联关系 }
属于:关系和外键在同一方,有关系的那一方属于另外一个模型
包含:关系和外键不在同一方,有关系的那一方包含另外一个有外键的模型
type User2 struct { Id int Name string Age int Addr string Articles []Article `gorm:"ForeignKey:UId;AssociationForeignKey:Id"` } type Article struct { Id int Title string Content string Desc string // 外键 UId int }
type Article2 struct {
AId int `gorm:"primary_key:true"`
Title string
Content string
Desc string
Tags []Tag `gorm:"many2many:Article2s2Tags"` // ;ForeignKey:AId;AssociationForeignKey:TId
}
type Tag struct {
TId int `gorm:"primary_key:true"`
Name string
Desc string
}
创建/更新时不会保存关联:gorm:"save_associations:false"
user_profile := relate_tables.UserProfile{
Pic:"1.jpg",CPic:"2.jpg",Phone:"xxx",
User:relate_tables.User{Name:"hallen",Age:18,Addr:"xxx"},
}
result := db.Create(&user_profile)
默认是关联创建或更新的
创建/更新时不会保存关联:`gorm:"save_associations:false"`
1.第一种方式:Association
// 先查询出来,在根据Association属性值关联查询,Association的值为关联表的模型名称
var u_profile relate_tables.UserProfile
db.First(&u_profile,1)
fmt.Println(u_profile)
db.Model(&u_profile).Association("User").Find(&u_profile.User)
2.第二种方式:Preload
var u_profile relate_tables.UserProfile
db.Debug().Preload("User").First(&u_profile,1) // 关系名
fmt.Println(u_profile)
3.第三种方式:Related
var user_profile3 relate_tables.UserProfile
db.First(&user_profile3,1)
var user relate_tables.User
db.Model(&user_profile3).Related(&user,"User")
// 先关联查询出来,再更新关联表中的字段
var u_profile1 relate_tables.UserProfile
db.Debug().Preload("User").First(&u_profile1,1)
fmt.Println(u_profile1)
db.Model(&u_profile1.User).Update("p_id",2)
// 通过主表删除关联表中的
var u_profile2 relate_tables.UserProfile
db.Debug().Preload("User").First(&u_profile2,1)
db.Delete(&u_profile2.User)
user := relate_tables.User2{ Name:"halle", Age:18, Addr:"xxx", Articles:[]relate_tables.Article{{ Title:"标题测试", Content:"内容测试", Desc:"描述测试", },{ Title:"标题测试2", Content:"内容测试2", Desc:"描述测试2", }, }, } ret := db.Create(&user) fmt.Println(ret.RowsAffected) fmt.Println(ret.Error)
1.Preload
var user2 relate_tables.User2
db.Preload("Articles").Find(&user2,2) // 关系名
fmt.Println(user2)
2.Association
var user2 relate_tables.User2
db.First(&user2,2)
db.Model(&user2).Association("Articles").Find(&user2.Articles)
fmt.Println(user2)
3.Related
var user2 relate_tables.User2
db.First(&user2,1)
var articles []relate_table.Article
db.Model(&user2).Related(&articles, "Articles") // 关系名称
// 先查询
var user2 relate_tables.User2
db.Preload("Articles").Find(&user2,2) // 关系名
// 再更新,更新指定条件,不然会把所有满足条件的都更新
db.Model(&user2.Articles).Where("title=? and uid=?","标题测试2",2).Update("uid",3) // name和uid限制条件
// update只能更新一个字段,如果想同时更新多个字段,使用save,后面会讲
// 先查询
var user2 relate_tables.User2
db.Preload("Articles").Find(&user2,2) // 关系名
// 再删除,删除要指定条件,不然会把所有满足条件的都删除
db.Delete(&user2.Articles,"title=? and uid=?","标题测试3",3)
// 或者使用where
db.Where("title=? and uid=?","标题测试3",3).Delete(&user2.Articles)
// 一篇文章有多个帖子 article := relate_tables.Article2{ Title:"测试多对多标题1", Content:"测试多对多内容1", Desc:"测试多对多描述1", Tags:[]relate_tables.Tag{{ Name:"django", Desc:"django标签", }, { Name:"python", Desc:"python标签", }, }, } ret := db.Create(&article) fmt.Println(ret.Error)
或者先插入单表的,再关联插入
tag := relate_tables.Tag{Name:"beego2",Desc:"beego2标签"}
db.Create(&tag)
article := relate_tables.Article2{
Title:"测试多对多标题4",
Content:"测试多对多内容5",
Desc:"测试多对多描述5",
Tags:[]relate_tables.Tag{
tag,
},
}
db.Create(&article) // 这里不会重复插入前面create的数据
1.Preload
var article relate_tables.Article2
db.Preload("Tags").Find(&article,1)
fmt.Println(article)
2.Association
var article relate_tables.Article2
db.First(&article,1)
db.Model(&article).Association("Tags").Find(&article.Tags)
fmt.Println(article)
var article relate_tables.Article2
db.First(&article,1)
var tags []relate_table.Tag
db.Model(&article).Related(&tags, "Tags")
// 先查询
var article relate_tables.Article2
db.Preload("Tags").Find(&article,1)
// 再更新,记得加条件
db.Model(&article.Tags).Where("name = ?","beego").Update("name","xxx")
// 先查询
var article relate_tables.Article2
db.Preload("Tags").Find(&article,1)
// 再删除,记得加条件
db.Where("name = ?","xxx").Delete(&article.Tags)
// 按照主键顺序的第一条记录,(主键升序)
var user model.User
result := db.First(&user)
fmt.Println(user)
result.RowsAffected // 返回找到的记录数
result.Error // returns error
// sql语句:SELECT * FROM users ORDER BY id LIMIT 1;
// 未找到 user,则根据给定条件创建一条新纪录
var user model.User
db.FirstOrCreate(&user, User{Name: "hallen"})
// 找到了 `name` = `hallen` 的 user
db.Where(User{Name: "hallen"}).FirstOrCreate(&user)
// 获取最后一条记录(主键降序)
var user model.User
// 按照主键顺序的最后一条记录
db.Last(&user)
fmt.Println(user)
// sql语句:SELECT * FROM users ORDER BY id DESC LIMIT 1;
// 获取一条记录,没有指定排序字段
var user model.User
db.Take(&user)
// sql语句:SELECT * FROM users LIMIT 1;
var user model.User // 所有记录 db.Find(&users, []int{1,2,3}) // sql语句:// SELECT * FROM users WHERE id IN (1,2,3); result := db.Find(&users) // sql语句:SELECT * FROM users; // 根据指定条件查询 db.Find(&user, "name = ?", "hallen") //或者结合where db.Where("name = ?", "hallen").Find(&users) // sql语句:SELECT * FROM users WHERE name = 'hallen'; db.Where("name LIKE ?", "%ha%").Find(&users) // sql语句:SELECT * FROM users WHERE name LIKE '%hal%';
var user model.User // 根据条件查询得到满足条件的第一条记录 db.Where("role_id = ?", "2").First(&user) fmt.Println(user) var users []model.User // 根据条件查询得到满足条件的所有记录 db.Where("user_id = ?", "1").Find(&users) fmt.Println(users) // like模糊查询 db.Where("role_id like ?", "%2").Find(&users) fmt.Println(users) db.Where("updated_at > ?", "2019-02-08 18:08:27").Find(&users) fmt.Println(users) // struct结构查询条件 db.Where(&DqmUserRole{RoleId: "1,2", UserId: "1"}).First(&user) fmt.Println(user) 条件: = LIKE IN:Where("name IN ?", []string{"hallen", "hallen2"}) AND:Where("name = ? AND age >= ?", "jinzhu", "22") Time:Where("updated_at > ?", lastWeek) BETWEEN:Where("created_at BETWEEN ? AND ?", lastWeek, today)
指定要从数据库检索的字段,默认情况下,将选择所有字段;
db.Select("name, age").Find(&users)
SELECT name, age FROM users;
db.Select([]string{"name", "age"}).Find(&users)
SELECT name, age FROM users;
db.Table("users").Select("COALESCE(age,?)", 42).Rows() // COALESCE:聚合
SELECT COALESCE(age,'42') FROM users;
1.插入单条
user := models.User{Name:"李四",Age:18,Addr:"xxx",Pic:"/static/upload/pic111.jpg",Phone:"13411232312"}
result := db.Create(&user)
user.ID // 返回插入数据的主键
result.Error // 返回 error
result.RowsAffected // 返回插入记录的条数
2.批量插入:暂不支持
user4 := []relate_tables.User{
{
Name:"hallen8",
Age:18,
Addr:"xxx",
},
{
Name:"hallen9",
Age:18,
Addr:"xxx",
},
}
db.Create(&user4) // 这种方式不支持
var user model.User
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
var users []model.User
db.Where("active = ?", true).find(&users).Update("name", "hello")
db.Where("active = ?", true).find(&users).Updates(User{Name: "hello", Age: 18})
// update也可以使用map:map[string]interface{}{"name": "hello", "age": 18}
// 也可以使用save更新
db.Delete(&user,1)
// 批量删除
db.Where("age = ?", 20).Delete(&User{})
// 也就是逻辑删除 // gorm.Model 将DeletedAt 字段设置为当前时间 // 需要再模型中指定 type User struct { ID int Deleted `gorm:"DeletedAt"` // 如果设置了所有的删除都将是逻辑删除 Name string } // 在查询时会忽略被软删除的记录 db.Where("age = 20").Find(&user) // SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL; // 查询逻辑删除的数据 db.Unscoped().Where("age = 20").Find(&users) // SELECT * FROM users WHERE age = 20; // 想要物理删除的办法 db.Unscoped().Delete(&user)
var user model.User
db.Not(User{Name: "hallen", Age: 18}).First(&user)
// SELECT * FROM `users` WHERE (`users`.`name` <> 'hallen6') AND (`users`.`age` <> 19);
var users []model.User
db.Where("name = 'hallen'").Or(User{Name: "hallen2", Age: 18}).Find(&users)
// SELECT * FROM users WHERE name = 'hallen' OR (name = 'jinzhu 2' AND age = 18);
var users []model.User
db.Order("age desc").Find(&users) // 注意这里的order要在find前面,否则不生效
fmt.Println(users)
// SELECT * FROM users ORDER BY age desc;
默认为asc
Limit 指定获取记录的最大数量 Offset 指定在开始返回记录之前要跳过的记录数量
var users []model.User
db.Limit(3).Find(&users) // 三条
// SELECT * FROM users LIMIT 3;
db.Limit(10).Offset(5).Find(&users) // 从5开始的10条数据
// SELECT * FROM users OFFSET 5 LIMIT 10;
将结果扫描到另一个结构中。
type Result struct {
Id int64
}
var results []Result
db.Select("id").Where("user_id in (?)", []string{"1", "2"}).Find(&dqmUserRole20).Scan(&results)
fmt.Println(results)
获取模型的记录数
db.Where("name = ?", "hallen").Find(&users).Count(&count)
// SELECT count(*) FROM users WHERE name = 'jinzhu'
db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
// SELECT count(*) FROM users WHERE name = 'jinzhu'; (count)
db.Table("deleted_users").Count(&count)
// SELECT count(*) FROM deleted_users;
GROUP BY语句用来与聚合函数(aggregate functions such as COUNT, SUM, AVG, MIN, or MAX.)联合使用,只返回一个单个值
HAVING语句通常与GROUP BY语句联合使用,用来过滤由GROUP BY语句返回的记录集。
HAVING语句的存在弥补了WHERE关键字不能与聚合函数联合使用的不足
type result struct {
Date time.Time
Total int
}
db.Select("name, count(*)").Group("name").Find(&result)
// select name,count(*) FROM users GROUP BY `age`
db.Select("name, count(*)").Group("name").Having("add = ?","xxx").Find(&result)
// select name,count(*) FROM users GROUP BY `age` 后面不能用where限制条件,只能使用having
// select name,count(age) FROM users GROUP BY `age` HAVING addr='xxx'
db.Distinct("name", "age").Order("name, age desc").Find(&results)
left join … on …
right join … on …
db.Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
FirstOrInit:获取第一个匹配的记录,或者使用给定的条件初始化一个新的记录(仅适用于struct,map条件)
Attrs:如果没有找到记录,则使用Attrs中的数据来初始化一条记录:
var user model.User
// 查不到该条记录,则使用attrs值替换,// 查到记录,则使用数据库中的值
db.Where(User{Name:"hallen"}).Attrs(User{Name:"hallen",Age:18}).FirstOrInit(&user)
Assign:不管是否找的到,最终返回结构中都将带上Assign指定的参数,有则代替,没有则添加
var user model.User
// 不管是否找到对应记录,使用Assign值替代查询到的值
db.Where("id = ?", "1").Assign(model.User{Id: "15"}).FirstOrInit(&user)
Pluck 用于从数据库查询单个列,并将结果扫描到切片。如果您想要查询多列,您应该使用 Select 和 Scan
var ages []int64
db.Find(&users).Pluck("age", &ages)
var names []string
db.Model(&User{}).Pluck("name", &names)
// 超过一列的查询,应该使用 `Scan` 或者 `Find`,例如:
db.Select("name", "age").Scan(&users)
db.Select("name", "age").Find(&users)
Scopes 允许你指定常用的查询,可以在调用方法时引用这些查询
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB { return db.Where("amount > ?", 1000) } func PaidWithCreditCard(db *gorm.DB) *gorm.DB { return db.Where("pay_mode_sign = ?", "C") } func PaidWithCod(db *gorm.DB) *gorm.DB { return db.Where("pay_mode_sign = ?", "C") } func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB { return func (db *gorm.DB) *gorm.DB { return db.Where("status IN (?)", status) } } db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders) // 查找所有金额大于 1000 的信用卡订单 db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders) // 查找所有金额大于 1000 的货到付款订单 db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders) // 查找所有金额大于 1000 且已付款或已发货的订单
Gorm有内置的日志记录器支持,默认情况下,它会打印发生的错误。
// 启用Logger,显示详细日志
db.LogMode(true)
// 也可以查看单语句的信息
db.Debug().Where("name = ?", "hallen").First(&User{})
如果在执行SQL查询的时候,出现错误,GORM 会将错误信息保存到 *gorm.DB 的Error字段,我们只要检测Error字段就可以知道是否存在错误。
err := db.Where("name = ?", "tizi365").First(&user).Error
if err != nil {
// 错误处理
}
// 或者
result := db.Where("name = ?", "jinzhu").First(&user)
if result.Error != nil {
// 错误处理
}
通过GetErrors获取错误列表
errors := db.First(&user).Limit(10).Find(&users).GetErrors()
fmt.Println(len(errors)) // 打印错误数量
// 遍历错误内容
for _, err := range errors {
fmt.Println(err)
}
gorm.错误类型进行判断,如gorm.ErrRecordNotFound
1.RecordNotFound:查询不到数据,不适用于切片
gorm.IsRecordNotFoundError(err) {
// 没有查询到数据
}
2.ErrInvalidSQL:无效sql
3.ErrInvalidTransaction:事务有错
4.ErrCantStartTransaction:无法开启事务,出现在使用Begin的情况下
5.ErrUnaddressable:使用不可寻址的值,传递的指针值不对
用 db.Begin() 声明开启事务,结束的时候调用 tx.Commit(),异常的时候调用 tx.Rollback()
ct := db.Begin() // 开启事务
ret3 := ct.Commit()
if ret3.Error != nil {
ct.Rollback() // 回滚
}
联合主键、组合主键
type Product struct {
ID int `gorm:"primary_key"`
ERPID int `gorm:"primary_key"`
}
var users []relate_tables.User
db.Raw("select * from users").Find(&users)
db.Exec("insert into users (name,age) values(?,?)","hallen222",111)
db.Exec("update users set name = ? where id = ?","hallen111",1)
db.Exec("delete from users where id = ?",1)
row,_ := db.Raw("select * from users").Row()
row,_ := db.Raw("select * from users").Rows()
{
"host":"localhost",
"port":"3306",
"user_name":"root",
"password":"Qazwsx123",
"database":"gin_project",
"logo_mode":true
}
package data_source import ( "os" "io/ioutil" "encoding/json" ) type MysqlConf struct { Host string `json:"host"` Port string `json:"port"` UserName string `json:"user_name"` Password string `json:"password"` DataBase string `json:"database"` LogoMode bool `json:"logo_mode"` } func LoadMysqlConf() *MysqlConf { mysql_conf := MysqlConf{} file,err := os.Open("conf/mysql.json") if err != nil { panic(err) } defer file.Close() byte_data,err2 := ioutil.ReadAll(file) if err2 != nil { panic(err2) } err3 := json.Unmarshal(byte_data,&mysql_conf) if err3 != nil { panic(err3) } return &mysql_conf }
package data_source import ( "github.com/jinzhu/gorm" "gin_project/models" _ "github.com/jinzhu/gorm/dialects/mysql" "fmt" ) var Db *gorm.DB var err error func init() { mysql_conf := LoadMysqlConf() logo_mode := mysql_conf.LogoMode data_source := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=true&loc=Local", mysql_conf.UserName, mysql_conf.Password, mysql_conf.Host, mysql_conf.Port, mysql_conf.DataBase, ) Db,err = gorm.Open("mysql",data_source) if err != nil { panic(err) } Db.LogMode(logo_mode) Db.DB().SetMaxOpenConns(100) // 最大连接数 Db.DB().SetMaxIdleConns(50) // 最大空闲数 Db.AutoMigrate(&models.User{}) }
代码示例抽取:
实现接口:D:\go\workspace\src\gin-vue-admin\common\datasource
base结构体:D:\go\workspace\src\gin-vue-admin\repository\BaseRepository.go
集成结构体:D:\go\workspace\src\gin-vue-admin\repository\UserRepository.go
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。