go get -u github.com/go-sql-driver/mysql
database/sql包提供了保证SQL或类SQL数据库的泛用接口, 并不提供具体的数据库驱动, 所以使用database/sql包时必须注入(至少)一个数据库驱动
import (
"database/sql"
_ "github.com/go-sql-driver/mysql" //自动执行init方法 注入数据库驱动
"log"
)
func main() {
//数据库信息
dsn := "root:root@tcp(127.0.0.1:3306)/test"
//连接数据库
DB, err := sql.Open("mysql", dsn) //不会校验 用户名,密码是否正确
if err != nil {
log.Fatal(err)
return
}
//Ping检查与数据库的连接是否仍有效,如果需要会创建连接
err = DB.Ping() //校验 dsn 是否正确
if err != nil {
log.Fatal(err)
return
}
log.Fatal("测试连接成功")
}
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" //自动执行init方法 注入数据库驱动
"log"
)
var db *sql.DB //是一个连接池对象 全局变量
func initDB() (err error) {
//数据库信息
dsn := "root:root@tcp(127.0.0.1:3306)/test"
//连接数据库
db, err = sql.Open("mysql", dsn) //不会校验 用户名,密码是否正确 这里 db 不能自动初始化(加:)
if err != nil {
log.Fatal(err)
return
}
//Ping检查与数据库的连接是否仍有效,如果需要会创建连接
err = db.Ping() //校验 dsn 是否正确
if err != nil {
log.Fatal(err)
return
}
return
}
type user struct {
id int
name string
age int
}
func main() {
err := initDB()
if err != nil {
log.Fatal(err)
}
fmt.Println("测试数据库连接成功!")
var u1 user
//1.写查询单条记录的sql语句
sql := `select id,name,age from user where id=2;`
// 2.执行
row := db.QueryRow(sql) //从 连接池 拿一个连接出来去数据库查询单条记录
// 3.拿到结果
row.Scan(&u1.id, &u1.name, &u1.age) //扫描将匹配行的列复制到dest所指向的值中
fmt.Printf("%+v", u1)
}
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" //自动执行init方法 注入数据库驱动
"log"
)
type user struct {
id int
name string
age int
}
var db *sql.DB //是一个连接池对象 全局变量
func initDB() (err error) {
//数据库信息
dsn := "root:root@tcp(127.0.0.1:3306)/test"
//连接数据库
db, err = sql.Open("mysql", dsn) //不会校验 用户名,密码是否正确 这里 db 不能自动初始化(加:)
if err != nil {
log.Fatal(err)
return
}
//Ping检查与数据库的连接是否仍有效,如果需要会创建连接
err = db.Ping() //校验 dsn 是否正确
if err != nil {
log.Fatal(err)
return
}
return
}
func queryrow(id int) {
var u1 user
//1.写查询单条记录的sql语句
sql := `select id,name,age from user where id=?;`
// 2.执行
row := db.QueryRow(sql, id) //从 连接池 拿一个连接出来去数据库查询单条记录
// 3.拿到结果 必须对row对象调用scan方法,因为该方法会释放数据库链接
row.Scan(&u1.id, &u1.name, &u1.age) //扫描将匹配行的列复制到dest所指向的值中
fmt.Printf("%+v", u1)
}
func main() {
err := initDB()
if err != nil {
log.Fatal(err)
}
fmt.Println("测试数据库连接成功!")
queryrow(1)
}
查询id>2的数据
//查询多条语句
func queryMore(n int) {
//1.sql语句
sql := `select id,name,age from user where id >?;`
//2.执行
rows, err := db.Query(sql, n)
if err != nil {
log.Fatal(err)
return
}
//3.一定要关闭rows rows.scan里 没有 自带关闭
defer rows.Close()
//4.循环取值
for rows.Next() { //有下一个结果就返回 true
var u1 user
rows.Scan(&u1.id, &u1.name, &u1.age)
fmt.Printf("%v\n", u1)
}
}
func main() {
err := initDB()
if err != nil {
log.Fatal(err)
}
fmt.Println("测试数据库连接成功!")
//queryrow(2)
queryMore(2)
}
插入 更新 删除 都用此操作
// 插入数据
func insertData() {
//1.写sql语句
sql := `insert into user(name,age) values('ocean',33)`
//2.exec
result, err := db.Exec(sql)
if err != nil {
log.Fatal(err)
return
}
//3.如果是 插入数据 的操作,能够拿到插入数据的 id
id, err := result.LastInsertId()
if err != nil {
log.Fatal(err)
return
}
fmt.Println(id)
}
func main() {
err := initDB()
if err != nil {
log.Fatal(err)
}
fmt.Println("测试数据库连接成功!")
//queryrow(2)
//queryMore(2)
insertData()
}
//更新数据
func updateData(newAge, id int) {
sql := `update user set age=? where id =?`
result, err := db.Exec(sql, newAge, id)
if err != nil {
log.Fatal(err)
return
}
//RowsAffected返回受更新、插入或删除影响的行数。
affected, err := result.RowsAffected()
if err != nil {
log.Fatal(err)
return
}
fmt.Printf("有%d行受影响了", affected)
}
func main() {
err := initDB()
if err != nil {
log.Fatal(err)
}
fmt.Println("测试数据库连接成功!")
//queryrow(2)
//queryMore(2)
//insertData()
updateData(9999, 1)
}
//删除
func deleteData(id int) {
sql := `delete from user where id=?`
exec, err := db.Exec(sql, id)
if err != nil {
log.Fatal(err)
}
affected, err := exec.RowsAffected()
if err != nil {
log.Fatal(err)
}
fmt.Printf("有%d行受影响", affected)
}
1.客户端对sql语句进行占位符替换得到完整的sql语句。
2.客户端发送完整sql语句到mysql服务端。
3.mysql服务端执行完整的sql语句并将结果返回给客户端。
1.将sql语句分为两部分,命令与数据。
2.先把命令部分发送给MySQL服务端,MySQL服务端进行sql预处理。
3.然后把数据部分发送给MySQL服务端,MySQL服务端对sql语句进行占位符替换。
4.mysql服务端执行完整的sql语句并将结果返回给客户端。
1.避免sql注入问题。
2.提升服务器性能,节省后续编译的成本。 优化重复执行的SQL语句
prepare
方法会先将sql语句发送给MySQL服务端,返回一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令。
//预处理插入
func prepareInsert() {
sql := `insert into user(name,age) values(?,?)` //命令一致,只用改变数字
prepare, err := db.Prepare(sql) //传递预处理命令
if err != nil {
log.Fatal(err)
return
}
//需关闭
defer prepare.Close()
//后续需要拿prepare去执行操作
var m = map[string]int{
"PG": 20,
"AFJN": 22,
"HX": 23,
}
for key, value := range m {
exec, err := prepare.Exec(key, value)
if err != nil {
log.Fatal(err)
}
affected, err := exec.RowsAffected()
if err != nil {
log.Fatal(err)
}
fmt.Printf("有%d行受影响", affected)
}
}
func transaction() {
//1.开启事务
begin, err := db.Begin()
if err != nil {
log.Fatal("开启失败", err)
}
//2.执行多个sql语句
sql1 := `update user set age=age-5 where id =2`
sql2 := `update user set age=age+5 where id =7`
//3.执行sql1
_, err = begin.Exec(sql1)
if err != nil {
//出错 回滚
begin.Rollback()
fmt.Println("执行语句1失败出错回滚!")
return
}
//4.执行语句2
_, err = begin.Exec(sql2)
if err != nil {
//出错回滚
begin.Rollback()
fmt.Println("执行语句2失败出错回滚!")
return
}
//5.上面两步都执行成功,就提交本次事务
err = begin.Commit()
if err != nil {
//提交出错,回滚
begin.Rollback()
fmt.Println("事务提交出错了")
return
}
fmt.Println("事务执行成功了")
}
简化操作 提高开发效率
使用sqlx时 结构体定义变量 首字母必须大写
查询单条语句
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql" //自动执行init方法 注入数据库驱动
"github.com/jmoiron/sqlx"
"log"
)
type user struct {
Id int
Name string
Age int
}
//改变地方
var db *sqlx.DB //是一个连接池对象 全局变量
func initDB() (err error) {
//数据库信息
dsn := "root:root@tcp(127.0.0.1:3306)/test"
//连接数据库 改变地方
//连接到数据库并使用ping进行验证
db, err = sqlx.Connect("mysql", dsn) //不会校验 用户名,密码是否正确 这里 db 不能自动初始化(加:)
if err != nil {
log.Fatal(err)
return
}
return
}
func main() {
err := initDB()
if err != nil {
log.Fatal(err)
}
fmt.Println("测试数据库连接成功!")
sql := `select id,name,age from user where id =8`
var u user
err = db.Get(&u, sql)
if err != nil {
log.Fatal(err)
}
fmt.Println(u)
}
查询多条语句
var users []user
sql2 := `select * from user`
db.Select(&users, sql2)
if err != nil {
log.Fatal(err)
return
}
fmt.Printf("%+v", users)