添加数据库db.Close()

This commit is contained in:
astaxie
2012-11-21 17:57:32 +08:00
parent a634fe67fb
commit de0e00a141
3 changed files with 379 additions and 373 deletions

272
5.2.md
View File

@@ -1,135 +1,137 @@
# 5.2使用MySQL数据库 # 5.2使用MySQL数据库
目前Internet上流行的网站构架方式是LAMP其中的M即MySQL, 作为数据库MySQL以免费、开源、使用方便为优势成为了很多Web开发的后端数据库存储引擎。 目前Internet上流行的网站构架方式是LAMP其中的M即MySQL, 作为数据库MySQL以免费、开源、使用方便为优势成为了很多Web开发的后端数据库存储引擎。
## MySQL驱动 ## MySQL驱动
Go中支持MySQL的驱动目前比较多有如下几种有些是支持database/sql标准而有些是采用了自己的实现接口,常用的有如下几种: Go中支持MySQL的驱动目前比较多有如下几种有些是支持database/sql标准而有些是采用了自己的实现接口,常用的有如下几种:
- http://code.google.com/p/go-mysql-driver/ 支持database/sql全部采用go写。 - http://code.google.com/p/go-mysql-driver/ 支持database/sql全部采用go写。
- https://github.com/ziutek/mymysql 支持database/sql也支持自定义的接口全部采用go写。 - https://github.com/ziutek/mymysql 支持database/sql也支持自定义的接口全部采用go写。
- https://github.com/Philio/GoMySQL 不支持database/sql自定义接口全部采用go写。 - https://github.com/Philio/GoMySQL 不支持database/sql自定义接口全部采用go写。
接下来的例子我主要以第一个驱动为例(我目前项目中也是采用它来驱动),也推荐大家采用它,主要理由: 接下来的例子我主要以第一个驱动为例(我目前项目中也是采用它来驱动),也推荐大家采用它,主要理由:
- 这个驱动比较新,维护的比较好 - 这个驱动比较新,维护的比较好
- 完全支持database/sql接口 - 完全支持database/sql接口
- 支持keepalive保持长连接,虽然[星星](http://www.mikespook.com)fork的mymysql也支持keepalive但不是线程安全的这个从底层就支持了keepalive。 - 支持keepalive保持长连接,虽然[星星](http://www.mikespook.com)fork的mymysql也支持keepalive但不是线程安全的这个从底层就支持了keepalive。
## 示例代码 ## 示例代码
接下来的几个小节里面我们都将采用同一个数据库表结构数据库test用户表userinfo关联用户信息表userdetail。 接下来的几个小节里面我们都将采用同一个数据库表结构数据库test用户表userinfo关联用户信息表userdetail。
CREATE TABLE `userinfo` ( CREATE TABLE `userinfo` (
`uid` INT(10) NOT NULL AUTO_INCREMENT, `uid` INT(10) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(64) NULL DEFAULT NULL, `username` VARCHAR(64) NULL DEFAULT NULL,
`departname` VARCHAR(64) NULL DEFAULT NULL, `departname` VARCHAR(64) NULL DEFAULT NULL,
`created` DATE NULL DEFAULT NULL, `created` DATE NULL DEFAULT NULL,
PRIMARY KEY (`uid`) PRIMARY KEY (`uid`)
) )
CREATE TABLE `userdetail` ( CREATE TABLE `userdetail` (
`uid` INT(10) NOT NULL DEFAULT '0', `uid` INT(10) NOT NULL DEFAULT '0',
`intro` TEXT NULL, `intro` TEXT NULL,
`profile` TEXT NULL, `profile` TEXT NULL,
PRIMARY KEY (`uid`) PRIMARY KEY (`uid`)
) )
如下示例将示范如何使用database/sql接口对数据库表进行增删改查操作 如下示例将示范如何使用database/sql接口对数据库表进行增删改查操作
package main package main
import ( import (
_ "code.google.com/p/go-mysql-driver/mysql" _ "code.google.com/p/go-mysql-driver/mysql"
"database/sql" "database/sql"
"fmt" "fmt"
//"time" //"time"
) )
func main() { func main() {
db, err := sql.Open("mysql", "astaxie:astaxie@/test?charset=utf8") db, err := sql.Open("mysql", "astaxie:astaxie@/test?charset=utf8")
checkErr(err) checkErr(err)
//插入数据 //插入数据
stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?") stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
checkErr(err) checkErr(err)
res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09") res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
checkErr(err) checkErr(err)
id, err := res.LastInsertId() id, err := res.LastInsertId()
checkErr(err) checkErr(err)
fmt.Println(id) fmt.Println(id)
//更新数据 //更新数据
stmt, err = db.Prepare("update userinfo set username=? where uid=?") stmt, err = db.Prepare("update userinfo set username=? where uid=?")
checkErr(err) checkErr(err)
res, err = stmt.Exec("astaxieupdate", id) res, err = stmt.Exec("astaxieupdate", id)
checkErr(err) checkErr(err)
affect, err := res.RowsAffected() affect, err := res.RowsAffected()
checkErr(err) checkErr(err)
fmt.Println(affect) fmt.Println(affect)
//查询数据 //查询数据
rows, err := db.Query("SELECT * FROM userinfo") rows, err := db.Query("SELECT * FROM userinfo")
checkErr(err) checkErr(err)
for rows.Next() { for rows.Next() {
var uid int var uid int
var username string var username string
var department string var department string
var created string var created string
err = rows.Scan(&uid, &username, &department, &created) err = rows.Scan(&uid, &username, &department, &created)
checkErr(err) checkErr(err)
fmt.Println(uid) fmt.Println(uid)
fmt.Println(username) fmt.Println(username)
fmt.Println(department) fmt.Println(department)
fmt.Println(created) fmt.Println(created)
} }
//删除数据 //删除数据
stmt, err = db.Prepare("delete from userinfo where uid=?") stmt, err = db.Prepare("delete from userinfo where uid=?")
checkErr(err) checkErr(err)
res, err = stmt.Exec(id) res, err = stmt.Exec(id)
checkErr(err) checkErr(err)
affect, err = res.RowsAffected() affect, err = res.RowsAffected()
checkErr(err) checkErr(err)
fmt.Println(affect) fmt.Println(affect)
} db.Close()
func checkErr(err error) { }
if err != nil {
panic(err) func checkErr(err error) {
} if err != nil {
} panic(err)
}
}
通过上面的代码我们可以看出Go操作Mysql数据库是很方便的。
关键的几个函数我解释一下: 通过上面的代码我们可以看出Go操作Mysql数据库是很方便的。
sql.Open()函数用来打开一个注册过的数据库驱动go-mysql-driver中注册了mysql这个数据库驱动第二个参数是DNS(Data Source Name)它是go-mysql-drivev定义的一些数据库链接和配置信息。它支持如下格式 关键的几个函数我解释一下
user@unix(/path/to/socket)/dbname?charset=utf8 sql.Open()函数用来打开一个注册过的数据库驱动go-mysql-driver中注册了mysql这个数据库驱动第二个参数是DNS(Data Source Name)它是go-mysql-drivev定义的一些数据库链接和配置信息。它支持如下格式
user:password@tcp(localhost:5555)/dbname?charset=utf8&keepalive=1
user:password@/dbname user@unix(/path/to/socket)/dbname?charset=utf8
user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname user:password@tcp(localhost:5555)/dbname?charset=utf8&keepalive=1
user:password@/dbname
db.Prepare()函数用来返回准备要执行的sql操作然后返回准备完毕的执行状态。 user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname
db.Query()函数用来直接执行Sql返回Rows结果 db.Prepare()函数用来返回准备要执行的sql操作然后返回准备完毕的执行状态
stmt.Exec()函数用来执行stmt准备好的SQL语句 db.Query()函数用来直接执行Sql返回Rows结果。
我们可以看到我们传入的参数都是=?对应的数据这样做的方式可以一定程度上防止SQL注入。 stmt.Exec()函数用来执行stmt准备好的SQL语句
我们可以看到我们传入的参数都是=?对应的数据这样做的方式可以一定程度上防止SQL注入。
## links
* [目录](<preface.md>)
* 上一节: [database/sql接口](<5.1.md>) ## links
* 下一节: [使用SQLite数据库](<5.3.md>) * [目录](<preface.md>)
* 上一节: [database/sql接口](<5.1.md>)
* 下一节: [使用SQLite数据库](<5.3.md>)

234
5.3.md
View File

@@ -1,116 +1,118 @@
# 5.3使用SQLite数据库 # 5.3使用SQLite数据库
SQLite 是一个开源的嵌入式关系数据库实现自包容、零配置、支持事务的SQL数据库引擎。其特点是高度便携、使用方便、结构紧凑、高效、可靠。 与其他数据库管理系统不同SQLite 的安装和运行非常简单,在大多数情况下,只要确保SQLite的二进制文件存在即可开始创建、连接和使用数据库。如果您正在寻找一个嵌入式数据库项目或解决方案SQLite是绝对值得考虑。SQLite可以是说开源的Access。 SQLite 是一个开源的嵌入式关系数据库实现自包容、零配置、支持事务的SQL数据库引擎。其特点是高度便携、使用方便、结构紧凑、高效、可靠。 与其他数据库管理系统不同SQLite 的安装和运行非常简单,在大多数情况下,只要确保SQLite的二进制文件存在即可开始创建、连接和使用数据库。如果您正在寻找一个嵌入式数据库项目或解决方案SQLite是绝对值得考虑。SQLite可以是说开源的Access。
## 驱动 ## 驱动
Go支持sqlite的驱动也比较多但是好多都是不支持database/sql接口的 Go支持sqlite的驱动也比较多但是好多都是不支持database/sql接口的
- https://github.com/mattn/go-sqlite3 支持database/sql接口基于cgo(关于cgo的知识请参看官方文档或者本书后面的章节)写的 - https://github.com/mattn/go-sqlite3 支持database/sql接口基于cgo(关于cgo的知识请参看官方文档或者本书后面的章节)写的
- https://github.com/feyeleanor/gosqlite3 不支持database/sql接口基于cgo写的 - https://github.com/feyeleanor/gosqlite3 不支持database/sql接口基于cgo写的
- https://github.com/phf/go-sqlite3 不支持database/sql接口基于cgo写的 - https://github.com/phf/go-sqlite3 不支持database/sql接口基于cgo写的
目前支持database/sql的SQLite数据库驱动只有第一个我目前也是采用它来开发项目的。采用标准接口有利于以后出现更好的驱动的时候做迁移。 目前支持database/sql的SQLite数据库驱动只有第一个我目前也是采用它来开发项目的。采用标准接口有利于以后出现更好的驱动的时候做迁移。
## 实例代码 ## 实例代码
示例的数据库表结构如下所示相应的建表SQL 示例的数据库表结构如下所示相应的建表SQL
CREATE TABLE `userinfo` ( CREATE TABLE `userinfo` (
`uid` INTEGER PRIMARY KEY AUTOINCREMENT, `uid` INTEGER PRIMARY KEY AUTOINCREMENT,
`username` VARCHAR(64) NULL, `username` VARCHAR(64) NULL,
`departname` VARCHAR(64) NULL, `departname` VARCHAR(64) NULL,
`created` DATE NULL `created` DATE NULL
); );
CREATE TABLE `userdeatail` ( CREATE TABLE `userdeatail` (
`uid` INT(10) NULL, `uid` INT(10) NULL,
`intro` TEXT NULL, `intro` TEXT NULL,
`profile` TEXT NULL, `profile` TEXT NULL,
PRIMARY KEY (`uid`) PRIMARY KEY (`uid`)
); );
看下面Go程序是如何操作数据库表数据:增删改查 看下面Go程序是如何操作数据库表数据:增删改查
package main package main
import ( import (
"database/sql" "database/sql"
"fmt" "fmt"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )
func main() { func main() {
db, err := sql.Open("sqlite3", "./foo.db") db, err := sql.Open("sqlite3", "./foo.db")
checkErr(err) checkErr(err)
//插入数据 //插入数据
stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) values(?,?,?)") stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) values(?,?,?)")
checkErr(err) checkErr(err)
res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09") res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
checkErr(err) checkErr(err)
id, err := res.LastInsertId() id, err := res.LastInsertId()
checkErr(err) checkErr(err)
fmt.Println(id) fmt.Println(id)
//更新数据 //更新数据
stmt, err = db.Prepare("update userinfo set username=? where uid=?") stmt, err = db.Prepare("update userinfo set username=? where uid=?")
checkErr(err) checkErr(err)
res, err = stmt.Exec("astaxieupdate", id) res, err = stmt.Exec("astaxieupdate", id)
checkErr(err) checkErr(err)
affect, err := res.RowsAffected() affect, err := res.RowsAffected()
checkErr(err) checkErr(err)
fmt.Println(affect) fmt.Println(affect)
//查询数据 //查询数据
rows, err := db.Query("SELECT * FROM userinfo") rows, err := db.Query("SELECT * FROM userinfo")
checkErr(err) checkErr(err)
for rows.Next() { for rows.Next() {
var uid int var uid int
var username string var username string
var department string var department string
var created string var created string
err = rows.Scan(&uid, &username, &department, &created) err = rows.Scan(&uid, &username, &department, &created)
checkErr(err) checkErr(err)
fmt.Println(uid) fmt.Println(uid)
fmt.Println(username) fmt.Println(username)
fmt.Println(department) fmt.Println(department)
fmt.Println(created) fmt.Println(created)
} }
//删除数据 //删除数据
stmt, err = db.Prepare("delete from userinfo where uid=?") stmt, err = db.Prepare("delete from userinfo where uid=?")
checkErr(err) checkErr(err)
res, err = stmt.Exec(id) res, err = stmt.Exec(id)
checkErr(err) checkErr(err)
affect, err = res.RowsAffected() affect, err = res.RowsAffected()
checkErr(err) checkErr(err)
fmt.Println(affect) fmt.Println(affect)
} db.Close()
func checkErr(err error) { }
if err != nil {
panic(err) func checkErr(err error) {
} if err != nil {
} panic(err)
}
}
我们可以看到上面的代码和MySQL例子里面的代码几乎是一模一样的唯一改变的就是导入的驱动改变了然后调用`sql.Open`是采用了SQLite的方式打开。
我们可以看到上面的代码和MySQL例子里面的代码几乎是一模一样的唯一改变的就是导入的驱动改变了然后调用`sql.Open`是采用了SQLite的方式打开。
>sqlite管理工具http://sqliteadmin.orbmu2k.de/
>可以方便的新建数据库管理。 >sqlite管理工具http://sqliteadmin.orbmu2k.de/
## links >可以方便的新建数据库管理。
* [目录](<preface.md>)
* 上一节: [使用MySQL数据库](<5.2.md>) ## links
* 下一节: [使用PostgreSQL数据库](<5.4.md>) * [目录](<preface.md>)
* 上一节: [使用MySQL数据库](<5.2.md>)
* 下一节: [使用PostgreSQL数据库](<5.4.md>)

246
5.4.md
View File

@@ -1,122 +1,124 @@
# 5.4使用PostgreSQL数据库 # 5.4使用PostgreSQL数据库
PostgreSQL 是一个自由的对象-关系数据库服务器(数据库管理系统),它在灵活的 BSD-风格许可证下发行。它提供了相对其他开放源代码数据库系统(比如 MySQL 和 Firebird),和对专有系统比如 Oracle、Sybase、IBM 的 DB2 和 Microsoft SQL Server的一种选择。 PostgreSQL 是一个自由的对象-关系数据库服务器(数据库管理系统),它在灵活的 BSD-风格许可证下发行。它提供了相对其他开放源代码数据库系统(比如 MySQL 和 Firebird),和对专有系统比如 Oracle、Sybase、IBM 的 DB2 和 Microsoft SQL Server的一种选择。
PostgreSQL和MySQL比较它更加庞大一点因为它是用来替代Oracle而设计的。所以在企业应用中采用PostgreSQL是一个明智的选择。 PostgreSQL和MySQL比较它更加庞大一点因为它是用来替代Oracle而设计的。所以在企业应用中采用PostgreSQL是一个明智的选择。
现在MySQL被Oracle收购之后有传闻Oracle正在逐步的封闭MySQL,,鉴于此将来我们也许会选择PostgreSQL而不是MySQL作为项目的后端数据库。 现在MySQL被Oracle收购之后有传闻Oracle正在逐步的封闭MySQL,,鉴于此将来我们也许会选择PostgreSQL而不是MySQL作为项目的后端数据库。
## 驱动 ## 驱动
Go实现的支持PostgreSQL的驱动也很多因为国外很多人在开发中使用了这个数据库。 Go实现的支持PostgreSQL的驱动也很多因为国外很多人在开发中使用了这个数据库。
- https://github.com/bmizerany/pq 支持database/sql驱动纯Go写的 - https://github.com/bmizerany/pq 支持database/sql驱动纯Go写的
- https://github.com/jbarham/gopgsqldriver 支持database/sql驱动纯Go写的 - https://github.com/jbarham/gopgsqldriver 支持database/sql驱动纯Go写的
- https://github.com/lxn/go-pgsql 支持database/sql驱动纯Go写的 - https://github.com/lxn/go-pgsql 支持database/sql驱动纯Go写的
在下面的示例中我采用了第一个驱动因为它目前使用的人最多在github上也比较活跃。 在下面的示例中我采用了第一个驱动因为它目前使用的人最多在github上也比较活跃。
## 实例代码 ## 实例代码
数据库建表语句: 数据库建表语句:
CREATE TABLE userinfo CREATE TABLE userinfo
( (
uid serial NOT NULL, uid serial NOT NULL,
username character varying(100) NOT NULL, username character varying(100) NOT NULL,
departname character varying(500) NOT NULL, departname character varying(500) NOT NULL,
Created date, Created date,
CONSTRAINT userinfo_pkey PRIMARY KEY (uid) CONSTRAINT userinfo_pkey PRIMARY KEY (uid)
) )
WITH (OIDS=FALSE); WITH (OIDS=FALSE);
CREATE TABLE userdeatail CREATE TABLE userdeatail
( (
uid integer, uid integer,
intro character varying(100), intro character varying(100),
profile character varying(100) profile character varying(100)
) )
WITH(OIDS=FALSE); WITH(OIDS=FALSE);
看下面这个Go如何操作数据库表数据:增删改查 看下面这个Go如何操作数据库表数据:增删改查
package main package main
import ( import (
"database/sql" "database/sql"
"fmt" "fmt"
_ "github.com/bmizerany/pq" _ "github.com/bmizerany/pq"
) )
func main() { func main() {
db, err := sql.Open("postgres", "user=astaxie password=astaxie dbname=test sslmode=disable") db, err := sql.Open("postgres", "user=astaxie password=astaxie dbname=test sslmode=disable")
checkErr(err) checkErr(err)
//插入数据 //插入数据
stmt, err := db.Prepare("INSERT INTO userinfo(username,departname,created) VALUES($1,$2,$3) RETURNING uid") stmt, err := db.Prepare("INSERT INTO userinfo(username,departname,created) VALUES($1,$2,$3) RETURNING uid")
checkErr(err) checkErr(err)
res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09") res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
checkErr(err) checkErr(err)
//pg不支持这个函数因为他没有类似MySQL的自增ID //pg不支持这个函数因为他没有类似MySQL的自增ID
id, err := res.LastInsertId() id, err := res.LastInsertId()
checkErr(err) checkErr(err)
fmt.Println(id) fmt.Println(id)
//更新数据 //更新数据
stmt, err = db.Prepare("update userinfo set username=$1 where uid=$2") stmt, err = db.Prepare("update userinfo set username=$1 where uid=$2")
checkErr(err) checkErr(err)
res, err = stmt.Exec("astaxieupdate", 1) res, err = stmt.Exec("astaxieupdate", 1)
checkErr(err) checkErr(err)
affect, err := res.RowsAffected() affect, err := res.RowsAffected()
checkErr(err) checkErr(err)
fmt.Println(affect) fmt.Println(affect)
//查询数据 //查询数据
rows, err := db.Query("SELECT * FROM userinfo") rows, err := db.Query("SELECT * FROM userinfo")
checkErr(err) checkErr(err)
for rows.Next() { for rows.Next() {
var uid int var uid int
var username string var username string
var department string var department string
var created string var created string
err = rows.Scan(&uid, &username, &department, &created) err = rows.Scan(&uid, &username, &department, &created)
checkErr(err) checkErr(err)
fmt.Println(uid) fmt.Println(uid)
fmt.Println(username) fmt.Println(username)
fmt.Println(department) fmt.Println(department)
fmt.Println(created) fmt.Println(created)
} }
//删除数据 //删除数据
stmt, err = db.Prepare("delete from userinfo where uid=$1") stmt, err = db.Prepare("delete from userinfo where uid=$1")
checkErr(err) checkErr(err)
res, err = stmt.Exec(1) res, err = stmt.Exec(1)
checkErr(err) checkErr(err)
affect, err = res.RowsAffected() affect, err = res.RowsAffected()
checkErr(err) checkErr(err)
fmt.Println(affect) fmt.Println(affect)
} db.Close()
func checkErr(err error) { }
if err != nil {
panic(err) func checkErr(err error) {
} if err != nil {
} panic(err)
}
从上面的代码我们可以看到PostgreSQL是通过`$1`,`$2`这种方式来指定要传递的参数而不是MySQL中的`?`另外在sql.Open中的dsn信息的格式也与MySQL的驱动中的dsn格式不一样所以在使用时请注意它们的差异。 }
还有pg不支持LastInsertId函数因为PostgreSQL内部没有实现类似MySQL的自增ID返回其他的代码几乎是一模一样 从上面的代码我们可以看到PostgreSQL是通过`$1`,`$2`这种方式来指定要传递的参数而不是MySQL中的`?`另外在sql.Open中的dsn信息的格式也与MySQL的驱动中的dsn格式不一样所以在使用时请注意它们的差异
## links 还有pg不支持LastInsertId函数因为PostgreSQL内部没有实现类似MySQL的自增ID返回其他的代码几乎是一模一样。
* [目录](<preface.md>)
* 上一节: [使用SQLite数据库](<5.3.md>) ## links
* 下一节: [使用beedb库进行ORM开发](<5.5.md>) * [目录](<preface.md>)
* 上一节: [使用SQLite数据库](<5.3.md>)
* 下一节: [使用beedb库进行ORM开发](<5.5.md>)