【GoCN酷Go推荐】postgresql ORM 框架 go-pg系列(一)
一、简介
1.1 go-pg是什么
官网描述为Golang ORM with focus on PostgreSQL features and performance
一个专注于 PostgreSQL 特性和性能的 Golang ORM。
如果你已经厌倦了手动写查询语句,那么可以尝试下go-pg框架来编写业务代码。
1.2 特性
基础类型: integers, floats, string, bool, time.Time, net.IP, net.IPNet. sql.NullBool, sql.NullString, sql.NullInt64, sql.NullFloat64 and pg.NullTime. sql.Scanner and sql/driver.Valuer interfaces. Structs, maps and arrays 默认序列化为json格式. PostgreSQL 多维数组使用 array tag and Array wrapper.(重点) hstore使用 hstore tag and Hstore wrapper.(重点) 组合类型Composite types. 默认情况下,所有结构字段都默认为空,并且零值(空字符串、0、零时间、空map映射或切片、nil 指针)被编组为 SQL“NULL”。 pg:",notnull"
标签用户添加SQL 非空约束。pg:",use_zero"
标签允许Go零值.Transactions. Prepared statements. Notifications using LISTEN
andNOTIFY
.拷贝数据 使用 COPY FROM
andCOPY TO
.Timeouts and canceling queries using context.Context. Automatic connection pooling with circuit breaker support. Queries retry on network errors. Working with models using ORM and SQL. Scanning variables using ORM and SQL. SelectOrInsert using on-conflict. INSERT ... ON CONFLICT DO UPDATE using ORM. Bulk/batch inserts, updates, and deletes. Common table expressions using WITH and WrapWith. CountEstimate using EXPLAIN
to get estimated number of matching rows.ORM 框架支持 has one, belongs to, has many, and many to many with composite/multi-column primary keys. Soft deletes. Creating tables from structs.从接口体来创建数据库表 ForEach that calls a function for each row returned by the query without loading all rows into the memory.
1.3 优缺点
1.优点
1.没有rows.Close去手动管理连接
在go-pg中,无需为每个打开的连接进行rows.Close操作。
2.go-pg比其他GORM性能更好
go-pg本身就很专注于性能这块。
3.go-pg自动将行数据映射为go的结构体和slice切片
4.go-pg 生成更高效的连接查询
与其他 ORM 甚至数据库包相比,您可以对连接进行高效查询。
5.简化你的代码
go-pg使用一个函数去编写语句,所以能简化你的代码。
6.提升开发效率
这是我们在项目中考虑使用ORM的首要原因。使用go-pg你无需手动的编写sql语句,所以可以加快你的开发效率。
2.缺点
1.Time.Time UTC转换
像 created_at、updated_at 这样的时间将转换为 UTC() ,所以如果你想使用时区,你必须添加你的时区。
2.此ORM仅限于postgre
二 、部署安装和建库
首先确保你已经安装了postgresql数据库,其安装和入门操作教程可参考https://www.runoob.com/postgresql/postgresql-tutorial.html。go-pg 支持 2 个最新的 Go 版本,并且需要一个支持模块的 Go 版本。
安装 pg/v10(注意导入中的 v10;省略它是一个常见的错误):
go get github.com/go-pg/pg/v10
手动创建数据库
CREATE DATABASE 命令需要在 PostgreSQL 命令窗口来执行:
CREATE DATABASE test;
此处我创建了一个名称为test的数据库,用来学习go-pg框架
三、CURD操作示例
3.1 连接数据库
db := pg.Connect(&pg.Options{
Addr: ":5432",
User: "user",
Password: "pass",
Database: "db_name",
})
Addr:数据库地址,需替换成你自己的postgresql数据库地址
User:用户名,需替换成你自己的用户名
Password:密码,需替换成你自己的密码
Database:要连接的数据库名称,,需替换成你自己的数据库名称
另一种更流行的方式是采用连接字符串
opt, err := pg.ParseURL("postgres://user:pass@localhost:5432/db_name")
if err != nil {
panic(err)
}
db := pg.Connect(opt)
3.2 创建表
创建数据库表的函数为CreateTable,函数参数为CreateTableOptions类型的指针,下面一起来看下CreateTableOptions结构体。
type CreateTableOptions struct {
Varchar int // replaces PostgreSQL data type `text` with `varchar(n)`
Temp bool
IfNotExists bool
// FKConstraints causes CreateTable to create foreign key constraints
// for has one relations. ON DELETE hook can be added using tag
// `pg:"on_delete:RESTRICT"` on foreign key field. ON UPDATE hook can be added using tag
// `pg:"on_update:CASCADE"`
FKConstraints bool
}
Varchar:用varchar(n)来替代postgresql的数据类型text
Temp:临时的
IfNotExists:如果不存在则创建
FKConstraints:添加创建外键的约束
示例代码如下所示:
//通过定义的结构体来创建数据库表
func createSchema(db *pg.DB) error {
models := []interface{}{
(*User)(nil),
}
for _, model := range models {
err := db.Model(model).CreateTable(&orm.CreateTableOptions{
//Temp: true,//建表是临时的
IfNotExists: true,
})
if err != nil {
return err
}
}
return nil
}
3.3 删除表
创建数据库表的函数为DropTable,函数参数为DropTableOptions类型的指针,下面一起来看下DropTableOptions结构体。
type DropTableOptions struct {
IfExists bool
Cascade bool
}
IfExists:如果存在则删除
示例代码如下所示:
func deleteSchema(db *pg.DB) error{
models := []interface{}{
(*User)(nil),
}
err := db.Model(&models).DropTable(&orm.DropTableOptions{
IfExists: true,
Cascade: true,
})
return err
}
3.4 查询 Select
此处演示代码为查询全部记录
var queryResult []User
err = pgsqlDB.Model(&queryResult).Select()
if err != nil{
goto ERR
}
fmt.Printf("query result:%v",queryResult)
3.5 添加 Insert
插入单行记录
//4.插入一条记录
user1 = &User{
Name: "admin",
Emails: []string{"admin1@admin", "admin2@admin"},
}
result, err = pgsqlDB.Model(user1).Insert()
if err != nil {
goto ERR
}
fmt.Printf("single insert rows affected:%d",result.RowsAffected())
插入多行记录
userList = []User{
{
Name: "haolipeng",
Emails: []string{"1078285863@qq.com"},
},
{
Name: "haolipeng",
Emails: []string{"haolipeng12345@163.com"},
},
}
result,err = pgsqlDB.Model(&userList).Insert()
if err != nil{
goto ERR
}
fmt.Printf("single insert rows affected:%d",result.RowsAffected())
3.6 删除 Delete
删除id为2的记录
delUser = User{
Id: 2,
}
result,err = pgsqlDB.Model(&delUser).WherePK().Delete()
if err != nil{
goto ERR
}
fmt.Printf("delete rows affected:%d\n",result.RowsAffected())
3.7 修改 Update
//修改除主键外的其他列
updateUser = User{
Id: 1,
Name: "antiy",
Emails: []string{"haolipeng@antiy.cn"},
}
result,err = pgsqlDB.Model(&updateUser).WherePK().Update()
if err != nil{
goto ERR
}
fmt.Printf("update rows affected:%d\n",result.RowsAffected())
四、完整代码
setting.go文件
package conf
const (
DbAddr = "10.240.19.200:5432" //postgresql数据库地址
User = "postgres"
Password = "123456"
DbName = "test"
UseConnectionString = false
)
main.go文件
package main
import (
"errors"
"fmt"
"github.com/go-pg/pg/v10"
"github.com/go-pg/pg/v10/orm"
"github.com/haolipeng/go-pg-example/conf"
)
type User struct {
Id int64
Name string
Emails []string
}
func (u User) String() string {
return fmt.Sprintf("User<%d %s %v>", u.Id, u.Name, u.Emails)
}
func main() {
var (
err error
pgsqlDB *pg.DB = nil
result pg.Result
user1 *User
updateUser User
delUser User
userList []User
queryResult []User
)
//1.连接数据库
pgsqlDB = pg.Connect(&pg.Options{
Addr: conf.DbAddr,
User: conf.User,
Password: conf.Password,
Database: conf.DbName,
})
if pgsqlDB == nil{
err = errors.New("pg.Connect() failed,error:")
goto ERR
}
//莫忘记关闭数据库连接
defer func(pgsqlDB *pg.DB) {
err := pgsqlDB.Close()
if err != nil {
fmt.Println("close postgresql failed")
}
}(pgsqlDB)
//3.创建表
err = createSchema(pgsqlDB)
if err != nil {
goto ERR
}
//4.插入一条记录
user1 = &User{
Id: 1,
Name: "admin",
Emails: []string{"admin1@admin", "admin2@admin"},
}
result, err = pgsqlDB.Model(user1).Insert()
if err != nil {
goto ERR
}
fmt.Printf("single insert rows affected:%d\n",result.RowsAffected())
//5.批量插入多条记录
userList = []User{
{
Id: 2,
Name: "haolipeng",
Emails: []string{"1078285863@qq.com"},
},
{
Id: 3,
Name: "haolipeng",
Emails: []string{"haolipeng12345@163.com"},
},
}
result,err = pgsqlDB.Model(&userList).Insert()
if err != nil{
goto ERR
}
fmt.Printf("batch insert rows affected:%d\n",result.RowsAffected())
//6.查询
err = pgsqlDB.Model(&queryResult).Select()
if err != nil{
goto ERR
}
fmt.Printf("query result:%v\n",queryResult)
//7.修改
//修改除主键外的其他列
updateUser = User{
Id: 1,
Name: "antiy",
Emails: []string{"haolipeng@antiy.cn"},
}
result,err = pgsqlDB.Model(&updateUser).WherePK().Update()
if err != nil{
goto ERR
}
fmt.Printf("update rows affected:%d\n",result.RowsAffected())
//8.删除记录(删除id为2的记录)
delUser = User{
Id: 2,
}
result,err = pgsqlDB.Model(&delUser).WherePK().Delete()
if err != nil{
goto ERR
}
fmt.Printf("delete rows affected:%d\n",result.RowsAffected())
//9.将当前记录查询并都打印出来
err = pgsqlDB.Model(&queryResult).Select()
if err != nil{
goto ERR
}
fmt.Printf("query result:%v\n",queryResult)
return
ERR:
fmt.Println("error:",err)
return
}
//通过结构体来删除表
func deleteSchema(db *pg.DB) error{
models := []interface{}{
(*User)(nil),
}
err := db.Model(&models).DropTable(&orm.DropTableOptions{
IfExists: true,
Cascade: true,
})
return err
}
//通过定义的结构体来创建数据库表
func createSchema(db *pg.DB) error {
models := []interface{}{
(*User)(nil),
}
for _, model := range models {
err := db.Model(model).CreateTable(&orm.CreateTableOptions{
//Temp: true,//建表是临时的
IfNotExists: true,
})
if err != nil {
return err
}
}
return nil
}
所有示例代码已上传到github仓库:
github地址:
https://github.com/haolipeng/go-pg-example
参考资料
https://pg.uptrace.dev/
https://medium.com/tunaiku-tech/go-pg-golang-postgre-orm-2618b75c0430
更多请查看:https://github.com/haolipeng/go-pg-example
欢迎加入我们GOLANG中国社区:https://gocn.vip/
《酷Go推荐》招募:
各位Gopher同学,最近我们社区打算推出一个类似GoCN每日新闻的新栏目《酷Go推荐》,主要是每周推荐一个库或者好的项目,然后写一点这个库使用方法或者优点之类的,这样可以真正的帮助到大家能够学习到
新的库,并且知道怎么用。
大概规则和每日新闻类似,如果报名人多的话每个人一个月轮到一次,欢迎大家报名!戳「阅读原文」,即可报名
扫码也可以加入 GoCN 的大家族哟~