在完成了数据库的初步设置之后,现在是时候创建我们的首条数据库记录了。本节还会用到之前几节展示过的Post结构,跟之前不一样的是,这次展示的程序将不会再把Post结构包含的信息存储到内存或者文件中,而是把这些信息存储到Postgres数据库中,并在需要的时候从数据库中获取这些信息。

前面的示例程序向我们展示了如何使用不同的函数执行数据的创建、获取、更新和删除操作,而在这一节,我们将会了解到使用Create函数创建新帖子的更多细节。在仔细研究Create函数的代码之前,让我们先来了解一下创建帖子的具体步骤。

创建帖子首先要做的是创建一个Post结构,并为该结构的Content字段和Author字段设置值。需要注意的是,因为结构的Id字段的值通常是由数据库的自增主键自动生成的,所以我们并不需要为这个字段设置值。

post := Post{Content: "Hello World!", Author: "Sau Sheong"}

如果我们现在使用一个fmt.Println语句打印这个结构,会看到Id字段的值被初始化成了0:

fmt.Println(post) //①

现在,我们可以通过执行Post结构的Create方法,把结构中包含的数据存储到数据库的记录(record)里面:

post.Create()

Create方法在发生故障时会返回一个错误,但为了让代码保持简单,我们这里暂且先省略相应的错误处理代码。现在,再次打印这个Post结构:

fmt.Println(post) //①{1 Hello World! Sau Sheong}

从打印的结果可以看到,Id字段的值现在被设置成了1。在了解了使用Create函数创建新帖子的具体步骤之后,现在是时候来看看代码清单6-8,了解一下它的具体实现代码了。

代码清单6-8 创建一篇帖子

func (post *Post) Create() (err error) {    
    statement := "insert into posts (content, author) values ($1, $2) returning id "    
    stmt, err := db.Prepare(statement)    
    if err != nil {          
        return    
    }    
    defer stmt.Close()    
    err = stmt.QueryRow(post.Content, post.Author).Scan(&post.Id)    
    if err != nil {          
        return    
    }    
    return
}

Create函数是Post结构的一个方法,这一点可以通过Create函数的定义看出:在func关键字和函数名Create之间,有一个指向Post结构的引用,这个名为post的引用也被称为方法的接收者receiver,接收者可以不使用&符号,直接在方法内部对结构进行引用。

Create方法做的第一件事是定义一条SQL预处理语句,一条预处理语句(prepared statement)就是一个SQL语句模板,这种语句通常用于重复执行指定的SQL语句,用户在执行预处理语句时需要为语句中的参数提供实际值。

比如,在创建数据库记录的时候,Create函数就会使用实际值去替换以下语句中的$1$2

statement := "insert into posts (content, author) values ($1, $2) returning id"

除了在数据库里面创建记录之外,这个语句还会要求数据库返回id列的值,本文稍后就会说明这样做的具体原因。

为了创建预处理语句,程序使用了sql.DB结构的Prepare方法:

stmt, err := db.Prepare(statement)

这行代码会创建一个指向sql.Stmt接口的引用,这个引用就是上面提到的预处理语句。sql.Stmt接口的定义位于sql.Driver包当中,而具体的结构则由数据库驱动实现。

之后,程序会调用预处理语句的QueryRow方法,并把来自接收者的数据传递给该方法,以此来执行预处理语句:

err = stmt.QueryRow(post.Content, post.Author).Scan(&post.Id)

我们之所以在这里使用QueryRow方法,是因为我们只想要获取一个指向sql.Row结构的引用:如果QueryRow发现被执行的SQL语句返回了多于一个sql.Row,那么它只会返回结果中的第一个sql.Row,并丢弃剩余的所有sql.Row。因为QueryRow方法的返回值只有一个sql.Row结构,它不会返回任何错误,所以QueryRow方法通常会跟Row结构的Scan方法搭配使用,并由Scan方法把行中的值复制到程序为其提供的参数里面。正如上面的代码所示,Scan方法会把SQL查询语句返回的id列的值设置为post接收者的Id字段的值,这也是我们前面在编写预处理语句时,要求SQL查询语句返回id列的值的原因。很明显,因为接收者的Content字段和Author字段都已经有值了,所以程序最后要做的就是将接收者的Id字段的值设置成数据库生成的自增整数。现在,正如你所料,因为post变量的Id字段也已经设置了值,所以程序得到的将是一个完整地进行了设置的Post结构,并且该结构包含的数据与数据库记录的数据完全一致。

results matching ""

    No results matching ""