文章目录
前言
无论做什么都需要进行数据的增删改查,而进行增删改查的时候使用文件虽操作方便,但是数据格式化要求不强,使用数据库可以规范的操作数据
一、连接mysql常用到的方法?
Open(“数据库类型”,“用户名:密码@/数据库”)
功能:操作成功返回一个DB对象,DB对象用于多线程是安全的,db对象内部封装了连池 底层实现:open函数并没有创建连接,他只是验证参数是否合法,然后开启一个单独 的线程去监听是否需要建立一个新连接,当有请求建立新连接时,就创建新连接
Exec()
执行不返回行的查询,比如插入修改删除 DB交给内部的exec方法负责查询,exec首先会调用db内部的conn方法,从连接池中获得一个连接 然后检查内部的driver.Conn实现了Execerl实现了接口没有,如果实现了接口就会调用,Execer接 口的Exec方法执行查询,否则调用Conn接口的Prepare方法进行查询。
Query()
功能:用于检索,比如select 实现:db先调用内部的query方法负责查询,query首先调用db内部的conn方法从连接池内获得 一个新连接,然后调用queryConn方法负责查询
QueryRow()
功能:用于返回单行的查询 实现:接收到参数转交给db.Query()
Prepare()
功能:返回一个stmt,使用stmt对象可以进行上面的三个操作 实现:db交给内部的preapre方法负责查询,preapre首先从连接池中获取一个连接 然后调用driverConn的prepareLocked方法负责查询。 db.Begin()开启事务返回Tx对象,调用该方法之后,tx与该指定的来连接就绑定到一起了, 一旦事物进行提交或者回滚该事务,绑定的连接就进行解绑 tx.Exec() tx.Query() tx.QueryRow() tx.Prepare() tx.Commit() tx.Rollback()
二、操作mysql
1.引入包
代码如下(示例):
_ "database/sql" "fmt" //这里如果包没有办法自动下载就去github上进行下载然后放在src目录下,前面导包的时候有介绍 _ "github.com/go-sql-driver/mysql"
2.建立连接
代码如下(示例):
// 对错误的封装 func printErr(e error) { if e != nil { fmt.Println(e) } } //open传进去的是数据库配置信息,登录用户密码还有主机地址端口号,map是连接的数据库名,以及编码方式 db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/map?charset=utf8") printErr(err)
3.插入数据
代码如下(示例):
package main import ( "database/sql" _ "database/sql" "fmt" "strconv" _ "github.com/go-sql-driver/mysql" // _"mysql" ) func printErr(err error) { if err != nil { fmt.Println("出错了:", err) } } func main() { // 进行数据库初始化 db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/map?charset=utf8") printErr(err) //这个是延时关闭 defer db.Close() // 使用该方法进行单个信息插入还好,不适用于多次插入(因为每次插入建立了一个新连接效率慢) db.Exec("insert into test(name,age) values(?,?)", "荷花66", 66) db.Exec("insert into test(name,age) values(?,?)", "荷花池", 12) // 以下进行事务的提交,期间只进行一次连接 // 开启一段事物 tx, err := db.Begin() printErr(err) for i := 0; i < 30; i++ { tx.Exec("insert into test(name,age) values(?,?)", "name"+strconv.Itoa(i), i) } // 进行事物的提交 tx.Commit() fmt.Println("213") }
4.修改数据
代码如下(示例):
package main import ( _ "database/sql" "fmt" "strconv" _ "github.com/go-sql-driver/mysql" ) func printErr(err error) { if err != nil { fmt.Println(err) } } func main() { db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1)/map?charset=utf8") printErr(err) defer db.Close() db.Exec("update test set age=? where name=?", strconv.Itoa(199), "荷花池") db.Exec("update test set name=? where name=?", strconv.Itoa(199), "荷花66") fmt.Println("213") }
5.删除数据
代码如下(示例):
package main import ( "database/sql" "fmt" "strconv" _ "github.com/go-sql-driver/mysql" // _"mysql" ) func printErr(err error) { if err != nil { fmt.Println(err) } } func main() { db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1)/map?charset=utf8") printErr(err) defer db.Close() tx, _ := db.Begin() for i := 0; i < 30; i++ { _, err := tx.Exec("delete from test where age=?", strconv.Itoa(i)) printErr(err) } tx.Commit() //使用Exec进行删除,会将所有满足的数据均删除掉 db.Exec("delete from test where age=?", strconv.Itoa(199)) fmt.Println("213") }
6.查询数据
代码如下(示例):
// 测试是否连接成功 row, err := db.Query("select * from schoolmp where place=1000") // fmt.Println() printErr(err) i := 0 //迭代数据库每一条数据(这个类似于一个迭代器) for row.Next() { var temp map_node //读取数据 err = row.Scan(&temp.place) //自己封装的错误打印函数 printErr(err) fmt.Println(temp) i++ } // 可以用于判断查询的数据一共有多少条 if i == 0 { fmt.Println("没有数据!") } else { fmt.Println("数据一共有", i, "条") }
三、使用sqlx进行效率的提高,以及方式sql注入攻击
①使用sqlx简化操作
sqlx最明显的操作是,可以对结构体对象直接进行操作,将数据库内的数据 直接读到结构体对象内
package main import ( "fmt" _ "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" ) // 存储查询到的信息 type PName struct { Name1 string Name2 string Worth int } var db *sqlx.DB func initDB() (err error) { dsn := "root:123456@tcp(127.0.0.1:3306)/map?charset=utf8mb4&parseTime=True" // 也可以使用MustConnect连接不成功就panic db, err = sqlx.Connect("mysql", dsn) if err != nil { fmt.Printf("connect DB failed, err:%v\n", err) return } // 最大打开的连接数 db.SetMaxOpenConns(20) // 设置闲置的连接数 db.SetMaxIdleConns(10) return } // 查询单条数据 func selectOne() { var p PName sql := "select name1,name2,worth from schoolmp_atb where name1 = ?;" err := db.Get(&p, sql, "大门") if err != nil { fmt.Println(err) } fmt.Println(p) } // 查询所有数据 func selectMany() { var p []PName sql := "select name1,name2,worth from schoolmp_atb;" _ = db.Select(&p, sql) for index, temp := range p { fmt.Println(index, temp) } } // 实现批量插入 // BatchInsertUsers2 使用sqlx.In帮我们拼接语句和参数, 注意传入的参数是[]interface{} func BatchInsertUsers2(users []interface{}) error { query, args, _ := sqlx.In( "INSERT INTO user (name, age) VALUES (?), (?), (?)", users..., // 如果arg实现了 driver.Valuer, sqlx.In 会通过调用 Value()来展开它 ) fmt.Println(query) // 查看生成的querystring fmt.Println(args) // 查看生成的args _, err := db.Exec(query, args...) return err } func main() { err := initDB() if err != nil { fmt.Println("建立连接失败!", err) return } fmt.Println("连接成功!") // selectOne() selectMany() }
②sql注入简介
(1)sql注入产生根本原因
执行sql命令的时候,可以对sql命令进行拼接,导致所有信息泄露,所以应该操作的时候不可以 直接对数据库进行操作,应该先对存储数据的集合或者链表进行操作
(2)模拟sql注入
package main import ( "fmt" _ "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" _ "github.com/jmoiron/sqlx" ) var db *sqlx.DB /*sql注入就是利用sql语言拼接的bug进行数据的批量获取*/ func initDB() (err error) { dsn := "root:123456@tcp(127.0.0.1:3306)/map?charset=utf8mb4&parseTime=True" // 也可以使用MustConnect连接不成功就panic db, err = sqlx.Connect("mysql", dsn) if err != nil { fmt.Printf("connect DB failed, err:%v\n", err) return } // 最大打开的连接数 db.SetMaxOpenConns(20) // 设置闲置的连接数 db.SetMaxIdleConns(10) return } type PName struct { Name1 string Name2 string Worth int } func selectData(name string) { var p []PName sql := "select name1,name2,worth from schoolmp_atb where name1 = %s ;" sqlStr := fmt.Sprintf(sql, name) err := db.Select(&p, sqlStr) if err != nil { fmt.Println(err) return } for index, temp := range p { fmt.Println(index, temp) } return } func main() { _ = initDB() // #在select中起到的是注释的作用 // 将所有数据查询出来 selectData("'xxx' or 1=1#") // 将大致数据统计出来(如果数据大于10个则将数据统计出来) selectData("'xxx' or (select count(*) from schoolmp_atb)>100#") // selectData("'大门'") fmt.Println(123) }
上述案例虽然是执行的xxx操作语句,但是打印结果是 0 {大门 操场 90} 1 {大门 图书馆 60} 2 {大门 6号宿舍 100} 3 {图书馆 操场 20} 4 {图书馆 6号宿舍 50} 5 {操场 小礼堂 65} 6 {图书馆 小礼堂 40} 7 {小礼堂 下沉广场 55} 8 {6号宿舍 下沉广场 80} 9 {下沉广场 学宛餐厅 50} 10 {下沉广场 专家公寓 100} 11 {学宛餐厅 专家公寓 130} 12 {专家公寓 教工3村 150} 13 {操场 教工3村 250} 14 {教工3村 荷花池 100} 15 {荷花池 9#教学楼 40} 16 {荷花池 12#教学楼 60} 17 {9#教学楼 12#教学楼 30} 18 {9#教学楼 学生广场 75} 19 {12#教学楼 学生广场 15} 20 {12#教学楼 15#教学楼 50} 21 {15#教学楼 学生广场 45} 22 {15#教学楼 网球场 160} 23 {学生广场 网球场 100} 24 {网球场 东南餐厅 120} 25 {北京 南京 2000} 26 {1 1 1} 27 {123 123 123} 28 {1 1 1} 123 //将所有的数据查询了出来 //也可以使用 "'xxx' or (select count(*) from schoolmp_atb)>100#" 语句进行数据量的范围估计(其中100是估算的数量范围)
总结
数据库操作较为简单,但是数据库安全尤为重要,虽然现在市面上许多公司完成了数据库一些简单的防控,但是我们仍然不可以掉以轻心。了解其原理是解决问题的关键。