【GoCN酷Go推荐】postgresql ORM 框架 go-pg系列(一)

共 14292字,需浏览 29分钟

 ·

2021-08-11 11:10

一、简介

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 and NOTIFY.
  • 拷贝数据 使用 COPY FROM and COPY 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 的大家族哟~





浏览 24
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报