数据库访问需要用到标准库database/sql和mysql的驱动”github.com/go-sql-driver/mysql”。这两个包都需要引用。mysql的驱动因为只是需要它的init()初始化,所以需要采用下划线的方式。

  1. import (
  2. "database/sql"
  3. _"github.com/go-sql-driver/mysql"
  4. "fmt"
  5. "log"
  6. )

在访问数据库前,我们先在MySql里建好表并预先插入一些数据以便测试程序。请在Mysql里执行下面的SQL脚本。

  1. -- ----------------------------
  2. -- Table structure for announcement
  3. -- ----------------------------
  4. DROP TABLE IF EXISTS `announcement`;
  5. CREATE TABLE `announcement` (
  6. `id` int(11) NOT NULL AUTO_INCREMENT,
  7. `imgUrl` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL,
  8. `detailUrl` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL,
  9. `createDate` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL,
  10. `state` int(11) DEFAULT NULL,
  11. PRIMARY KEY (`id`)
  12. ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  13. -- ----------------------------
  14. -- Records of announcement
  15. -- ----------------------------
  16. INSERT INTO `announcement` VALUES ('1', '/visitshop/img/ann/ann1.jpg', null, '2016-07-20', '0');
  17. INSERT INTO `announcement` VALUES ('2', '/visitshop//img/ann/ann1.jpg', null, '2016-07-20', '0');
  18. INSERT INTO `announcement` VALUES ('3', '/visitshop//img/ann/ann1.jpg', null, '2016-07-20', '0');
  19. INSERT INTO `announcement` VALUES ('4', '/visitshop//img/ann/ann1.jpg', null, '2016-07-20', '0');

我们将在主函数里调用增删该查函数。先预设这四种操作的函数名

  1. query() //查询
  2. query2() //查询
  3. insert() //插入
  4. update() //修改
  5. remove() //删除

然后分别实现这几个函数。

查询

首先是query()查询函数。
要想访问数据库,首先要打开数据库链接。这就需要用到database/sql Open函数。

  1. db, err := sql.Open("mysql", "root:@/shopvisit")

我们的数据库链接是这个样子的。为了简化操作,我的数据库用户 root 是没有密码的。而正常的数据库链接应该是这个样子的

  1. db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/shopvisit")

这个连接说明数据库用户root的密码是123456,使用tcp协议,数据库ip地址是127.0.0.1,使用3306端口作为通讯端口。当前使用的库名是shopvisit。
当然链接数据库的方式其实是有好几种的

  1. user@unix(/path/to/socket)/dbname?charset=utf8
  2. user:password@tcp(localhost:5555)/dbname?charset=utf8
  3. user:password@/dbname
  4. user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname

既然有了数据库连接的语句,就要有错误检查。而错误检查会比较频繁的出现,所以写一个函数来直接处理它。

  1. func check(err error) {
  2. if err != nil{
  3. fmt.Println(err)
  4. panic(err)
  5. }
  6. }

这样每当需要对错误进程检查的时候,就执行check(err)。
当db链接数据库正常之后,我们就可以执行sql查询语句了。

  1. rows, err := db.Query("SELECT * FROM shopvisit.announcement")
  2. check(err)

然后用for循环遍历返回的结果。

  1. for rows.Next() {

在循环体内,我们先取得记录的列(字段),把列名参数的值和列地址关联。

  1. columns, _ := rows.Columns()
  2. scanArgs := make([]interface{}, len(columns))
  3. values := make([]interface{}, len(columns))
  4. for i := range values {
  5. scanArgs[i] = &values[i]
  6. }

再把数据保存到record字典中

  1. //将数据保存到 record 字典
  2. err = rows.Scan(scanArgs...)
  3. record := make(map[string]string)
  4. for i, col := range values {
  5. if col != nil {
  6. record[columns[i]] = string(col.([]byte))
  7. }
  8. }

打印记录fmt.Println(record)之后,一定要记得释放资源

  1. rows.Close()

query()完整代码

  1. func query() {
  2. db, err := sql.Open("mysql", "root:@/shopvisit")
  3. check(err)
  4. rows, err := db.Query("SELECT * FROM shopvisit.announcement")
  5. check(err)
  6. for rows.Next() {
  7. columns, _ := rows.Columns()
  8. scanArgs := make([]interface{}, len(columns))
  9. values := make([]interface{}, len(columns))
  10. for i := range values {
  11. scanArgs[i] = &values[i]
  12. }
  13. //将数据保存到 record 字典
  14. err = rows.Scan(scanArgs...)
  15. record := make(map[string]string)
  16. for i, col := range values {
  17. if col != nil {
  18. record[columns[i]] = string(col.([]byte))
  19. }
  20. }
  21. fmt.Println(record)
  22. }
  23. rows.Close()
  24. }

在main函数中,注释掉其他函数,只留下query(),运行看结果。多运行几次,比较每次运行结果。

  1. 第一次运行
  2. map[state:0 id:1 imgUrl:/visitshop/img/ann/ann1.jpg createDate:2016-07-20]
  3. map[id:2 imgUrl:/visitshop//img/ann/ann1.jpg createDate:2016-07-20 state:0]
  4. map[id:3 imgUrl:/visitshop//img/ann/ann1.jpg createDate:2016-07-20 state:0]
  5. map[createDate:2016-07-20 state:0 id:4 imgUrl:/visitshop//img/ann/ann1.jpg]
  6. 第二次运行
  7. map[id:1 imgUrl:/visitshop/img/ann/ann1.jpg createDate:2016-07-20 state:0]
  8. map[id:2 imgUrl:/visitshop//img/ann/ann1.jpg createDate:2016-07-20 state:0]
  9. map[id:3 imgUrl:/visitshop//img/ann/ann1.jpg createDate:2016-07-20 state:0]
  10. map[id:4 imgUrl:/visitshop//img/ann/ann1.jpg createDate:2016-07-20 state:0]
  11. 第三次运行
  12. map[createDate:2016-07-20 state:0 id:1 imgUrl:/visitshop/img/ann/ann1.jpg]
  13. map[id:2 imgUrl:/visitshop//img/ann/ann1.jpg createDate:2016-07-20 state:0]
  14. map[imgUrl:/visitshop//img/ann/ann1.jpg createDate:2016-07-20 state:0 id:3]
  15. map[id:4 imgUrl:/visitshop//img/ann/ann1.jpg createDate:2016-07-20 state:0]

为什么每次都不一样呢?这是因为我们使用了 map 字典来保存列。map 是无序的,所以每次都是随机的显示顺序。
这显然不符合我们一般的结果需要,那么,我们来编写 query2()。
仍然是链接数据库的语句作为开端,再跟着是查询语句。

  1. db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/shopvisit?charset=utf8")
  2. check(err)
  3. rows, err := db.Query("SELECT id,imgUrl,createDate,state FROM announcement")
  4. check(err)

既然已经查询无错了,那么直接for循环记录结果

  1. for rows.Next(){
  2. var id int
  3. var state int
  4. var imgUrl string
  5. var createDate string
  6. //注意这里的Scan括号中的参数顺序,和 SELECT 的字段顺序要保持一致。
  7. if err := rows.Scan(&id,&imgUrl,&createDate,&state); err != nil {
  8. log.Fatal(err)
  9. }
  10. fmt.Printf("%s id is %d on %s with state %d\n", imgUrl, id, createDate, state)
  11. }

每一次循环的时候,我们都可以按照我们想要的顺序,取得所有的字段值。唯一需要注意的是,rows.Scan的参数顺序,需要和select语句的字段保持顺序一致。这里主要指的是数据类型。参数名可以不同。

做个比较

  1. db.Query("SELECT id,imgUrl,createDate,state FROM announcement")
  2. rows.Scan(&id,&imgUrl,&createDate,&state);

&符号是取变量的地址,注意观察变量的数据类型和select后面参数的数据类型必须是一致的。声明变量时的顺序无所谓,Scan调用变量时的顺序要注意select参数的顺序一致。

  1. var id int
  2. var state int
  3. var imgUrl string
  4. var createDate string

然后可以按照你想要的顺序打印输出

  1. fmt.Printf("%s id is %d on %s with state %d\n", imgUrl, id, createDate, state)

query2()完整代码

  1. func query() {
  2. db, err := sql.Open("mysql", "root:@/shopvisit")
  3. check(err)
  4. rows, err := db.Query("SELECT * FROM shopvisit.announcement")
  5. check(err)
  6. for rows.Next() {
  7. columns, _ := rows.Columns()
  8. scanArgs := make([]interface{}, len(columns))
  9. values := make([]interface{}, len(columns))
  10. for i := range values {
  11. scanArgs[i] = &values[i]
  12. }
  13. //将数据保存到 record 字典
  14. err = rows.Scan(scanArgs...)
  15. record := make(map[string]string)
  16. for i, col := range values {
  17. if col != nil {
  18. record[columns[i]] = string(col.([]byte))
  19. }
  20. }
  21. fmt.Println(record)
  22. }
  23. rows.Close()
  24. }
  25. func query2() {
  26. fmt.Println("Query2")
  27. db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/shopvisit?charset=utf8")
  28. check(err)
  29. rows, err := db.Query("SELECT id,imgUrl,createDate,state FROM announcement")
  30. check(err)
  31. for rows.Next(){
  32. var id int
  33. var state int
  34. var imgUrl string
  35. var createDate string
  36. //注意这里的Scan括号中的参数顺序,和 SELECT 的字段顺序要保持一致。
  37. if err := rows.Scan(&id,&imgUrl,&createDate,&state); err != nil {
  38. log.Fatal(err)
  39. }
  40. fmt.Printf("%s id is %d on %s with state %d\n", imgUrl, id, createDate, state)
  41. }
  42. if err := rows.Err(); err != nil {
  43. log.Fatal(err)
  44. }
  45. rows.Close()
  46. }

修改main函数中的当前可执行函数query2(),运行结果如下:

  1. Query2
  2. /visitshop/img/ann/ann1.jpg id is 1 on 2016-07-20 with state 0
  3. /visitshop//img/ann/ann1.jpg id is 2 on 2016-07-20 with state 0
  4. /visitshop//img/ann/ann1.jpg id is 3 on 2016-07-20 with state 0
  5. /visitshop//img/ann/ann1.jpg id is 4 on 2016-07-20 with state 0

插入

插入数据insert()函数,链接数据库还是一样的,而db调用的函数改为Prepare。执行的sql语句需要这样写

  1. stmt, err := db.Prepare(`INSERT announcement (imgUrl, detailUrl, createDate, state) VALUES (?, ?, ?, ?)`)
  2. check(err)

插入的值,必须按照sql顺序来插入

  1. res, err := stmt.Exec("/visitshop/img/ann/cofox1.png",nil,"2017-09-06",0)
  2. check(err)

返回刚插入的这条记录的id

  1. id, err := res.LastInsertId()
  2. check(err)

insert()完整带代码

  1. func insert() {
  2. db, err := sql.Open("mysql", "root:@/shopvisit")
  3. check(err)
  4. stmt, err := db.Prepare(`INSERT announcement (imgUrl, detailUrl, createDate, state) VALUES (?, ?, ?, ?)`)
  5. check(err)
  6. res, err := stmt.Exec("/visitshop/img/ann/cofox1.png",nil,"2017-09-06",0)
  7. check(err)
  8. id, err := res.LastInsertId()
  9. check(err)
  10. fmt.Println(id)
  11. stmt.Close()
  12. }

修改

修改函数和插入函数结构类似,sql语句不同

  1. stmt, err := db.Prepare("UPDATE announcement set imgUrl=?, detailUrl=?, createDate=?, state=? WHERE id=?")
  2. check(err)

那么参数语句也要增加个id值(注意id参数是你要修改的那条记录的id)

  1. res, err := stmt.Exec("/visitshop/img/ann/cofox2.png", nil, "2017-09-05", 1, 7)
  2. check(err)

修改的结果返回语句就也调用了不同的函数res.RowsAffected

  1. num, err := res.RowsAffected()
  2. check(err)

完整的修改函数代码

  1. func update() {
  2. db, err := sql.Open("mysql", "root:@/shopvisit")
  3. check(err)
  4. stmt, err := db.Prepare("UPDATE announcement set imgUrl=?, detailUrl=?, createDate=?, state=? WHERE id=?")
  5. check(err)
  6. res, err := stmt.Exec("/visitshop/img/ann/cofox2.png", nil, "2017-09-05", 1, 7)
  7. check(err)
  8. num, err := res.RowsAffected()
  9. check(err)
  10. fmt.Println(num)
  11. stmt.Close()
  12. }

删除

删除函数的代码与修改函数的代码比较起来就只有sql语句好参数的差别了,其他的完全一样。

完整的删除函数的代码

  1. func remove() {
  2. db, err := sql.Open("mysql", "root:@/shopvisit")
  3. check(err)
  4. stmt, err := db.Prepare("DELETE FROM announcement WHERE id=?")
  5. check(err)
  6. res, err := stmt.Exec(7)
  7. check(err)
  8. num, err := res.RowsAffected()
  9. check(err)
  10. fmt.Println(num)
  11. stmt.Close()
  12. }

完整的代码实例

  1. package main
  2. import (
  3. "database/sql"
  4. _"github.com/go-sql-driver/mysql"
  5. "fmt"
  6. "log"
  7. )
  8. func main() {
  9. query()
  10. //query2()
  11. //insert()
  12. //update()
  13. //remove()
  14. }
  15. //查询数据
  16. func query() {
  17. db, err := sql.Open("mysql", "root:@/shopvisit")
  18. check(err)
  19. rows, err := db.Query("SELECT * FROM shopvisit.announcement")
  20. check(err)
  21. for rows.Next() {
  22. columns, _ := rows.Columns()
  23. scanArgs := make([]interface{}, len(columns))
  24. values := make([]interface{}, len(columns))
  25. for i := range values {
  26. scanArgs[i] = &values[i]
  27. }
  28. //将数据保存到 record 字典
  29. err = rows.Scan(scanArgs...)
  30. record := make(map[string]string)
  31. for i, col := range values {
  32. if col != nil {
  33. record[columns[i]] = string(col.([]byte))
  34. }
  35. }
  36. fmt.Println(record)
  37. }
  38. rows.Close()
  39. }
  40. func query2() {
  41. fmt.Println("Query2")
  42. db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/shopvisit?charset=utf8")
  43. check(err)
  44. rows, err := db.Query("SELECT id,imgUrl,createDate,state FROM announcement")
  45. check(err)
  46. for rows.Next(){
  47. var id int
  48. var state int
  49. var imgUrl string
  50. var createDate string
  51. //注意这里的Scan括号中的参数顺序,和 SELECT 的字段顺序要保持一致。
  52. if err := rows.Scan(&id,&imgUrl,&createDate,&state); err != nil {
  53. log.Fatal(err)
  54. }
  55. fmt.Printf("%s id is %d on %s with state %d\n", imgUrl, id, createDate, state)
  56. }
  57. if err := rows.Err(); err != nil {
  58. log.Fatal(err)
  59. }
  60. rows.Close()
  61. }
  62. //插入数据
  63. func insert() {
  64. db, err := sql.Open("mysql", "root:@/shopvisit")
  65. check(err)
  66. stmt, err := db.Prepare(`INSERT announcement (imgUrl, detailUrl, createDate, state) VALUES (?, ?, ?, ?)`)
  67. check(err)
  68. res, err := stmt.Exec("/visitshop/img/ann/cofox1.png",nil,"2017-09-06",0)
  69. check(err)
  70. id, err := res.LastInsertId()
  71. check(err)
  72. fmt.Println(id)
  73. stmt.Close()
  74. }
  75. //修改数据
  76. func update() {
  77. db, err := sql.Open("mysql", "root:@/shopvisit")
  78. check(err)
  79. stmt, err := db.Prepare("UPDATE announcement set imgUrl=?, detailUrl=?, createDate=?, state=? WHERE id=?")
  80. check(err)
  81. res, err := stmt.Exec("/visitshop/img/ann/cofox2.png", nil, "2017-09-05", 1, 7)
  82. check(err)
  83. num, err := res.RowsAffected()
  84. check(err)
  85. fmt.Println(num)
  86. stmt.Close()
  87. }
  88. //删除数据
  89. func remove() {
  90. db, err := sql.Open("mysql", "root:@/shopvisit")
  91. check(err)
  92. stmt, err := db.Prepare("DELETE FROM announcement WHERE id=?")
  93. check(err)
  94. res, err := stmt.Exec(7)
  95. check(err)
  96. num, err := res.RowsAffected()
  97. check(err)
  98. fmt.Println(num)
  99. stmt.Close()
  100. }
  101. func check(err error) {
  102. if err != nil{
  103. fmt.Println(err)
  104. panic(err)
  105. }
  106. }

Golang 访问Mysql数据库实现增删改查 - 图1