go使用mysql

go的mysql驱动相对比较统一,基本都用go-sql-driver。这个驱动也实现了数据库连接池。但是没有实现orm。 目前go的mysql orm实现则比较多 gormxormbeego-orm。orm使用起来比较灵活,但是在大型项目中,由于orm屏蔽底层细节的原因通常不建议使用。本文主要介绍go-sql-driver不介绍rom。

创建一个测试table

CREATE TABLE `tb_test` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) NOT NULL,
  `img` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

连接mysql

导入 github.com/go-sql-driver/mysql

    "database/sql"
	_ "github.com/go-sql-driver/mysql"

注意这边使用 _用来执行这个包的init()函数,来注册mysql驱动,sql.Register("mysql", &MySQLDriver{})

连接mysql,注意连接池的主要三个配置ConnMaxLifetimeMaxOpenConnsMaxIdleConns

var db *sql.DB
func init()  {
	var err error
	db, err = sql.Open("mysql", "user:password@tcp(localhost:3306)/test") 
	if err != nil {
		panic(err.Error())
	}
	db.SetConnMaxLifetime(10*time.Minute)
	db.SetMaxIdleConns(2)
	db.SetMaxOpenConns(10)
}

注意由于连接池的存在,一般只有确认不再使用msyql了才会调用db.Close()

curd操作

添加

func add()  {
	stmtIns, err := db.Prepare("INSERT INTO tb_test VALUES(?,?,?)") // ? = placeholder
	if err != nil {
		panic(err.Error())
	}
	defer stmtIns.Close()
	for i := 0; i < 25; i++ {
		_, err = stmtIns.Exec(i+1, i * i,fmt.Sprintf("http://abc.com/%d.jpg",i))
		if err != nil {
			panic(err.Error())
		}
	}
}

获取

func query()  {
	//findone
	stmtOut, err := db.Prepare("SELECT uid FROM tb_test WHERE id = ?")
	if err != nil {
		panic(err.Error())
	}
	defer stmtOut.Close()
	var squareNum int

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)  //查询超时时间
	defer cancel()
	err = stmtOut.QueryRowContext(ctx,13).Scan(&squareNum)
	if err != nil {
		panic(err.Error())
	}
	fmt.Printf("The square number of 13 is: %d \n", squareNum)
	err = stmtOut.QueryRow(5).Scan(&squareNum)  //不带超时
	if err != nil {
		panic(err.Error())
	}
	fmt.Printf("The square number of 5 is: %d \n", squareNum)

	//findmany
	stmtOut, err = db.Prepare("SELECT * FROM tb_test")
	if err != nil {
		panic(err.Error())
	}
	defer stmtOut.Close()

	type Entry struct{
		Id int32
		Uid int
		Img string}
	var entrys []Entry
	ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)  //查询超时时间,对耗时的查询使用超时处理对程序的健壮性有很大帮助
	defer cancel()
	rows ,err := stmtOut.QueryContext(ctx)
	if err!=nil {
		println(err)
	}
	defer rows.Close()
	for rows.Next() {
		entry := Entry{}
		rows.Scan(&entry.Id,&entry.Uid,&entry.Img) //这边需要和数据库的字段顺序保持一致,另外一种方法是select中指定字段,这边scan的顺序和指定的字段顺序一致
		entrys = append(entrys,entry)
	}
	fmt.Println(entrys)
}

更新

func update()  {
	stm,_ := db.Prepare("update tb_test set uid=? where id=? ")
	ret ,err := stm.Exec(999,1)
	if err !=nil {
		panic(err)
	}

	fmt.Println(ret.RowsAffected()) //影响条数
	stm.Close()
}

删除

func delete()  {
	stm,_ := db.Prepare("DELETE from tb_test  where id=? ")
	ret ,err := stm.Exec(25)
	if err !=nil {
		panic(err)
	}
	fmt.Println(ret.RowsAffected()) //影响条数
	stm.Close()
}

事务

func transaction()  {
	tx ,_:=db.Begin()
	stmt,err := tx.Prepare("SELECT uid FROM tb_test WHERE id = ?")
	if err !=nil {
		tx.Rollback()
		return
	}
	var uid  int32
	stmt.QueryRow(1).Scan(&uid)

	ret,err := tx.Exec("UPDATE tb_test set img='http://abc.com/3.jpg' where id=?",1)
	if err !=nil {
		tx.Rollback()
		return
	}
	fmt.Println(ret.RowsAffected())

	tx.Commit()
}

参考资料