go语言自身"database/sql"库实现了sql连接的接口,只要改变数据库驱动就可以连接多种数据库,下面以MySql数据库为例子,演示使用原生sql,gorm,sqlx关于数据库的CURD操作
# 准备工作
使用navcat建立一个数据库go_demo,执行下列建表语句
建表语句
CREATE TABLE `user_inos` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '姓名',
`gender` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '性别(男,女)',
`hobby` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '爱好',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
1.原生sql(database/sql+mysql-driver)
安装mysql-driver驱动
go get github.com/go-sql-driver/mysql
1.1 建立sql连接
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
_ "time"
)
var db *sql.DB
// 初始化数据库连接
func initSql() (err error) {
// 数据库连接来源,用于定义如何连接数据库,不同数据库的DSN格式是不同的
dsn := "root:password@tcp(127.0.0.1:3306)/go_demo"
db, err = sql.Open("mysql", dsn)
if err != nil {
panic(err)
}
// Ping方法才是真正实现与数据库建立连接的操作
err = db.Ping()
if err != nil {
fmt.Println("数据库连接失败...")
return err
}
fmt.Println("数据库连接成功...")
// 其他Mysql配置
// 默认情况下,同时打开的连接数 (使用中 + 空闲) 没有限制。
// 当值为0或者小于0时表示无限制(0为默认配置)
db.SetMaxOpenConns(5)
// 默认情况下 sql.DB 会在连接池中最多保留 2 个空闲连接
// 小于或等于0时,不保留任何空闲连接
db.SetMaxIdleConns(5)
// 设置了可重用连接的最大时间长度
// 设置为0,表示没有最大生存期,并且连接会被重用
//db.SetConnMaxLifetime(time.Hour)
return nil
}
func main() {
err := initSql()
if err != nil {
fmt.Println("数据库连接失败...")
return
}
// 关闭sql连接
// 可以不用返回err,此语句在返回err以前会自动关闭sql连接
defer func(db *sql.DB) {
err := db.Close()
if err != nil {
panic(err)
}
fmt.Println("数据库连接关闭...")
}(db)
}
1.2 添加数据
func save() {
sqlStr := "INSERT INTO user_inos VALUES(?, ?, ?, ?);"
result, err := db.Exec(sqlStr, nil, "ikun", "男", "篮球")
if err != nil {
fmt.Println("插入数据失败...")
return
}
fmt.Println(result.LastInsertId()) // 新插入数据的id, 2 <nil>
fmt.Println(result.RowsAffected()) // sql语句执行影响的行数
}
1.3 查询数据
// 查询一条数据
func queryOne() {
sqlStr := "SELECT * FROM user_inos WHERE id = ?;"
// 执行QueryRow必须调用Scan方法,否则会导致数据库链接无法释放(出现卡住的情况)
var user User
err := db.QueryRow(sqlStr, 2).Scan(&user.id, &user.gender, &user.name, &user.hobby)
if err != nil {
return
}
fmt.Println("row: ", user) // row: {2 男 ikun 篮球}
}
// 查询多条数据
func queryRows() {
sqlStr := "SELECT * FROM user_inos;"
rows, err := db.Query(sqlStr)
if err != nil {
return
}
// 非常重要,关闭rows的数据库连接
defer rows.Close()
// 循环读取rows中的数据
for rows.Next() {
var user User
err := rows.Scan(&user.id, &user.gender, &user.name, &user.hobby)
if err != nil {
return
}
fmt.Println(user)
}
}
1.4 更新数据
// 更新数据
func updateOneData() {
sqlStr := "UPDATE user_inos SET name = ? WHERE id = ?;"
result, err := db.Exec(sqlStr, "纯路人", 2)
if err != nil {
return
}
fmt.Println(result.RowsAffected())
}
1.5 删除数据
// 删除数据
func deleteOneData() {
sqlStr := "DELETE FROM user_inos WHERE id = ?;"
result, err := db.Exec(sqlStr, 4)
if err != nil {
return
}
fmt.Println(result.RowsAffected())
}
1.6 预处理
// 使用预处理查询数据
func prepareQueryOneData() {
sqlStr := "SELECT * FROM user_inos WHERE id = ?;"
stmt, err := db.Prepare(sqlStr)
if err != nil {
return
}
defer stmt.Close()
var user User
err = stmt.QueryRow(2).Scan(&user.id, &user.gender, &user.name, &user.hobby)
if err != nil {
return
}
fmt.Println(user)
}
2. Sqlx使用
sqlx是对 database/sql 的部分功能做了扩展,让其使用更为方便
2.1 建立连接
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
var db *sqlx.DB
// User 必须使用大写,否则sqlx无法解析
type User struct {
Id int `db:"id"`
Name string `db:"name"`
Gender string `db:"gender"`
Hobby string `db:"hobby"`
}
// 初始化数据库连接
func initSql() (err error) {
// 数据库连接来源,用于定义如何连接数据库,不同数据库的DSN格式是不同的
dsn := "root:hy1680456489@tcp(127.0.0.1:3306)/go_demo?charset=utf8mb4&parseTime=True"
db, err = sqlx.Connect("mysql", dsn)
if err != nil {
panic(err)
}
// Ping方法才是真正实现与数据库建立连接的操作
err = db.Ping()
if err != nil {
fmt.Println("数据库连接失败...")
return err
}
fmt.Println("数据库连接成功...")
// 其他Mysql配置
// 默认情况下,同时打开的连接数 (使用中 + 空闲) 没有限制。
// 当值为0或者小于0时表示无限制(0为默认配置)
db.SetMaxOpenConns(5)
// 默认情况下 sql.DB 会在连接池中最多保留 2 个空闲连接
// 小于或等于0时,不保留任何空闲连接
db.SetMaxIdleConns(5)
// 设置了可重用连接的最大时间长度
// 设置为0,表示没有最大生存期,并且连接会被重用
//db.SetConnMaxLifetime(time.Hour)
return nil
}
func main() {
err := initSql()
if err != nil {
fmt.Println("数据库连接失败...")
return
}
defer db.Close()
}
可以看到建立连接的方式与 golang自带的大差不差
2.2 查询
func queryOneData() {
sqlStr := "SELECT * FROM user_inos WHERE id = ?;"
var user User
err := db.Get(&user, sqlStr, 2)
if err != nil {
fmt.Printf("error: %v", err)
return
}
fmt.Println("row: ", user)
}
// 查询多条数据
func queryRows() {
sqlStr := "SELECT * FROM user_inos;"
var users []User
err := db.Select(&users, sqlStr)
if err != nil {
return
}
fmt.Println("rows: ", users)
}
sqlx 简化了查询的方式,让你可以更容易的使用查询
sqlx 插入,更新,删除数据的方式与原生的一样。除此外,sqlx还有NamedQuery,NamedExec方法可以让sql语句带有名称的占位符
2.3 批量插入数据
// InsertManyData 批量插入
func InsertManyData(users []User) error {
sqlStr := "INSERT INTO user_inos (id, name, gender, hobby) VALUES (null, :name, :gender, :hobby);"
_, err := db.NamedExec(sqlStr, users)
if err != nil {
fmt.Println("插入失败...")
return err
}
return nil
}
// 插入多条数据
users := []User{
{Name: "小黑子2号", Gender: "男", Hobby: "最爱篮球"},
{Name: "小黑子3号", Gender: "女", Hobby: "最爱露出鸡脚"},
}
err = InsertManyData(users)
if err != nil {
fmt.Printf("ERROR: %v", err)
return
}
fmt.Println("插入成功...")
2.4 In查询
// QueryByIds In查询
func QueryByIds(ids []int) (users []User, err error) {
// 动态填充id
query, args, err := sqlx.In("SELECT id, name, gender FROM user_inos WHERE id IN (?)", ids)
if err != nil {
return nil, err
}
// sqlx.In 返回带有 `?` bindvar的查询语句,使用Rebind重新绑定
query = db.Rebind(query)
fmt.Println("query: ", query)
err = db.Select(&users, query, args...)
return users, nil
}
func main () {
// In查询
users, err := QueryByIds([]int{1, 2, 3})
if err != nil {
return
}
fmt.Println("rows: ", users)
}
2.5 In查询,但保持数据顺序
// QuerySortByIds 查询id在更定id集合中的数据
func QuerySortByIds(ids []int) (users []User, err error) {
// 动态填充id
strIds := make([]string, 0, len(ids))
for _, id := range ids {
strIds = append(strIds, fmt.Sprintf("%d", id))
}
query, args, err := sqlx.In("SELECT id, name, gender FROM user_inos WHERE id IN (?) ORDER BY FIND_IN_SET(id, ?)", ids, strings.Join(strIds, ","))
if err != nil {
return nil, err
}
// sqlx.In 返回带有 `?` bindvar的查询语句,使用Rebind重新绑定
query = db.Rebind(query)
fmt.Println("query: ", query)
err = db.Select(&users, query, args...)
return users, nil
}
func main() {
err := initSql()
if err != nil {
fmt.Println("数据库连接失败...")
return
}
defer db.Close()
// In查询,但是保持id的顺序
users, err := QuerySortByIds([]int{3, 1, 2, 4})
if err != nil {
return
}
fmt.Println("rows: ", users)
}
评论列表
已有0条评论