连接数据库
Mysql
- 导入gorm库和mysql驱动包
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
- 编写dsn连接数据库
dsn:="<user>:<password>@tcp(<ip such as 127.0.0.1>:<port such as 3306>)/<dbname>?charset=utf8mb4&parseTime=True&loc=Local"
db,err:=gorm.Open(mysql.Open(dsn))
Sqlite
- 导入gorm库和sqlite驱动
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
- 连接数据库
db,err:=gorm.Open(sqlite.Open("<DB filename such as hello.db>"))
gorm包的路径比较简单,gorm用得也比较多,其包路径最好记住,gorm相关包都位于gorm.io,一般使用gorm的话就是gorm.io/gorm,需要使用自动生成工具就导入gorm.io/gen,需要数据库驱动就导入gorm.io/driver/<数据库名字>。
迁移模型
迁移模型的主要作用就是根据你编写的go结构体生成对应的数据库表结构。
下面是一个简单的例子,用于根据User结构体生成对应的数据库表users.为了简单和方便,我们使用sqlite数据库作为示范。
import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"time"
)
type User struct {
ID uint64
Username string
Password string
CreateAt time.Time
UpdateAt time.Time
}
func main() {
db, err := gorm.Open(sqlite.Open("test.db"))
if err != nil {
fmt.Println(err)
}
db.AutoMigrate(User{})
}
其中,AutoMigrate方法会进行模型的迁移。我们执行上面这段代码,会发现当前项目下会创建一个test.db的文件。
我们通过安装的sqlite3数据库打开这个文件。在确定操作系统上已经安装sqlite3数据库后执行以下命令打开数据库:
sqlite3 test.db
接着输入以下命令:
.tables;
可以看到以下输出:
我们发现gorm已经在sqlite中自动创建了一个名叫users的表,我们发现它给User结构体自动加了个s用于表示一个复数的数据库名。
我们接着输入以下命名:
.schema
可以看到以下输出:
我们发现gorm自动创建了一张users表,并且其字段和我们定义的结构体一样,并将我们的id作为了主键。
添加记录
前面我们已经创建了一个数据库,并且通过自动迁移的功能根据User结构体自动创建了一张users表,现在让我们来向数据库中添加一条记录吧!
我们在原来的主函数的末尾添加以下代码:
if tx:=db.Create(User{Username: "tom", Password: "123456"});tx.Error!=nil{
fmt.Println(tx.Error)
}
运行它,我们发现报错了:
怎么回事呢?
原来是因为我们传给Create的User不是地址。
好的,我们修改下代码成下面的样子:
if tx := db.Create(&User{Username: "tom", Password: "123456"}); tx.Error != nil {
fmt.Println(tx.Error)
}
接着运行它,我们发现没有什么问题,应该是成功了,我们在命令行窗口输入以下语句,查看下数据库记录:
记录添加成功了,我们修改下Create函数里面的内容在执行下,接着查看下数据库:
我们发现,当我们不指定ID的值时,它会进行自增。但是CreateAt和UpdateAt的零值不是我们想要的,怎么办呢?
我们尝试给sqlite添加一个触发器,在数据库层面完成数据的自动更新:
CREATE TRIGGER update_timestamp
AFTER INSERT ON users
FOR EACH ROW
BEGIN
UPDATE users
SET create_at= DATETIME('now')
WHERE id = NEW.id;
END;
也可以在插入记录的时候指定CreateAt字段的值为time.Now(),或者你也可以给创建语句添加一个狗子,在创建成功后修改create_at和update_at的值,但是个人不太推荐,我们通过指定字段time.Now()值来添加它的时间,比如下面这样:
if tx := db.Create(&User{Username: "Thomas", Password: "7777", CreateAt: time.Now(), UpdateAt: time.Now()}); tx.Error != nil {
fmt.Println(tx.Error)
}
我们发现,成功添加上了时间:
删除记录
上面我们已经学会了如何通过gorm往数据库中添加记录,接下来我们尝试删除前面添加的某条记录:
if tx:=db.Delete(User{},"id = ?",2);tx.Error!=nil{
fmt.Println(tx.Error)
}
上面Delete语句第一个参数需要指定数据模型,对应数据库表,后面接需要删除的记录的筛选条件,这里我们删除id为2的记录。
我们查看下数据库:
发现第2条记录成功删除了。
查找记录
前面我们都是通过sql语句查找数据库的记录,接下来我们通过gorm代码来查找数据库记录。
var data []User
if tx := db.Find(&data); tx.Error != nil {
fmt.Println(tx.Error)
}
fmt.Println(data)
我们发现,成功查找到了所有记录:
这里需要注意的是Find函数的第一个参数是一个地址,如果只是一个结构体的地址的话只会查找一条记录,如果是个切片的地址则会查找全部的记录,最后会将数据写入该地址。同时也能在Find的可选参数里面指定查询条件,这里省略了。
修改记录
下面我们尝试修改第一条记录中tom的密码位555.
if tx := db.Model(User{}).Where("id = ?", 1).Update("password", "555"); tx.Error != nil {
fmt.Println(tx.Error)
}
我们发现,它似乎变复杂了点,首先我们需要调用Model方法指定需要修改的表,接着我们需要通过Where语句进行筛选,后面的Update语句,指定需要更新的column和其更新后的值。在这里Where语句不能省略,如果去掉,gorm会拒绝执行。
实际上,我们还可以通过另外一种更简单的方式来更新数据库,那就是通过Updates方法,该方法使用的时候需要指定User的id,它会根据id去自动查找记录,然后将其他字段替换成你的结构体中的字段的值。
if tx := db.Updates(User{ID: 4, Username: "zhangsna", Password: "9999"}); tx.Error != nil {
fmt.Println(tx.Error)
}
比如上面的代码会将id为1的记录的其他字段替换成新的值,我们输出数据库表数据发现是这样的:
实际上,没有指定的CreatAt和UpdateAt都被忽略了,因为我们没有指定这两个字段值,则他们会被赋值为零值,而零值会被gorm忽略,在执行查询和更新操作时。
零值问题
使用结构体进行查询和更新,零值会被忽略,如果本身需要查询的值就是零值,则应该使用map进行查询和更新。
默认表名
gorm的默认表明是结构体的复数表示,但是你也可以指定表名,只需要给结构体添加一个TableNam()方法
func (u *User) TableName() string {
return "users"
}
事务
我们可以通过以下的方式开启一个事务
if err:=db.Transaction(func(tx *gorm.DB) error {
if err:=tx.Updates(User{ID: 1,Username: "lisi"}).Error;err!=nil{
return err
}
if err:=tx.Updates(User{ID: 3,Password: "123"}).Error;err!=nil{
return err
}
return nil
});err!=nil{{
fmt.Println(err)
}}
其中,当最后返回的结果是nil的时候事务会成功生效,当返回了任意的error,则事务会失败,gorm会自动进行回滚,在这里特别需要注意的是,一定要使用tx而不是db.