go语言连接关系型数据库的几种方式

Golang 2022-08-13 846

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)
}

 

标签:Golang

文章评论

评论列表

已有0条评论