当前位置: 首页 > 科技观察

Golang如何优雅地连接MySQL数据库?

时间:2023-03-12 06:33:33 科技观察

Go原生支持连接数据库,所以在使用Golang开发时,需要与数据库交互时可以使用database/sql包。在Go中访问DB,需要使用sql.DB接口:可以创建语句(statements)和事务(transactions),执行查询,得到结果。使用DB时,除了database/sql包外,还需要引入具体要使用的DB驱动。官方没有提供实现,需要先下载第三方实现。单击此处查看各种实施版本。通常选择DB为MySQL,所以选择的驱动为:github.com/go-sql-driver/mysql,需要导入包:"database/sql"_"github.com/go-sql-driver/mysql"包名前"_"import下划线的作用(如:import_github/demo):当导入一个包时,该包下的文件中的所有init()函数都会被执行。但是,有时候我们并不需要将整个包导入Imported,只是想让它执行init()函数。这时候可以使用import_来引用包。上面的MySQL驱动介绍了MySQL包中的各个init()方法,不能通过包名调用包中的其他函数。导入时,驱动的初始化函数会调用sql.Register将自己注册到database/sql包的全局变量sql.drivers中,以便后面通过sql.Open访问。案例使用数据表初始化数据库连接。sql.Open()中数据库连接字符串的格式为:“username:password@tcp(IP:port)/database?charset=utf8”。DB的类型是:*sql.DB,有了DB就可以执行增删改查了。Go将数据库操作分为两类:Query和ExecQuery表示一个查询,它将从数据库中获取查询结果(一系列行,可能为空)。exec表示执行语句,它不会返回行。常见的数据库操作模式:QueryRow只返回一行查询,作为Query的常见特例。Prepare准备一条需要多次使用的语句,以便后续执行。查询操作varuserUserrows,e:=DB.Query("select*fromuserwhereidin(1,2,3)")ife==nil{errors.New("queryincurerror")}forrows.Next(){e:=rows.Scan(user.sex,user.phone,user.name,user.id,user.age)ife!=nil{fmt.Println(json.Marshal(user))}}rows.Close()//单行查询操作DB.QueryRow("select*fromuserwhereid=1").Scan(user.age,user.id,user.name,user.phone,user.sex)执行过程使用db.Query()向数据库发送查询并获取结果SetRows,并检查错误。使用rows.Next()作为循环条件,迭代读取结果集。使用rows.Scan从结果集中获取一行。在退出迭代后使用rows.Err()检查错误。使用rows.Close()关闭结果集并释放连接。增删改查和Exec通常不限制你使用Query进行查询,只是Query会返回结果集,而Exec不会。所以如果是进行增删改查,一般用Exec比较好。Exec返回的结果是Result,Result接口允许获取执行结果的元数据:typeResultinterface{//用于返回自增ID,不是所有的关系型数据库都有这个功能。LastInsertId()(int64,error)//返回受影响的行数。RowsAffected()(int64,error)}Preparequery如果现在要使用placeholder函数,where条件要作为参数传入,Go提供了db.Prepare语句帮你绑定。准备好的查询的结果是准备好的语句,其中可以包含执行时所需参数的占位符(即绑定值)。准备好的查询比string字符串好很多,可以转义参数,避免SQL注入。同时,准备查询也为一些数据库节省了解析和生成执行计划的开销,有利于性能。占位符PostgreSQL使用$N作为占位符。N是一个从1开始递增的整数,代表参数的位置,方便参数的重复使用。MySQL使用?作为占位符,SQLite可以使用这两个占位符,而Oracle使用:param1。MySQLPostgreSQLOracle======================WHEREcol=?WHEREcol=$1WHEREcol=:colVALUES(?,?,?)VALUES($1,$2,$3)VALUES(:val1,:val2,:val3)stmt,e:=DB.Prepare("select*fromuserwhereid=?")query,e:=stmt.Query(1)query.Scan()事务通过db.Begin()要开始一个事务,Begin方法将返回一个事务对象Tx。对结果变量Tx调用Commit()或Rollback()方法提交或回滚更改并关闭事务。在底层,Tx会从连接池中获取一个连接,并在交易过程中保持独占。事务对象Tx上的方法对应数据库对象sql.DB的方法,如Query、Exec等。事务对象也可以准备查询,事务创建的准备语句显式绑定到创建它的事务.//开启交易tx,err:=DB.Begin()iferr!=nil{fmt.Println("txfail")}//准备sql语句stmt,err:=tx.Prepare("DELETEFROMuserWHEREid=?")iferr!=nil{fmt.Println("Preparefail")returnfalse}//设置参数并执行sql语句res,err:=stmt.Exec(user.id)iferr!=nil{fmt.Println("Execfail")returnfalse}//Committransactiontx.Commit()下面来做一个完整的sql操作:packagemainimport("database/sql""encoding/json""fmt"_"github.com/go-sql-driver/mysql""github.com/pkg/errors""strings")//数据库配置const(userName="root"password="123456"ip="127.0.0.1"port="3306"dbName="test")//db数据库连接池varDB*sql.DBtypeUserstruct{idint64namestringageint8sexint8phonestring}//注意方法名的大小写,即publicfuncInitDB(){//构造连接:"用户名:password@tcp(IP:port)/database?charset=utf8"path:=strings.Join([]string{userName,":",password,"@tcp(",ip,":",port,")/",dbName,"?charset=utf8"},"")//打开数据库,前者是驱动名,所以导入:_"github.com/go-sql-driver/mysql"DB,_=sql.Open("mysql",path)//设置最大个数数据库连接DB。SetConnMaxLifetime(100)//设置数据库最大空闲连接数DB.SetMaxIdleConns(10)//验证连接iferr:=DB.Ping();err!=nil{fmt.Println("opendatabasefail")return}fmt.Println("connectsuccess")}//查询操作funcQuery(){varuserUserrows,e:=DB.Query("select*fromuserwhereidin(1,2,3)")ife==nil{errors.New("queryincurerror")}forrows.Next(){e:=rows.Scan(user.sex,user.phone,user.name,user.id,user.age)ife!=nil{fmt.Println(json.Marshal(user))}}rows.Close()DB.QueryRow("select*fromuserwhereid=1").Scan(user.age,user.id,user.name,user.phone,user.sex)stmt,e:=DB.Prepare("select*fromuserwhereid=?")query,e:=stmt.Query(1)query.Scan()}funcDeleteUser(userUser)bool{//开启交易tx,err:=DB.Begin()iferr!=nil{fmt.Println("txfail")}//准备sql语句stmt,err:=tx.Prepare("DELETEFROMuserWHEREid=?")iferr!=nil{fmt.Println("Prepafail")returnfalse}//设置参数并执行sql语句res,err:=stmt.Exec(user.id)iferr!=nil{fmt.Println("Execfail")returnfalse}//提交事务tx.Commit()//获取最后一次插入的idfmt.Println(res.LastInsertId())返回true}funcInsertUser(userUser)bool{//开启交易tx,err:=DB.Begin()iferr!=nil{fmt.Println("txfail")returnfalse}//准备sql语句stmt,err:=tx.Prepare("INSERTIINTOuser(`name`,`phone`)VALUES(?,?)")iferr!=nil{fmt.Println("Prepafail")returnfalse}//将参数传入sql语句执行res,err:=stmt.Exec(user.name,user.phone)iferr!=nil{fmt.Println("Execfail")returnfalse}//提交事务tx.Commit()//获取idfmt.Println(res.LastInsertId())returntrue}funcmain(){InitDB()Query()deferDB.Close()}参考https://www.cnblogs.com/rickiyang/p/11074180.html