Go学习(二十五)操作MYSQL,实现CRUD
1。介绍
Go官方提供了database包,database包下有sqldriver。该包用来定义操作数据库的接口,这保证了无论使用哪种数据库,操作方式都是相同的。但Go官方并没有提供连接数据库的driver,如果要操作数据库,还需要第三方的driver包。2。下载安装驱动
gosqldriver驱动源码地址:https:github。comgosqldrivermysql2。1安装驱动gogetugithub。comgosqldrivermysql3。匿名导入
通常来说,导入包后就能调用该包中的数据和方法。但是对于数据库操作来说,不应该直接使用导入驱动包所提供的方法,而应该使用sql。DB对象所提供的统一的方法。因此在导入MySQL驱动时,使用了匿名导入包的方式。
匿名导入包:只导入包但是不使用包内的类型和数据,使用匿名的方式:在包路径前添加下画线import(github。comgosqldrivermysql)
在导入一个数据库驱动后,该驱动会自行初始化并注册到Go的databasesql上下文中,这样就可以通过databasesql包所提供的方法来访问数据库了。4。连接数据库4。1连接方法
使用sql包中的Open()函数来连接数据库。Open(driverName,dataSourceNamestring)(DB,error)driverName:使用的驱动名,如mysql。(注册到databasesql时所使用的名字)dataSourceName:数据库连接信息,格式:〔用户名:密码tcp(IP:port)数据库?charsetutf8〕,例如:root:123456tcp(127。0。0。1:3306)test?charsetutf84。2sql。DB作用sql。Open()返回的sql。DB对象是Goroutine并发安全的。sql。DB通过数据库驱动为开发者提供管理底层数据库连接的打开和关闭操作。sql。DB帮助开发者管理数据库连接池。正在使用的连接被标记为繁忙,用完后回到连接池等待下次使用。所以,如果开发者没有把连接释放回连接池,会导致过多连接使系统资源耗尽。4。3sql。DB设计目标
sql。DB的设计目标就是作为长连接(一次连接多次数据交互)使用,不宜频繁开关。比较好的做法是,为每个不同的datastore建一个DB对象,保持这些对象打开。如果需要短连接(一次连接一次数据交互),就把DB作为参数传入function,而不要在function中开关。5。写操作(增、删、改)5。1执行步骤先使用预编译语句(PreparedStatement)来拼接sql。然后调用db。Exec()执行SQL,返回执行结果5。2代码示例packagemainimport(databasesqlfmtgithub。comgosqldrivermysqltime)funcmain(){连接数据库open,err:sql。Open(mysql,root:roottcp(127。0。0。1:3306)test?charsetutf8)checkError(err)插入数据add(open)更新数据update(open)删除数据del(open)}插入数据funcadd(opensql。DB){插入数据prepare,err:open。Prepare(insertusersetusername?,password?,mobile?,createtime?)checkError(err)exec,err:prepare。Exec(李四,123456,17600000000,time。Now()。Unix())checkError(err)id,err:exec。LastInsertId()checkError(err)fmt。Printf(插入数据ID:d,id)}更新funcupdate(opensql。DB){prepare,err:open。Prepare(updateusersetusername?whereid?)checkError(err)exec,err:prepare。Exec(王五,18)checkError(err)rows,err:exec。RowsAffected()checkError(err)fmt。Printf(更新数据成功,影响条数d,rows)}删除数据funcdel(opensql。DB){prepare,err:open。Prepare(deletefromuserwhereid?)checkError(err)exec,err:prepare。Exec(8)checkError(err)rows,err:exec。RowsAffected()checkError(err)fmt。Printf(删除数据成功,影响条数d,rows)}检测错误funccheckError(errerror){iferr!nil{panic(操作失败:err。Error())}}6。读操作(查询)6。1执行步骤1。查询多条步骤调用db。Query()方法执行SQL语句,返回一个Rows查询结果。将rows。Next()方法的返回值作为for循环的条件,迭代查询数据。在循环中,通过rows。Scan()方法读取每一行数据。调用db。Close()关闭查询。2。查询单条步骤调用db。QueryRow()方法执行SQL语句,返回一个Row查询结果。然后调用row。Scan()读取数据。6。2代码示例packagemainimport(databasesqlfmtgithub。comgosqldrivermysql)funcmain(){连接数据库db,err:sql。Open(mysql,root:roottcp(127。0。0。1:3306)nsbdapp?charsetutf8)checkError(err)查询多条数据rows:queryRows(db)fmt。Printf(多条返回:v,rows)查询单条数据row:queryRow(db)fmt。Printf(单条返回:v,row)}创建表的映射对象typeUserstruct{UidintUserNamestringCreateTimeintBirthdaysql。NullString有的值可能为NULL}查询多条数据funcqueryRows(dbsql。DB)〔〕User{stmt,err:db。Prepare(selectid,username,createtime,birthdayfromnsbduserwhereid?)checkError(err)rows,err:stmt。Query(30)延迟关闭deferrows。Close()checkError(err)user:new(User)users:make(〔〕User,5)varusers〔〕Userforrows。Next(){rows。Scan()方法的参数顺序很重要,必须和查询结果的column相对应(数量和顺序都需要一致)err:rows。Scan(user。Uid,user。UserName,user。CreateTime,user。Birthday)checkError(err)usersappend(users,user)}returnusers}查询单条数据funcqueryRow(dbsql。DB)User{stmt,err:db。Prepare(selectid,username,createtime,birthdayfromnsbduserwhereid?)checkError(err)user:new(User)errstmt。QueryRow(4)。Scan(user。Uid,user。UserName,user。CreateTime,user。Birthday)checkError(err)returnuser}检测错误funccheckError(errerror){iferr!nil{panic(操作失败:err。Error())}}
输出:多条返回:〔{Uid:1UserName:adminCreateTime:0Birthday:{String:20170415Valid:true}}{Uid:2UserName:u2CreateTime:1605858072Birthday:{String:19930214Valid:true}}{Uid:3UserName:u3CreateTime:1606289644Birthday:{String:19910531Valid:true}}{Uid:4UserName:u4CreateTime:1610521164Birthday:{String:19891124Valid:true}}{Uid:5UserName:u5CreateTime:1610588359Birthday:{String:Valid:false}}〕单条返回:{Uid:4UserName:u4CreateTime:1610521164Birthday:{String:19891124Valid:true}}6。3注意事项rows。Scan()方法的参数顺序很重要,必须和查询结果的column相对应(数量和顺序都需要一致);Go是强类型语言,在查询数据时先定义数据类型,针对字段值为NULL时,数据类型应定义为:sql。NullString、sql。NullInt64等,并可以通过Valid值来判断查询到的值是赋值状态还是未赋值状态。每次db。Query()操作后,都建议调用rows。Close()。rows。Close()操作是幂等操作,即便对已关闭的rows再执行close()也没关系。6。4为什么查询后要关闭连接?
因为db。Query()会从数据库连接池中获取一个连接,这个底层连接在结果集(rows)未关闭前会被标记为处于繁忙状态。当遍历读到最后一条记录时,会发生一个内部EOF错误,自动调用rows。Close()。但如果出现异常,提前退出循环,rows不会关闭,连接不会回到连接池中,连接也不会关闭,则此连接会一直被占用。因此通常使用deferrows。Close()来确保数据库连接可以正确放回到连接池中。
微信搜索【猿码记】获取最新文章信息。