最近写到一个程序,是关于go获取数据库的内容。按照以往用java的经验,我都是可以直接使用orm的,很方便快捷。go也有第三方的orm库,但是我嫌麻烦不想用。

问题很简单:我需要从数据库里面获取出来数据之后,封装成我需要的数据结构。在实施的时候遇到了一些问题。

数据结构

假设我需要获取的这个数据结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
type Article struct {
	Cid          int    `json:"cid"`
	Title        string `json:"title"`
	Slug         string `json:"slug"`
	Created      int    `json:"created"`
	Modified     int    `json:"modified"`
	Text         string `json:"text"`
	Order        int    `json:"order"`
	AuthorId     int    `json:"author_id"`
	Template     string `json:"template"`
	ArticleType  string `json:"article_type"`
	Status       string `json:"status"`
	Password     string `json:"password"`
	CommentSum   int    `json:"comment_sum"`
	AllowComment bool   `json:"allow_comment"`
	AllowPing    bool   `json:"allow_ping"`
	AllowFeed    bool   `json:"allow_feed"`
	Parent       int    `json:"parent"`
}

我所做的就是传递类型,然后从数据库提取出来数据,反射构造变量,完成! 但是,根据go默认的定义,变量和函数如果是小写的话,会被认为私有成员,无法反射。 所以,我的struct里面的成员全部都是大写开头。

数据库提取

这块问题不大,go已经有了mysql的驱动,我们直接使用就好了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
db, err := sql.Open("mysql", "root:123456@/typecho?charset=utf8")
if err != nil {
	panic(err)
}
defer db.Close()
stmt, err := db.Prepare(query)
if err != nil {
	panic(err)
}
defer stmt.Close()
rows, err := stmt.Query()
if err != nil {
	panic(err)
}
defer rows.Close()

变量反射

这一块是个难点,也是我们这篇文章的核心。 我们所做的是需要获取struct里面的value,然后依次获取里面的变量类型,使用数据库函数写入。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
result := make([]interface{}, 0)
s := reflect.ValueOf(struc).Elem()
leng := s.NumField()
onerow := make([]interface{}, leng)
for i := 0; i < leng; i++ {
	onerow[i] = s.Field(i).Addr().Interface()
}
for rows.Next() {
	rows.Scan(onerow...)
	result = append(result, s.Interface())
}

正常情况下,我们应该这么写

1
2
3
4
err:=rows.Scan(onerow...)
if err != nil {
	panic(err)
}

但是,如果数据库里面是空的话,err一定不为空的。如果我们不检测,反正数据赋值没有错误,就不加了!