连接

步骤:

  1. 依赖下载:go get -u github.com/go-sql-driver/mysql

  2. 代码 ```go import ( “database/sql” _ “github.com/go-sql-driver/mysql” // init() )

var db *sql.DB

func initDB() (err error) { dsn := “root:204079790@tcp(127.0.0.1:3306)/demo” db, err = sql.Open(“mysql”, dsn) if err != nil { return }

  1. err = db.Ping()
  2. if err != nil {
  3. return
  4. }
  5. return nil

}

func main() { err := initDB() if err != nil { panic(err) } }

  1. <a name="cxvX6"></a>
  2. # MySQL驱动
  3. init()函数位于:github.com/go-sql-driver/mysql/driver.go
  4. ```go
  5. func init() {
  6. sql.Register("mysql", &MySQLDriver{})
  7. }

database/sql.go

  1. var (
  2. driversMu sync.RWMutex
  3. drivers = make(map[string]driver.Driver)
  4. )
  5. ...
  6. ...
  7. type DB struct {
  8. // Atomic access only. At top of struct to prevent mis-alignment
  9. // on 32-bit platforms. Of type time.Duration.
  10. waitDuration int64 // 连接等待时间
  11. connector driver.Connector
  12. // numClosed is an atomic counter which represents a total number of
  13. // closed connections. Stmt.openStmt checks it before cleaning closed
  14. // connections in Stmt.css.
  15. numClosed uint64
  16. mu sync.Mutex // protects following fields
  17. freeConn []*driverConn
  18. connRequests map[uint64]chan connRequest
  19. nextRequest uint64 // Next key to use in connRequests.
  20. numOpen int // number of opened and pending open connections
  21. // Used to signal the need for new connections
  22. // a goroutine running connectionOpener() reads on this chan and
  23. // maybeOpenNewConnections sends on the chan (one send per needed connection)
  24. // It is closed during db.Close(). The close tells the connectionOpener
  25. // goroutine to exit.
  26. openerCh chan struct{}
  27. closed bool
  28. dep map[finalCloser]depSet
  29. lastPut map[*driverConn]string // stacktrace of last conn's put; debug only
  30. maxIdleCount int // zero means defaultMaxIdleConns; negative means 0
  31. maxOpen int // <= 0 means unlimited
  32. maxLifetime time.Duration // maximum amount of time a connection may be reused
  33. maxIdleTime time.Duration // maximum amount of time a connection may be idle before being closed
  34. cleanerCh chan struct{}
  35. waitCount int64 // Total number of connections waited for.
  36. maxIdleClosed int64 // Total number of connections closed due to idle count.
  37. maxIdleTimeClosed int64 // Total number of connections closed due to idle time.
  38. maxLifetimeClosed int64 // Total number of connections closed due to max connection lifetime limit.
  39. stop func() // stop cancels the connection opener.
  40. }
  41. ...
  42. ...
  43. // 将驱动名注册到全局drivers中
  44. func Register(name string, driver driver.Driver) {
  45. driversMu.Lock()
  46. defer driversMu.Unlock()
  47. if driver == nil {
  48. panic("sql: Register driver is nil")
  49. }
  50. if _, dup := drivers[name]; dup {
  51. panic("sql: Register called twice for driver " + name)
  52. }
  53. drivers[name] = driver
  54. }
  55. ...
  56. ...
  57. func OpenDB(c driver.Connector) *DB {
  58. ctx, cancel := context.WithCancel(context.Background())
  59. db := &DB{
  60. connector: c,
  61. openerCh: make(chan struct{}, connectionRequestQueueSize),
  62. lastPut: make(map[*driverConn]string),
  63. connRequests: make(map[uint64]chan connRequest),
  64. stop: cancel,
  65. }
  66. go db.connectionOpener(ctx)
  67. return db
  68. }
  69. ...
  70. ...
  71. // 输入驱动数据库名和DSN,返回一个DB对象
  72. func Open(driverName, dataSourceName string) (*DB, error) {
  73. driversMu.RLock()
  74. driveri, ok := drivers[driverName]
  75. driversMu.RUnlock()
  76. if !ok {
  77. return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
  78. }
  79. if driverCtx, ok := driveri.(driver.DriverContext); ok {
  80. connector, err := driverCtx.OpenConnector(dataSourceName)
  81. if err != nil {
  82. return nil, err
  83. }
  84. return OpenDB(connector), nil
  85. }
  86. return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil
  87. }

CRUD

Creat

  1. func insertRow() {
  2. sqlStr := `INSERT INTO user(age,name) values(?,?)`
  3. ret, err := db.Exec(sqlStr, 1, "富贵🐷")
  4. if err != nil {
  5. fmt.Printf("insert failed, err: %v\n", err)
  6. }
  7. ID, err := ret.LastInsertId()
  8. if err != nil {
  9. fmt.Printf("get LastId failed, err: %v\n", err)
  10. } else {
  11. fmt.Printf("insert success, the id is: %d.\n", ID)
  12. }
  13. }

Retrieve

单行查询

  1. func queryRow() {
  2. sqlStr := `select id, name, age from user where id = ?`
  3. var u User
  4. err := db.QueryRow(sqlStr, 2).Scan(&u.Id, &u.Name, &u.Age)
  5. if err != nil {
  6. fmt.Printf("scan failed, err: %v\n", err)
  7. } else {
  8. fmt.Printf("id:%d, name:%s, age:%d\n", u.Id, u.Name, u.Age)
  9. }
  10. }

多行查询

  1. func queryRows() {
  2. sqlStr := `select id, name, age from user where ID > ?`
  3. rows, err := db.Query(sqlStr, 0)
  4. if err != nil {
  5. fmt.Printf("query failed, err: %v\n", err)
  6. }
  7. // 关闭rows,释放连接
  8. defer rows.Close()
  9. for rows.Next() {
  10. var u User
  11. err := rows.Scan(&u.Id, &u.Name, &u.Age)
  12. if err != nil {
  13. fmt.Printf("scan failed, err: %v\n", err)
  14. return
  15. }
  16. fmt.Printf("id:%d, name:%s, age:%d\n", u.Id, u.Name, u.Age)
  17. }
  18. }

Update

  1. func updateRow() {
  2. sqlStr := `update user set age=? where id=?`
  3. ret, err := db.Exec(sqlStr, 3, 2)
  4. if err != nil {
  5. fmt.Printf("update failed, err: %v\n", err)
  6. return
  7. }
  8. n, err := ret.RowsAffected() // 操作影响的行数
  9. if err != nil {
  10. fmt.Printf("get RowsAffected failed, err: %v\n", err)
  11. return
  12. }
  13. fmt.Printf("update success, %d rows are affected\n", n)
  14. }

Delete

  1. func deleteRow() {
  2. sqlErr := `DELETE FROM user WHERE id=?`
  3. ret, err := db.Exec(sqlErr, 2)
  4. if err != nil {
  5. fmt.Printf("delete failed, err: %V\n", err)
  6. return
  7. }
  8. n, err := ret.RowsAffected() // 操作影响的行数
  9. if err != nil {
  10. fmt.Printf("get RowsAffected failed, err: %v\n", err)
  11. return
  12. }
  13. fmt.Printf("delete success, %d rows are affected\n", n)
  14. }

MySQL预处理

什么是预处理?

普通SQL语句执行过程:

  1. 客户端对SQL语句进行占位符替换得到完整的SQL语句。
  2. 客户端发送完整SQL语句到MySQL服务端
  3. MySQL服务端执行完整的SQL语句并将结果返回给客户端。

预处理执行过程:

  1. 把SQL语句分成两部分,命令部分与数据部分。
  2. 先把命令部分发送给MySQL服务端,MySQL服务端进行SQL预处理。
  3. 然后把数据部分发送给MySQL服务端,MySQL服务端对SQL语句进行占位符替换。
  4. MySQL服务端执行完整的SQL语句并将结果返回给客户端。

    为什么要预处理?

  5. 优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本。
  6. 避免SQL注入问题。

语法

  1. // 预处理查询
  2. func preparedQuery() {
  3. sqlStr := "SELECT id, title, status FROM todolist WHERE id > ?"
  4. stmt, err := db.Prepare(sqlStr)
  5. if err != nil {
  6. fmt.Printf("prepare failed, err: %v", err)
  7. }
  8. defer stmt.Close()
  9. rows, err := stmt.Query(0)
  10. if err != nil {
  11. fmt.Printf("query failed, err: %v", err)
  12. }
  13. defer rows.Close()
  14. for rows.Next() {
  15. var l todolist
  16. err := rows.Scan(&l.id, &l.title, &l.status)
  17. if err != nil {
  18. fmt.Printf("scan failed, err: %v", err)
  19. }
  20. fmt.Println(l)
  21. }
  22. }

sql注入

什么是sql注入?
当用户端输入sql语句,可能导致用户直接操作或访问服务端数据库的漏洞。

举例:

  1. func sqlInjectDemo(name string) {
  2. sqlStr := fmt.Sprintf("select id, name, age from user where name='%s'", name)
  3. fmt.Printf("SQL:%s\n", sqlStr)
  4. var u user
  5. err := db.QueryRow(sqlStr).Scan(&u.id, &u.name, &u.age)
  6. if err != nil {
  7. fmt.Printf("exec failed, err:%v\n", err)
  8. return
  9. }
  10. fmt.Printf("user:%#v\n", u)
  11. }
  12. func main() {
  13. sqlInjectDemo("xxx' or 1=1#")
  14. sqlInjectDemo("xxx' union select * from user #")
  15. sqlInjectDemo("xxx' and (select count(*) from user) <10 #")
  16. }

事务

事务相关概念见:https://www.yuque.com/u21069870/htoiko/ccvl1x#PTbOn

  1. // 事务操作示例
  2. func transactionDemo() {
  3. // 开启事务
  4. tx, err := db.Begin()
  5. if err != nil {
  6. if tx != nil {
  7. tx.Rollback() // 回滚
  8. }
  9. fmt.Printf("begin trans failed, err:%v\n", err)
  10. return
  11. }
  12. // 第一次更新数据
  13. sqlStr1 := "Update user set age=30 where id=?"
  14. ret1, err := tx.Exec(sqlStr1, 2)
  15. if err != nil {
  16. tx.Rollback() // 回滚
  17. fmt.Printf("exec sql1 failed, err:%v\n", err)
  18. return
  19. }
  20. affRow1, err := ret1.RowsAffected()
  21. if err != nil {
  22. tx.Rollback() // 回滚
  23. fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n", err)
  24. return
  25. }
  26. // 第二次更新数据
  27. sqlStr2 := "Update user set age=40 where id=?"
  28. ret2, err := tx.Exec(sqlStr2, 3)
  29. if err != nil {
  30. tx.Rollback() // 回滚
  31. fmt.Printf("exec sql2 failed, err:%v\n", err)
  32. return
  33. }
  34. affRow2, err := ret2.RowsAffected()
  35. if err != nil {
  36. tx.Rollback() // 回滚
  37. fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n", err)
  38. return
  39. }
  40. // 确保事务的一致性
  41. if affRow1 == 1 && affRow2 == 1 {
  42. fmt.Println("事务提交...")
  43. tx.Commit() // 提交事务
  44. } else {
  45. tx.Rollback()
  46. fmt.Println("事务回滚...")
  47. }
  48. }