1:安装golang数据库操作的第三方数据库操作驱动
sudo go get github.com/go-sql-driver/mysql
2:连接数据库操作函数
Open() – creates a DB连接
Close() - closes the DB连接
函数原型
func Open(driverName, dataSourceName string) (*DB, error)
driverName: 使用的驱动名. 这个名字其实就是数据库驱动注册到 database/sql 时所使用的名字.
dataSourceName: 数据库连接信息,这个连接包含了数据库的用户名, 密码, 数据库主机以及需要连接的数据库名等信息.
1.sql.Open并不会立即建立一个数据库的网络连接, 也不会对数据库链接参数的合法性做检验, 它仅仅是初始化一个sql.DB对象. 当真正进行第一次数据库查询操作时, 此时才会真正建立网络连接;
2.sql.DB表示操作数据库的抽象接口的对象,但不是所谓的数据库连接对象,sql.DB对象只有当需要使用时才会创建连接,如果想立即验证连接,需要用Ping()方法;
3.sql.Open返回的sql.DB对象是协程并发安全的.
4.sql.DB的设计就是用来作为长连接使用的。不要频繁Open, Close。比较好的做法是,为每个不同的datastore建一个DB对象,保持这些对象Open。如果需要短连接,那么把DB作为参数传入function,而不要在function中Open, Close。
3:连接数据库的格式为
username:password@protocol(address)/dbname?param=value
1
示例代码:
db, err = sql.Open("mysql", "root:123456@tcp(192.168.2.225:3306)/mysql?charset=utf8")
if err != nil {
fmt.Println("failed to open database:", err.Error())
return
}
defer db.Close()
4:执行sql
DB中执行SQL通过Exec和Query方法,查询操作是通过Query完成,它会返回一个sql.Rows的结果集,包含一个游标用来遍历查询结果;Exec方法返回的是sql.Result对象,用于检测操作结果,及被影响记录数
创建数据库表:
_, err := db.Exec("CREATE DATABASE IF NOT EXISTS my_db;")
if err != nil {
fmt.Println("failed to create databases",err.Error())
return
}
_, err = db.Exec("USE my_db;")
if err != nil {
fmt.Println("select database failed")
return
}
_, err = db.Exec("CREATE TABLE IF NOT EXISTS tb_user(id int(10) primary key,name varchar(20),age int(10),sex varchar(5),addr varchar(64),tel varchar(11));")
if err != nil {
fmt.Println("create table failed:", err.Error())
return
}
插入:
result, err := db.Exec("INSERT INTO `tb_user`(`id`,`name`,`age`,`sex`,`addr`,`tel`) values(1024,'ALICE',20,'WOMAN','SZ','123')")
if err != nil {
fmt.Println("insert data failed:", err.Error())
return
}
id, err := result.LastInsertId()
if err != nil {
fmt.Println("fetch last insert id failed:", err.Error())
return
}
if RowAffect, err := ret.RowsAffected(); nil == err {
fmt.Println("RowAffect", RowAffect)
}
fmt.Println("insert new record", id)
查询
rows, err := db.Query("SELECT * FROM tb_user")
if err != nil {
fmt.Println("fetech data failed:", err.Error())
return
}
defer rows.Close()
for rows.Next(){
Id,Age uint32
Name,Sex,Addr,Tel string
rows.Scan(&Id, &d.Name, &Age,&Sex, &Addr, &Tel)
fmt.Println("get data ,id:", Id, "name:",Name.String, "age:", Age,"sex:",Sex, "addr:", Addr.String, "tel:",Tel.String)
}
1:rows.Scan 参数的顺序很重要, 需要和查询的结果的column对应. 例如 “SELECT * From tb_user where age >=20 AND age < 30” 查询的行的 column 顺序是 “id, name, age,sex,addr,tel” 和插入操作顺序相同, 因此 rows.Scan 也需要按照此顺序 rows.Scan(&id, &name, &age,&sex,&addr,&tel), 不然会造成数据读取的错位.
2:因为golang是强类型语言,所以查询数据时先定义数据类型,但是查询数据库中的数据存在三种可能:存在值,存在零值,未赋值NULL 三种状态,
因为可以将待查询的数据类型定义为sql.Nullxxx类型,可以通过判断Valid值来判断查询到的值是否为赋值状态还是未赋值NULL状态.
3:每次db.Query操作后, 都建议调用rows.Close(). 因为 db.Query() 会从数据库连接池中获取一个连接,
这个底层连接在结果集(rows)未关闭前会被标记为处于繁忙状态。当遍历读到最后一条记录时,会发生一个内部EOF错误,自动调用rows.Close(),但如果提前退出循环,rows不会关闭,连接不会回到连接池中,连接也不会关闭,
则此连接会一直被占用. 因此通常我们使用 defer rows.Close() 来确保数据库连接可以正确放回到连接池中;
不过阅读源码发现rows.Close()操作是幂等操作,即一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同,
所以即便对已关闭的rows再执行close()也没关系.
更新
result, err := db.Exec("UPDATE `tb_user` SET `name`=? WHERE `id`=?", "tom", 1024)
if err != nil {
fmt.Println("update error", err.Error())
return
}
num, err := result.RowsAffected()
fmt.Println(num)
5:预编译语句
sql.Stmt支持预备表达式,可以用来优化SQL查询提高性能,减少SQL注入的风险, DB.Prepare()和Tx.Prepare()都提供了对于预备表达式的支持:
PreparedStatement 可以实现自定义参数的查询
PreparedStatement 通常来说, 比手动拼接字符串 SQL 语句高效.
PreparedStatement 可以防止SQL注入攻击
一般用Prepared Statements和Exec()完成INSERT, UPDATE, DELETE操作。
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
type DBTask struct {
Dbconnection *sql.DB
UserInfomation UserTableStruct
}
type UserTableStruct struct {
Id sql.NullInt64
Name sql.NullString
Age sql.NullInt64
Sex sql.NullString
Addr sql.NullString
Tel sql.NullString
}
func main() {
dbTask := &DBTask{}
var err error
dbTask.Dbconnection, err = sql.Open("mysql", "root:123456@tcp(192.168.2.225:3306)/mysql?charset=utf8")
if err != nil {
fmt.Println("failed to open database:", err.Error())
return
}
defer dbTask.Dbconnection.Close()
dbTask.CreateDatabase()
dbTask.InsertData( 1024, "Alice", 23, "woman", "shenzhen", "12312312312")
dbTask.QueryData( 20, 30)
dbTask.UpdateData( 40, "man", 1024)
dbTask.QueryData(20,30)
}
func (d *DBTask) CreateDatabase() {
_, err := d.Dbconnection.Exec("CREATE DATABASE IF NOT EXISTS my_db;")
if err != nil {
fmt.Println("failed to create databases",err.Error())
return
}
_, err = d.Dbconnection.Exec("USE my_db;")
if err != nil {
fmt.Println("select database failed")
return
}
_, err = d.Dbconnection.Exec("CREATE TABLE IF NOT EXISTS tb_user(id int(10) primary key,name varchar(20),age int(10),sex varchar(5),addr varchar(64),tel varchar(11));")
if err != nil {
fmt.Println("create table failed:", err.Error())
return
}
}
func (d *DBTask) InsertData( id int, name string, age int, sex string, addr string, tel string) {
stmt, _ := d.Dbconnection.Prepare(`INSERT INTO tb_user(id,name,age,sex,addr,tel) values(?,?,?,?,?,?)`)
defer stmt.Close()
ret, err := stmt.Exec(id, name, age, sex, addr, tel)
if err != nil {
fmt.Println("insert data error", err.Error())
return
}
if LastInsertId, err := ret.LastInsertId(); nil == err {
fmt.Println("LastInsertId", LastInsertId)
}
if RowAffect, err := ret.RowsAffected(); nil == err {
fmt.Println("RowAffect", RowAffect)
}
}
func (d *DBTask) QueryData(lower int, higher int) {
stmt, _ := d.Dbconnection.Prepare(`SELECT * FROM tb_user where age>= ? AND age < ?`)
defer stmt.Close()
d.UserInfomation = UserTableStruct{}
rows, err := stmt.Query(lower, higher)
defer rows.Close()
if err != nil {
fmt.Println("insert data error", err.Error())
return
}
for rows.Next() {
rows.Scan(&d.UserInfomation.Id, &d.UserInfomation.Name, &d.UserInfomation.Age,&d.UserInfomation.Sex, &d.UserInfomation.Addr, &d.UserInfomation.Tel)
if !d.UserInfomation.Addr.Valid {
d.UserInfomation.Name.String = ""
}
if !d.UserInfomation.Age.Valid {
d.UserInfomation.Age.Int64 = 0
}
fmt.Println("get data ,id:", d.UserInfomation.Id, "name:", d.UserInfomation.Name.String, "age:", "sex:",d.UserInfomation.Sex,d.UserInfomation.Age, "addr:", d.UserInfomation.Addr.String, "tel:", d.UserInfomation.Tel.String)
}
}
func (d *DBTask) UpdateData( age int, sex string, id int) {
stmt, err := d.Dbconnection.Prepare(`UPDATE tb_user SET age=?,sex=? WHERE id=?`)
defer stmt.Close()
result, err := stmt.Exec( age, sex, id)
if err != nil {
fmt.Println("update error", err.Error())
return
}
num, err := result.RowsAffected()
fmt.Println(num)
}