基本介绍
gorm是一个在Go中的ORM(对象关系映射)库。ORM是一种技术,它将数据库表中的数据映射到面向对象的模型中,从而简化了数据库操作。
gorm支持多种常见的数据库系统,包括MySQL、SQLSever、PostgreSQL、SQLite。这使得开发者可以在不同的项目中使用不同的数据库系统,而无需更改代码。
gorm实践
这里使用MySQL数据库进行实践。使用了MySQL数据库的官方数据库案例。
数据库结构如下
数据连接
新建项目,打开终端,安装gorm。(安装前项目中需要有go.mod)
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
导入必要的包
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
打开数据库
dsn := “user:password@tcp(ip:port)/dbName?charset=utf8mb4&parseTime=True&loc=Local”
db, err := gorm.Open(mysql.Open(dns), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
例如上图本地下world数据库的连接。用户名user为root、ip为localhost、port为3306、数据库名dbName我们使用的之前说过的官方mysql数据库示例world。
dsn := “root:本地数据库密码@tcp(127.0.0.1:3306)/world?charset=utf8mb4&parseTime=True&loc=Local”
db, err := gorm.Open(mysql.Open(dns), &gorm.Config{})
查询
创建一个结构体和数据表字段对应
world中有三张表city、country和countrylanguage。这里以world中city表为例进行数据查询:
type City struct {
ID int `gorm:"primaryKey"`
Name string `gorm:"not null;default:''"`
CountryCode string `gorm:"column:CountryCode;not null;default:''"`
District string `gorm:"not null;default:''"`
Population int `gorm:"not null;default:0"`
}
// 设置 `City` 的表名为 `city`
func (City) TableName() string {
return "city"
}
注意,需要设置func (City) TableName(),否则查询时将报错Error 1146 (42S02): Table 'world.cities' doesn't exist。GORM默认将struct name转为snake_cases为表名,对于 struct City,其表名是cities约定俗成的。GORM默认struct字段名field name转为snake_case单数形式为表字段名。可以通过设置gorm:"column:..."来使用别的转换规则,上面例子中的CountryCode只需要使用gorm:"column:CountryCode"标签定义为CountryCode字段指定表字段名,其他使用默认值即可。
执行查询操作
多个结果,因为city为list可全部接收,如果city长度有限,则只接收有限个结果。
var city []City
db.Find(&city)
查询特定情况:主键检索。
db.Find(&city, 1)
db.Find(&users,[]int{1,2,3})
查询特定情况:where case检索。
db.Find(&city, "CountryCode=? AND Population>?", "AFG", "150000") //通过find内联。
db.Where("CountryCode=? AND Population>?", "AFG", "150000").Find(&city)
db.Where(map[string]interface{}{"CountryCode": "AFG"}).Find(&city)
db.Where(&City{CountryCode: "AFG"}).Find(&city)
where NOT 检索
db.Not("CountryCode =?", "USA").Find(&city)
db.Not(map[string]interface{}{"CountryCode": []string{"NLD", "IND", "CHN", "USA"}}).Find(&city)
db.Not(City{CountryCode: "USA", District: "Guangdong"}).Find(&city)
Or条件:
db.Where("CountryCode =?", "USA").Or(City{District: "Guangdong"}).Find(&city)
选择特定的column:
db.Select("ID", "Population").Find(&city)
排序order
db.Order("Population").Where(City{District: "Guangdong"}).Find(&city)
db.Order("CountryCode, Population desc").Where("Population > ?", "5000000").Find(&city)
db.Order("CountryCode").Order("Population desc").Where("Population > ?", "5000000").Find(&city)
不规则排序
db.Clauses(clause.OrderBy{
Expression: clause.Expr{SQL: "FIELD(列名,?)", Vars: []interface{}{[]int{值1, 值2, ...}}, WithoutParentheses: true},
}).Find(&表struct)
// SELECT * FROM 表 ORDER BY FIELD(列名,值1, 值2, ...)
别名,子查询,scan, group by、Having,AVG、MAX、MIN。
下例查询所有平均地区人口比中国平均地区人口高的国家。
type Result struct {
CountryCode string
Avg float32
}
var result []Result
subQuery := db.Select("AVG(Population)").Where("CountryCode = ?", "CHN").Table("city")
db.Table("city").Select("CountryCode, AVG(Population) as avg").Group("CountryCode").Having("avg > (?)", subQuery).Scan(&result)
/*
SELECT CountryCode, AVG(Population) as avg
FROM 'city'
GROUP BY 'CountryCode'
HAVING avg > (SELECT AVG(Population) FROM 'city' WHERE CountryCode = 'CHN')
*/
增添
使用db.Create()实现记录的创建。注意外键的关联和主键的唯一,否则会报错。
incity := City{ID: 5000, Name: "swwhome", CountryCode: "CHN", District: "swwdis", Population: 3}
db.Create(&incity) //{5000 swwhome CHN swwdis 3}
选择字段添加 Select()
// INSERT INTO `city` (`ID`,`Name`,`CountryCode`) VALUES (5005, ""swwhome, "CHN")
incity := City{ID: 5005, Name: "swwhome", CountryCode: "CHN", District: "swwdis", Population: 3}
db.Select("ID", "Name", "CountryCode").Create(&incity) //{5005 swwhome CHN '' 0}
忽略被选择字段,创建其他字段 Omit()
incity := City{ID: 5006, Name: "swwhome", CountryCode: "CHN", District: "swwdis", Population: 3}
db.Omit("District").Create(&incity) //{5006 swwhome CHN '' 3}
批量创建。db.CreateInBatches(interface, size)能够实现分批创建,注意数据会全写进去,只是分批写进去。
incitys := []City{{ID: 4088, Name: "swwhome", CountryCode: "CHN", District: "swwdis", Population: 3},
{ID: 4089, Name: "swwhome", CountryCode: "CHN", District: "swwdis", Population: 3},
{ID: 4090, Name: "swwhome", CountryCode: "CHN", District: "swwdis", Population: 3},
{ID: 4091, Name: "swwhome", CountryCode: "CHN", District: "swwdis", Population: 3}}
db.Create(&incitys)
db.CreateInBatches(&incitys, 2) //两个两个创建
更新
把id为4086的数据的Population属性从3改为4
方法一Save()
var city City
db.Find(&city, 4086)
city.Population = 4
db.Save(&city)
方法二Update()
db.Find(&city, 4086)
fmt.Println(city)
db.Model(&city).Update("Population", 4)
db.Find(&city, 4086)
fmt.Println(city)
方法三Updates()
db.Find(&city, 4086)
db.Model(&city).Updates(map[string]interface{}{"District": "swwDistrict", "Population": 4})
批量条件更新,把所有Population属性为3的改为4
db.Table("city").Where("Population=?", 3).Updates(map[string]interface{}{"Population": 4})
删除
通常使用Delete()进行删除操作,这是一种软删除。拥有软删除能力的模型调用 Delete 时,记录不会更新进数据库。但GORM会将 DeletedAt 置为当前时间, 并且不能再通过普通的查询方法找到该记录。
db.Find(&city, 4090)
db.Delete(&city)
通过主键删除
db.Delete(&City{}, []int{4090, 4091})
db.Delete(&City{}, 4090)
条件删除
db.Where("Population=?", 4).Delete(&City{})
物理删除不知道为什么不起作用,mark一下
查找软删除记录
db.Unscoped().Where("Population<?", 5).Find(&citys)物理删除需要用
Unscoped()db.Unscoped().Delete()