GORM实现数据库连接和增删改查|青训营

102 阅读4分钟

基本介绍

gorm是一个在Go中的ORM(对象关系映射)库。ORM是一种技术,它将数据库表中的数据映射到面向对象的模型中,从而简化了数据库操作。

gorm支持多种常见的数据库系统,包括MySQL、SQLSever、PostgreSQL、SQLite。这使得开发者可以在不同的项目中使用不同的数据库系统,而无需更改代码。

gorm实践

这里使用MySQL数据库进行实践。使用了MySQL数据库的官方数据库案例

0b73291afa3f906dbcc0f4c3ca183b9.png

数据库结构如下

98e313deead36b988be42f326082cca.png

数据连接

新建项目,打开终端,安装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")
}

image.png

例如上图本地下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表为例进行数据查询:

image.pngimage.png

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()