准备工作

首先我们要准备一个写golang的IDE,我用的是goland,还可以用vscode。
然后我们要知道golang的基本使用。
其次我们还要知道连接MySQL的基本步骤,如果有jdbc的封装经历就更好啦。
那就开始吧!

导入驱动
go get github.com/go-sql-driver/mysql_ "github.com/go-sql-driver/mysql"driver.go
func init() {
	sql.Register("mysql", &MySQLDriver{})
}

我们导入mysql这个包的作用主要是为了执行这个init方法,注册mysql驱动。

连接mysql
func Connect() (*sql.DB, error) {
	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", username, password, url, dbName))
	if err != nil {
		return nil, err
	}
	return db, err
}
fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", username, password, url, dbName)root:123456@tcp(127.0.0.1:3306)/test?charset=utf8
增删改查

func Insert(sql string, params ...interface{}) {
	// 注意:这里必须是params... 不然会转成数组类型了
	res, err := DB.Exec(sql, params...)
	// 打印上一次插入自增的id
	if res != nil {
		fmt.Println(res.LastInsertId())
	} else {
		fmt.Printf("插入失败:%v\n", err)
	}
}

前面是sql语句,后面是要加的参数,调用方法是这样的:

var user = User{
	Username: "xxx",
	Password: "xxx",
}
var sql = "insert into user (username, password) values (?, ?)"
Insert(sql, user.Username, user.Password)

func Delete(sql string, params ...interface{}) {
	res, err := DB.Exec(sql, params...)
	if err != nil {
		fmt.Printf("删除失败:%v\n", err)
	} else {
		fmt.Println(res.RowsAffected())
	}
}

调用方法与增加类似

var sql = "delete from user where id = ?"
// 删除id为1的用户
Delete(sql, 1)

func Update(sql string, params ...interface{}) {
	res, err := DB.Exec(sql, params...)
	if err != nil {
		fmt.Printf("更新失败:%v\n", err)
	} else {
		fmt.Println(res.RowsAffected())
	}
}

更新的调用方法也与之前一样的

var sql = "update user set username = ? where id = ?"
// 更新id为1的用户的用户名为张三
Update(sql, "张三", 1)

查询这里我是用反射封装了一个比较通用的查询,最后会返回一个list表示结果集,结果的类型应该是一个结构体,结构体的具体类型由用户传入的参数决定。

func Query(sqlString string, i interface{}) (res *list.List) {
	rows, _ := DB.Query(sqlString)
	res = list.New()
	t := reflect.TypeOf(i)
	// 获取表的所有列
	cols, _ := rows.Columns()
	// 这里需要琢磨一下
	values := make([]sql.RawBytes, len(cols))
	scanArgs := make([]interface{}, len(values))
	for i := 0; i < len(values); i++ {
		scanArgs[i] = &values[i]
	}

	for rows.Next() {
		_ = rows.Scan(scanArgs...)
		obj := reflect.New(t).Elem()
		for i, v := range values {
			// FiledByName 是根据字段名获取该字段的值...
			value := obj.FieldByName(StringToCamel(cols[i]))
			if !value.IsValid() {
				fmt.Printf("字段名为 %s 的字段与结构体不匹配\n", cols[i])
				continue
			} else {
				switch value.Kind() {
				case reflect.Bool:
					// TODO 转换Bool类型
				case reflect.String:
					value.SetString(string(v))
				case reflect.Int:
					temp, _ := strconv.Atoi(string(v))
					value.SetInt(int64(temp))
				}
			}
		}
		res.PushBack(obj)
	}
	return
}

调用方法如下

type User struct {
	Username string
	Password string
}
var sql = "select * from user"
// 查询所有的用户,结果封装在res中
res := Query(sql, User{})
// 遍历res,打印结果
for i := res.Front(); i != nil; i = i.Next() {
		fmt.Println(i.Value)
}

要注意的是如何将数据从rows中读取出来,还有就是反射,是一个Type和一个Value,是对应的,Type是类型,Value是具体的值,比如User的Username字段,它的Type是string,而Value是Username的具体的值,这个需要琢磨一下。