Go 使用 PostgreSQL、JSON 和 Go详解

在本章中,我们将从整体上看 SQL。在前面的章节中,我们讨论了 SQLite3,它是一个用于快速原型制作的小型数据库。但对于生产级应用程序,人们更喜欢 MySQL 或 PostgreSQL。这两种方法都在 web 应用程序领域得到了很好的验证。首先,我们将讨论 PostgreSQL 的内部结构,然后继续在 Go 中编写数据库模型。然后,我们将尝试用一个可靠的例子来实现 URL 缩短服务。

在本章中,我们将介绍以下主题:

您可以在获取本章的代码示例 https://github.com/narenaryan/gorestful/tree/master/chapter7 。在上一章中,我们讨论了协议缓冲区和 GRPC。但是在这里,我们回到 RESTAPI 和 JSON,看看 PostgreSQL 是如何补充 JSON 的。

PostgreSQL 是一种开源数据库,可以安装在多个平台上。在 Ubuntu 上,可以使用以下命令安装:

要将回购添加到套餐列表中,请执行以下操作:


sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" >> /etc/apt/sources.list.d/pgdg.list' 
wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O - | sudo apt-key add -

要更新包列表,请执行以下操作:


sudo apt-get update
apt-get install postgresql postgresql-contrib

这将在 Ubuntu 机器上安装数据库,并在端口5432上启动服务器。现在,为了进入数据库 shell,使用以下命令。PostgreSQL 创建一个名为postgres的默认用户登录。请查看以下命令:

**```go sudo su - postgres


现在用户可以访问数据库了。使用`psql`命令启动 PostgreSQL shell:

```go
psql

这表明,与其他类似数据库(如 MySQL 或 SQLite3)相比,PostgreSQL 采用不同的方法进入 shell。在 Windows 上,通过单击二进制安装程序文件完成安装。这是一个基于 GUI 的安装,其中应提供超级用户的端口和密码。一旦安装了数据库,我们可以使用pgAdmin3工具检查。macOS X 安装与 Ubuntu 类似,只是安装是通过自制完成的。请查看以下命令:

brew install postgresql

然后使用以下命令使数据库服务器即使在系统重新启动时也能运行:

pg_ctl -D /usr/local/var/postgres start && brew services start postgresql

现在,PostgreSQL server 开始运行,可以在 macOS X 上存储和检索数据。

现在,我们应该知道如何创建新的用户和数据库。为此,我们将使用 Ubuntu/Mac 作为一般示例。我们在一个称为psql壳的壳中进行此操作。我们可以使用\?命令在psql中看到所有可用的命令。要进入psql,首先更改为postgres用户。在 Ubuntu 上,您可以使用以下命令执行此操作:

sudo su postgres

现在,它把我们变成了一个名为postgres的用户。然后,使用psql命令启动psql外壳。如果您在其中键入\?,您将看到所有可用命令的输出:

要列出所有可用用户及其权限,您可以在 shell 帮助的Informational部分找到一个命令,即:

\du - List roles

角色是授予用户的访问权限。该列表中的默认角色为postgres

postgres=# \du

 List of roles 
 Role name |      Attributes               | Member of 
-----------+------------------------------------------------------------+-----------
 postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}

前面的命令列出了角色(用户)及其属性(允许角色执行的操作)和其他选项。要添加新用户,我们只需键入以下psql命令:

CREATE ROLE naren with LOGIN PASSWORD 'passme123';

这将创建一个名为naren且密码为passme123.的新用户。现在,使用以下命令授予该用户创建数据库和其他角色的权限:

**```go ALTER USER naren CREATEDB, CREATEROLE;


要删除用户,只需在相同的上下文中使用`DROP`命令:

```go
DROP ROLE naren;

不要试图更改默认postgres用户的密码。拟为 sudo 账号,不应作为普通用户保存。相反,创建一个角色并为其授予 require 权限。

**现在我们知道了如何创建角色。让我们再看几个 CRUD 命令,它们实际上是我们在其他关系数据库中也看到的 SQL 命令。请看下表:

| 动作 | SQL 命令 | | 创建数据库 |

CREATE DATABASE mydb;

| | 创建表 |

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric
);

| | 插入表格 |

INSERT INTO products VALUES (1, 'Rice', 5.99);

| | 更新表 |

UPDATE products SET price = 10 WHERE price = 5.99;

| | 从表中删除 |

DELETE FROM products WHERE price = 5.99;

|

现在,让我们看看如何与 PostgreSQL 对话,并使用一个简单的示例尝试执行前面的操作

在前面的章节中,当我们处理 SQLite3 时,我们使用了一个名为go-sqlite3的外部库。同样,一个数据库驱动程序库可以连接 Go 和 PostgreSQL。那个图书馆叫pq我们可以使用以下命令安装该库:****

****```go go get github.com/lib/pq


在获得这个库之后,我们需要以类似于 SQLite3 的方式使用它。API 将与 Go 的`database/sql`**包一致。为了创建一个新表,我们应该初始化数据库。要创建一个新的数据库,只需在`psql`shell 中键入此命令,如下命令所示;这是一次性的:**

 **```go
CREATE DATABASE mydb;

现在,我们将编写一个小的代码说明来解释pq驱动程序的用法。在您的$GOPATH中创建一个名为models的目录。这里,我的GOPATH/home/naren/workspace/与前面章节中的所有示例类似,我们将在src/目录中创建我们的包和应用程序源:

**```go mkdir github.com/narenaryan/src/models


现在,添加一个名为`web_urls.go`的文件。**此文件将具有表创建逻辑:**

 **```go
package models

import (
        "database/sql"
        "log"
        _ "github.com/lib/pq"
)

func InitDB() (*sql.DB, error) {
        var err error
        db, err := sql.Open("postgres", "postgres://naren:passme123@localhost/mydb?sslmode=disable")
        if err != nil {
                return nil, err
        } else {
                // Create model for our URL service
                stmt, err := db.Prepare("CREATE TABLE WEB_URL(ID SERIAL PRIMARY KEY, URL TEXT NOT NULL);")
                if err != nil {
                        log.Println(err)
                        return nil, err
                }
                res, err := stmt.Exec()
                log.Println(res)
                if err != nil {
                        log.Println(err)
                        return nil, err
                }
                return db, nil
        }
}

我们正在这里导入pq库。我们正在使用sql.Open功能启动一个新的数据库连接池。如果观察连接字符串,它由多个部分组成。请看下图:

**

连接字符串应包括数据库类型、username:password对、数据库服务器 IP 和 sslmode 设置。然后我们创建一个名为web_url的表。所有的错误处理程序都在那里,用于指定是否出现错误。InitDB函数将数据库连接对象返回给导入该函数的任何程序。让我们编写使用此软件包的主程序:

package main

import (
       "log"
      "github.com/narenaryan/models"
)

func main() {
  db, err := models.InitDB()
  if err != nil {
    log.Println(db)
  }
}

此程序导入models包并使用其中的InitDB功能。我们正在打印数据库连接,它将是一个地址。如果运行该程序,您将看到打印对象的地址:

**```go go run main.go


这将在`mydb`数据库中创建一个`web_url`表**。**我们可以通过进入`psql`外壳并键入以下内容来交叉检查:****

 ****```go
\c mydb \dt

它将用户连接到mydb数据库,并列出所有可用的表,如下面的代码片段所示:

You are now connected to database "mydb" as user "postgres".
 List of relations
 Schema | Name | Type | Owner
--------+---------+-------+-------
 public | web_url | table | naren
(1 row)

在 PostgreSQL 中,在为表创建提供模式时,需要将自动增量类型替换为串行。

让我们编写 URL 缩短服务来解释我们在上一节中讨论的所有概念。在此之前,让我们设计一个包来实现带有编码/解码功能的 Base62 算法。URL 缩短技术需要 Base62 算法将长 URL 转换为短 URL,反之亦然。然后,我们编写一个实例来说明这种编码是如何工作的。在GOPATH中创建一个名为base62的目录:

**```go mkdir $GOPATH/src/github.com/narenaryan/base62


现在,添加一个名为`encodeutils.go`的文件,其中包含我们的编码和解码功能。

# 定义 Base62 算法

我们在前面的章节中看到了 Base62 算法的工作原理。下面是该算法的可靠实现。这种逻辑是纯数学的,在网络上随处可见。请看下面的代码:

```go
package base62

import (
     "math"
     "strings"
)

const base = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const b = 62

// Function encodes the given database ID to a base62 string
func ToBase62(num int) string{
    r := num % b
    res := string(base[r])
    div := num / b
    q := int(math.Floor(float64(div)))

    for q != 0 {
        r = q % b
        temp := q / b
        q = int(math.Floor(float64(temp)))
        res = string(base[int(r)]) + res
    }

    return string(res)
}

// Function decodes a given base62 string to datbase ID
func ToBase10(str string) int{
    res := 0
    for _, r := range str {
        res = (b * res) + strings.Index(base, string(r))
    }
    return res
}

在前面的程序中,我们定义了两个名为ToBase62ToBase10的函数。第一个取整数生成base62字符串,后一个取反效果;也就是说,它接受一个base62字符串并给出原始数字。为了说明这一点,让我们创建一个简单的程序,使用这两个函数来显示编码/解码:

**```go vi $GOPATH/src/github.com/narenaryan/usebase62.go


向其中添加以下内容:

```go
package main

import (
      "log"
      base62 "github.com/narenaryan/base62"
)

func main() {
  x := 100
  base62String := base62.ToBase62(x)
  log.Println(base62String)
  normalNumber := base62.ToBase10(base62String)
  log.Println(normalNumber)
}

这里,我们正在使用base62中的函数,并尝试查看输出。如果我们使用以下命令运行此程序(从$GOPATH/src/github.com/narenaryan

**```go go run usebase62.go


它打印:

```go
2017/08/07 23:00:05 1C
2017/08/07 23:00:05 100

100base62编码为1C。这是因为在我们的base62逻辑中,索引 100 缩小到1C

const base = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

原始数字将用于映射此基本字符串中的字符。然后,将数字除以 62 以找出下一个字符。这个算法的美妙之处在于为每个给定的数字创建一个唯一的、较短的字符串。我们使用这种技术将数据库 ID 传递到ToBase62算法中,并得到一个较短的字符串。每当 URL 缩短请求到达我们的服务器时,它应执行以下步骤:

  1. 将 URL 存储在数据库中,并获取插入的记录的 ID。
  2. 将此 ID 作为 API 响应传递给客户端。
  3. 每当客户端加载缩短的 URL 时,它都会命中我们的 API 服务器。
  4. 然后,API 服务器将短 URL 转换回数据库 ID,并从原始 URL 获取记录。
  5. 最后,客户端可以使用此 URL 重定向到原始站点。

我们将在这里编写一个 Go 项目来实现前面的步骤。让我们来编写程序。我正在为我们的项目创建一个目录结构。我们从前面的插图中获取文件,用于处理编码/解码base62和数据库逻辑。目录结构如下所示:

urlshortener
├── main.go
├── models
│   └── models.go
└── utils
 └── encodeutils.go

2 directories, 3 files

将此目录复制到$GOPATH/src/github.com/narenaryan。再一次,一个小小的警告。将narenaryan替换为您的用户名。从前面的示例中复制encodeutils.gomodels.go。然后,开始编写主程序:****

****```go package main

import ( "database/sql" "encoding/json" "io/ioutil" "log" "net/http" "time"

"github.com/gorilla/mux"
_ "github.com/lib/pq"
"github.com/narenaryan/urlshortener/models"
base62 "github.com/narenaryan/urlshortener/utils"

)

// DB stores the database session imformation. Needs to be initialized once type DBClient struct { db *sql.DB }

// Model the record struct type Record struct { ID int json:"id" URL string json:"url" }

// GetOriginalURL fetches the original URL for the given encoded(short) string func (driver DBClient) GetOriginalURL(w http.ResponseWriter, r http.Request) { var url string vars := mux.Vars(r) // Get ID from base62 string id := base62.ToBase10(vars["encoded_string"]) err := driver.db.QueryRow("SELECT url FROM weburl WHERE id = $1", id).Scan(&url) // Handle response details if err != nil { w.Write([]byte(err.Error())) } else { w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") responseMap := map[string]interface{}{"url": url} response, := json.Marshal(responseMap) w.Write(response) } }

// GenerateShortURL adds URL to DB and gives back shortened string func (driver DBClient) GenerateShortURL(w http.ResponseWriter, r http.Request) { var id int var record Record postBody, _ := ioutil.ReadAll(r.Body) json.Unmarshal(postBody, &record) err := driver.db.QueryRow("INSERT INTO web_url(url) VALUES($1) RETURNING id", record.URL).Scan(&id) responseMap := map[string]interface{}{"encodedstring": base62.ToBase62(id)} if err != nil { w.Write([]byte(err.Error())) } else { w.Header().Set("Content-Type", "application/json") response, := json.Marshal(responseMap) w.Write(response) } }

func main() { db, err := models.InitDB() if err != nil { panic(err) } dbclient := &DBClient{db: db} if err != nil { panic(err) } defer db.Close() // Create a new router r := mux.NewRouter() // Attach an elegant path with handler r.HandleFunc("/v1/short/{encoded_string:[a-zA-Z0-9]}", dbclient.GetOriginalURL).Methods("GET") r.HandleFunc("/v1/short", dbclient.GenerateShortURL).Methods("POST") srv := &http.Server{ Handler: r, Addr: "127.0.0.1:8000", // Good practice: enforce timeouts for servers you create! WriteTimeout: 15 time.Second, ReadTimeout: 15 * time.Second, } log.Fatal(srv.ListenAndServe()) }


首先,我们导入了`postgres`库和其他必要的库。我们从模型中导入了数据库会话。接下来,我们导入了我们的编码/解码 base62 算法来实现我们的逻辑:

```go
// DB stores the database session imformation. Needs to be initialized once
type DBClient struct {
  db *sql.DB
}

// Model the record struct
type Record struct {
  ID  int    `json:"id"`
  URL string `json:"url"`
}

为了在各种功能之间传递数据库驱动程序,需要使用DBClient。记录是与插入数据库的记录相似的结构。我们在代码中定义了两个函数,分别是GenerateShortURLGetOriginalURL,用于将 URL 添加到数据库中,然后从数据库中取回。正如我们已经解释的 URL 缩短的内部技术一样,使用此服务的客户端将获得必要的响应。让我们先运行程序并查看输出,然后再开始进一步的详细说明:

go run $GOPATH/src/github.com/narenaryan/urlshortener/main.go

如果您的$GOPATH/bin已经在系统PATH变量中,我们可以先安装二进制文件并按如下方式运行:

go install github.com/narenaryan/urlshortener/main.go

然后就是程序名:

urlshortener

安装二进制文件是最佳做法,因为它在系统范围内可用。但是对于较小的程序,我们可以通过访问程序目录来运行main.go

现在它运行 HTTP 服务器并开始收集 URL 缩短服务的请求。打开控制台并键入以下命令:

curl -X POST \
 http://localhost:8000/v1/short \
 -H 'cache-control: no-cache' \
 -H 'content-type: application/json' \
 -d '{
 "url": "https://www.forbes.com/forbes/welcome/?toURL=https://www.forbes.com/sites/karstenstrauss/2017/04/20/the-highest-paying-jobs-in-tech-in-2017/&refURL=https://www.google.co.in/&referrer=https://www.google.co.in/"
}'

它返回缩短的字符串:

{
  "encoded_string": "1"
}

编码的字符串只是"1"。Base62 算法开始分配从一开始到字母数字字母组合的较短字符串。现在,如果我们需要检索原始 URL,我们可以执行一个GET请求:

curl -X GET \
 http://localhost:8000/v1/short/1 \
 -H 'cache-control: no-cache' \

它返回以下 JSON:

{   
"url":"https://www.forbes.com/forbes/welcome/?toURL=https://www.forbes.com/sites/karstenstrauss/2017/04/20/the-highest-paying-jobs-in-tech-in-2017/\u0026refURL=https://www.google.co.in/\u0026referrer=https://www.google.co.in/"}

因此,服务可以使用此结果将用户重定向到原始 URL(站点)。这里,生成的字符串不依赖于 URL 的长度,因为只有数据库 ID 是编码的标准。

PostgreSQL 中的INSERTSQL 命令需要添加RETURNING关键字来获取最后插入的数据库 ID。MySQL 或 SQLite3INSERT INTO web_url( ) VALUES($1) RETURNING id, record.URL不是这种情况。此 DB 查询返回最后插入的记录的 ID。如果我们删除该RETURNING关键字,查询将不返回任何内容。

PostgreSQL>9.2有一个突出的特性 9.2“dbid=“254735”称为 JSON 存储。PostgreSQL 引入了一种新的数据类型来存储 JSON 数据。PostgreSQL 允许用户插入一个jsonb字段类型它保存 JSON 字符串。它在建模实际数据时非常有用,而实际数据的结构必须更加灵活。PostgreSQL 通过允许我们存储 JSON 字符串和关系类型,充分利用了这两个方面的优势。

在本节中,我们将尝试实现我们在前几章中为电子商务网站定义的一些 JSON 模型。但在这里,我们将使用 JSON 字段在 PostgreSQL 中存储和检索项。对于访问 PostgreSQL 的 JSON 存储,普通的pq库非常繁琐。因此,为了更好地处理这个问题,我们可以使用名为GORM对象关系映射器ORM**)。

此 ORM 具有可在database/sql包中完成的所有操作的 API。我们可以使用以下命令安装 GORM:

**```go go get -u github.com/jinzhu/gorm


有关此 ORM 的完整文档,请访问[http://jinzhu.me/gorm/](http://jinzhu.me/gorm/) 。让我们编写一个实现用户和订单类型 JSON 模型的程序。用户可以下订单。我们将使用上一章中定义的模型。我们可以在`$GOPATH/src/github.com/narenaryan`中创建一个名为`jsonstore`的新目录,并在其中为我们的模型创建一个新目录:

```go
mkdir jsonstore
mkdir jsonstore/models
touch jsonstore/models/models.go

现在,将models.go文件编辑为:

package models

import (
  "github.com/jinzhu/gorm"
  _ "github.com/lib/pq"
)

type User struct {
  gorm.Model
  Orders []Order
  Data string `sql:"type:JSONB NOT NULL DEFAULT '{}'::JSONB" json:"-"`
}

type Order struct {
  gorm.Model
  User User
  Data string `sql:"type:JSONB NOT NULL DEFAULT '{}'::JSONB"`
}

// GORM creates tables with plural names. Use this to suppress it
func (User) TableName() string {
  return "user"
}

func (Order) TableName() string {
  return "order"
}

func InitDB() (*gorm.DB, error) {
  var err error
  db, err := gorm.Open("postgres", "postgres://naren:passme123@localhost/mydb?sslmode=disable")
  if err != nil {
    return nil, err
  } else {
    /*
    // The below AutoMigrate is equivalent to this
    if !db.HasTable("user") {
      db.CreateTable(&User{})
    }

    if !db.HasTable("order") {
      db.CreateTable(&Order{})        
    }
    */
    db.AutoMigrate(&User{}, &Order{})
    return db, nil
  }
}

这看起来类似于我们在本章前面定义的模型。在这里,很多事情对我们来说都是新的。我们创建的每个模型(表)都应该在 GORM 中表示为一个结构。这就是我们创建两个结构的原因,UserOrder。第一行应该是gorm.Model其他字段为表中的字段。默认情况下,将创建递增的 ID。在前面的 URL 缩短器模型中,我们在对表进行操作之前手动检查表的存在性。但这里有一个功能:

**```go db.AutoMigrate(&User{}, &Order{})


此函数为作为参数传递的结构创建表。它确保如果表已经存在,则跳过创建。如果您仔细观察,我们为这些结构添加了一个函数,`TableName`。**默认情况下,GORM 创建的所有表名都是复数名(`users`**是为`User`创建的)。**为了强制它创建给定的名称,我们需要重写该函数。更有趣的是,在 structs 中,我们使用了一个名为`Data`的字段。这是一种:******

 ******```go
`sql:"type:JSONB NOT NULL DEFAULT '{}'::JSONB" json:"-"`

是的,它是一个jsonb类型的字符串。现在,我们将其类型添加为string.PostgreSQL,GORM 负责处理它。然后,我们将数据库连接返回给导入models包的人。

在开始之前,让我们设计 API 规范表,它显示了各种 URL 端点的 RESTAPI 签名。请参阅下表:

| 终点 | 方法 | 说明 | | /v1/user/id | GET | 使用 ID 获取用户 | | /v1/user | POST | 创建新用户 | | /v1/user?first_name=NAME | GET | 按给定的名字获取所有用户 | | /v1/order/id | GET | 获取具有给定 ID 的订单 | | /v1/order | POST | 创建新订单 |

现在我们来看主要节目,;让我们在jsonstore项目中再添加一个文件。在本程序中,我们将尝试实现前三个端点。我们建议将其余两个端点的实现作为读者的任务。请查看以下命令:

**```go touch jsonstore/main.go


程序结构与我们迄今为止看到的所有程序都遵循相同的风格。我们使用 Gorilla Mux 作为 HTTP 路由器,并将数据库驱动程序导入我们的程序:

```go
package main

import (
  "encoding/json"
  "io/ioutil"
  "log"
  "net/http"
  "time"

  "github.com/gorilla/mux"
  "github.com/jinzhu/gorm"
    _ "github.com/lib/pq"
  "github.com/narenaryan/jsonstore/models"
)

// DB stores the database session imformation. Needs to be initialized once
type DBClient struct {
  db *gorm.DB
}

// UserResponse is the response to be send back for User
type UserResponse struct {
  User models.User `json:"user"`
  Data interface{} `json:"data"`
}

// GetUsersByFirstName fetches the original URL for the given encoded(short) string
func (driver *DBClient) GetUsersByFirstName(w http.ResponseWriter, r *http.Request) {
  var users []models.User
  name := r.FormValue("first_name")
  // Handle response details
  var query = "select * from \"user\" where data->>'first_name'=?"
  driver.db.Raw(query, name).Scan(&users)
  w.WriteHeader(http.StatusOK)
  w.Header().Set("Content-Type", "application/json")
  //responseMap := map[string]interface{}{"url": ""}
  respJSON, _ := json.Marshal(users)
  w.Write(respJSON)
}

// GetUser fetches the original URL for the given encoded(short) string
func (driver *DBClient) GetUser(w http.ResponseWriter, r *http.Request) {
  var user = models.User{}
  vars := mux.Vars(r)
  // Handle response details
  driver.db.First(&user, vars["id"])
  var userData interface{}
  // Unmarshal JSON string to interface
  json.Unmarshal([]byte(user.Data), &userData)
  var response = UserResponse{User: user, Data: userData}
  w.WriteHeader(http.StatusOK)
  w.Header().Set("Content-Type", "application/json")
  //responseMap := map[string]interface{}{"url": ""}
  respJSON, _ := json.Marshal(response)
  w.Write(respJSON)
}

// PostUser adds URL to DB and gives back shortened string
func (driver *DBClient) PostUser(w http.ResponseWriter, r *http.Request) {
  var user = models.User{}
  postBody, _ := ioutil.ReadAll(r.Body)
  user.Data = string(postBody)
  driver.db.Save(&user)
  responseMap := map[string]interface{}{"id": user.ID}
  var err string = ""
  if err != "" {
    w.Write([]byte("yes"))
  } else {
    w.Header().Set("Content-Type", "application/json")
    response, _ := json.Marshal(responseMap)
    w.Write(response)
  }
}

func main() {
  db, err := models.InitDB()
  if err != nil {
    panic(err)
  }
  dbclient := &DBClient{db: db}
  if err != nil {
    panic(err)
  }
  defer db.Close()
  // Create a new router
  r := mux.NewRouter()
  // Attach an elegant path with handler
  r.HandleFunc("/v1/user/{id:[a-zA-Z0-9]*}", dbclient.GetUser).Methods("GET")
  r.HandleFunc("/v1/user", dbclient.PostUser).Methods("POST")
  r.HandleFunc("/v1/user", dbclient.GetUsersByFirstName).Methods("GET")
  srv := &http.Server{
    Handler: r,
    Addr:    "127.0.0.1:8000",
    // Good practice: enforce timeouts for servers you create!
    WriteTimeout: 15 * time.Second,
    ReadTimeout:  15 * time.Second,
  }
  log.Fatal(srv.ListenAndServe())
}

这里有三个重要方面:

  • 我们用 GORM 驱动程序替换了传统的驱动程序
  • 用于 CRUD 操作的 GORM 函数
  • 我们将 JSON 插入 PostgreSQL 并在 JSON 字段中检索结果

让我们详细解释一下所有的要素。首先,我们导入了所有必要的软件包。有趣的是:

  "github.com/jinzhu/gorm"
   _ "github.com/lib/pq"
  "github.com/narenaryan/jsonstore/models"

GORM 内部在某种程度上使用了database/sql包。我们从前面代码中创建的包中导入了模型。接下来,我们创建了三个函数,实现了前三个 API 规范。它们是GetUsersByFirstName,60;GetUser、和PostUser。每个函数都继承了数据库驱动程序,并作为main函数中 URL 端点的处理函数传递:

*```go r.HandleFunc("/v1/user/{id:[a-zA-Z0-9]}", dbclient.GetUser).Methods("GET") r.HandleFunc("/v1/user", dbclient.PostUser).Methods("POST") r.HandleFunc("/v1/user", dbclient.GetUsersByFirstName).Methods("GET")


现在,如果我们输入第一个函数,很简单,这些语句将吸引我们的注意力:

```go
driver.db.First(&user, vars["id"])

前面的语句告诉 DB 使用给定的第二个参数ID从数据库中获取第一条记录。它填充返回到user结构的数据。我们在GetUser中使用UserResponse而不是User结构,因为User由数据字段组成,数据字段是一个字符串。但是,为了向客户端返回完整和正确的 JSON,我们需要将数据转换为正确的结构,然后封送:

**``go // UserResponse is the response to be send back for User type UserResponse struct { User models.Userjson:"user" Data interface{}json:"data"` }


这里,我们创建了一个空接口,可以保存任何 JSON 数据。当我们使用驱动程序调用第一个**函数时,用户结构有一个数据字段,它是一个字符串。我们需要将该字符串转换为结构,然后在`UserResponse`中将其与其他详细信息一起发送。现在让我们看看这一点的实际效果。使用以下命令运行程序:**

 **```go
go run jsonstore/main.go

并发出几个 CURL 命令以查看 API 响应:

创建用户:

curl -X POST \
  http://localhost:8000/v1/user \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -d '{
     "username": "naren",
     "email_address": "narenarya@live.com",
     "first_name": "Naren",
     "last_name": "Arya"
}'

它返回数据库中插入的记录:

{
  "id": 1
}

现在,如果我们GET插入记录的详细信息:

curl -X GET http://localhost:8000/v1/user/1 

它返回有关用户的所有详细信息:

{"user":{"ID":1,"CreatedAt":"2017-08-27T11:55:02.974371+05:30","UpdatedAt":"2017-08-27T11:55:02.974371+05:30","DeletedAt":null,"Orders":null},"data":{"email_address":"narenarya@live.com","first_name":"Naren","last_name":"Arya","username":"naren"}}

再插入一条记录以检查名字 API:

curl -X POST \
  http://localhost:8000/v1/user \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -d '{
     "username": "nareny",
     "email_address": "naren.yellavula@gmail.com",
     "first_name": "Naren",
     "last_name": "Yellavula"
}'

这是我们的第二个记录。让我们测试我们的第三个 API,GetUsersByFirstName

**```go curl -X GET 'http://localhost:8000/v1/user?first_name=Naren'


这将返回具有给定名字的所有用户:

```go
[{"ID":1,"CreatedAt":"2017-08-27T11:55:02.974371+05:30","UpdatedAt":"2017-08-27T11:55:02.974371+05:30","DeletedAt":null,"Orders":null},{"ID":2,"CreatedAt":"2017-08-27T11:59:41.84332+05:30","UpdatedAt":"2017-08-27T11:59:41.84332+05:30","DeletedAt":null,"Orders":null}]

该项目的核心理念是展示如何从 PostgreSQL 中存储和检索 JSON。这里的特殊之处在于,我们查询的是 JSON 字段,而不是User表中的普通字段。

记住,PostgreSQL 将其用户存储在一个名为 user 的表中。如果要创建新的用户表,请使用"user"(双引号)创建它。即使在检索时也要使用双引号。否则 DB 将获取内部用户详细信息:

SELECT * FROM "user"; // Correct way SELECT * FROM user; // Wrong way. It fetches database users 

我们的 PostgreSQL 之旅到此结束。在研究生中还有很多东西需要探索。它允许我们在同一个表中存储关系数据和 JSON 数据,从而实现了这两个方面的最佳效果。

在本章中,我们通过安装 PostgreSQL 开始了我们的旅程。我们正式引入了 PostgreSQL,并尝试查看所有可能的 CRUD 操作 SQL 查询。然后,我们了解了如何在 PostgreSQL 中添加用户和数据库。然后,我们安装并解释了用于 Go 语言的 Postgres 驱动程序pq。我们解释了驱动程序 API 如何执行原始 SQL 查询

然后是 URL 缩短服务的实现部分;REST 服务接受原始 URL 并返回一个缩短的字符串。它还接受缩短的 URL 并返回原始 URL。我们编写了一个示例程序来说明为我们的服务提供动力的 Base62 算法。我们在接下来的服务中利用了这个算法,并创建了一个 RESTAPI。

GORM 是著名的 Go 对象关系映射器。使用 ORM,可以轻松地管理数据库操作。GORM 提供了一些有用的功能,例如AutoMigrate(如果不存在表,则创建一个表),用于在传统database/sql驱动程序上编写直观的 Go 代码。

PostgreSQL 还允许 JSON 存储(称为 JSON 存储)超过 9.2 版。它使开发人员能够利用 JSON 格式的关系数据库。我们可以在 JSON 字段上创建索引,在 JSON 字段上查询,等等。我们使用 GORM 为我们在前几章中定义的电子商务模型实现了 RESTAPI。PostgreSQL 是一个完善的、开源的关系数据库,可以满足我们企业的需要。驾驶员对 Go 的支持是例外的,有pqgorm****

教程来源于Github,感谢apachecn大佬的无私奉献,致敬!

技术教程推荐

从0开始学微服务 -〔胡忠想〕

如何设计一个秒杀系统 -〔许令波〕

TypeScript开发实战 -〔梁宵〕

移动端自动化测试实战 -〔思寒〕

讲好故事 -〔涵柏〕

手把手带你搭建秒杀系统 -〔佘志东〕

React Native 新架构实战课 -〔蒋宏伟〕

运维监控系统实战笔记 -〔秦晓辉〕

B端产品经理入门课 -〔董小圣〕