用golang对数据库标准操做进行封装,为后面的rest server提供数据库访问层。实现的目标是:能根据rest请求参数自动生成数据库操做语句,提供增、删、改、查、批量写入、事务等必要的数据库操做封装。并能够方便的扩展到多种数据库,让全部的数据库操做对于rest server来讲表现为一致的访问接口。java
按功能模块对核心代码进行说明node
数据库标准操做接口定义,根据个人实践经验,如下的接口设计已经可以很好的支持大部分的数据库操做,这些操做包括了根据json参数自动完成的CURD、手写sql支持、批量插入(更新)心及事务操做。
type IBock interface{ //根据参数,自动完成数据库查询 Retrieve(params map[string]interface{}, args ...interface{}) map[string]interface{} //根据参数,自动完成数据库插入 Create(params map[string]interface{}, args ...interface{}) map[string]interface{} //根据参数,自动完成数据库更新(只支持单条) Update(params map[string]interface{}, args ...interface{}) map[string]interface{} //根据参数,自动完成数据库删除(只支持单条) Delete(params map[string]interface{}, args ...interface{}) map[string]interface{} //手写查询sql支持 QuerySql(sql string, values []interface{}, params map[string]interface{}) map[string]interface{} //手写非查询sql支持 ExecSql(sql string, values []interface{}) map[string]interface{} //批量插入或更新 InsertBatch(tablename string, els []interface{}) map[string]interface{} //事务支持 TransGo(objs map[string]interface{}) map[string]interface{} }
参数说明
接口的具体实现,本文是对mysql的实现,暂只实现了基本的CURD,项目中会逐步完善。
//咱们把操做对象定义在一个表上 type Bock struct { Table string } //parseArgs函数的功能是解析args参数中包括的可变参数,实如今下面 func (b *Bock) Retrieve(params map[string]interface{}, args ...interface{}) map[string]interface{} { //查询时咱们通常只关注查询哪些表字段 _, fields, _ := parseArgs(args) //调用具体的查询接口,查询接口将根据输入参数params自动实现sql查询语句,支持多样的查询定义,如:lks(从多个字体查询相同内容),ors(或查询),ins(in查询)等 return Query(b.Table, params, fields) } func (b *Bock) Create(params map[string]interface{}, args ...interface{}) map[string]interface{} { //新建接口,通常都会关注用户在session的ID _, _, session := parseArgs(args) uId := session["userid"].(string) params["u_id"] = uId //调用具体的插入接口 return Insert(b.Table, params) } func (b *Bock) Update(params map[string]interface{}, args ...interface{}) map[string]interface{} { //只支持单个更新,因此ID必须存在 id, _, _ := parseArgs(args) if len(id) == 0 { rs := make(map[string]interface{}) rs["code"] = 301 rs["err"] = "Id must be input." return rs } return Update(b.Table, params) } func (b *Bock) Delete(params map[string]interface{}, args ...interface{}) map[string]interface{} { //只支持单个删除,因此ID必须存在 id, _, _ := parseArgs(args) if len(id) == 0 { rs := make(map[string]interface{}) rs["code"] = 301 rs["err"] = "Id must be input." return rs } return Delete(b.Table, params) }
parseArgs函数的实现
func parseArgs(args []interface{}) (string, []string, map[string]interface{}) { //解析指定的参数 var id string //信息ID var fields []string //查询字段集 var session map[string]interface{} //用户session对象 for _, vs := range args { switch vs.(type) { case map[string]interface{}: //只接收指定类型 for k, v := range vs.(map[string]interface{}) { if k == "id" { id = v.(string) } if k == "fields" { fields = v.([]string) } if k == "session" { session = v.(map[string]interface{}) } } default: } } return id, fields, session //返回解析成功的参数 }
数据操做的具体实现,大可能是伪代码,项目后续会逐步完善,查询接口最重要,后面会有单独文章进行解析
func Query(tablename string, params map[string]interface{}, fields []string ) map[string]interface{} { //调用具体实现的私用函数,接口中分自动和手动两个函数,在私用函数中屏蔽差别内聚功能 return query(tablename, params, fields, "", nil) } func Insert(tablename string, params map[string]interface{}) map[string]interface{} { sql := "Insert into " + tablename values := make([]interface{},0) return execute(sql, values) } func Update(tablename string, params map[string]interface{}) map[string]interface{} { sql := "Update " + tablename + " set " values := make([]interface{},0) return execute(sql, values) } func Delete(tablename string, params map[string]interface{}) map[string]interface{} { sql := "Delete from " + tablename + " where" values := make([]interface{},0) return execute(sql, values) }
私用查询函数定义
//五个输入参数,分别适配自动与手动查询 func query(tablename string, params map[string]interface{}, fields []string, sql string, vaules []interface{}) map[string]interface{} { if vaules == nil { vaules = make([]interface{},0) } //调用真正的数据库操做函数 return execQeury("select "+ strings.Join(fields, ",")+" from " + tablename, vaules) }
非查询类具体操做函数
//由于golang把有结果集的和无结果集的操做是分开的,不象在java或node.js中,能够有高级函数进行统一操做,只能分开。 func execute(sql string, values []interface{}) map[string]interface{} { //返回json对象,以map形式表达 rs := make(map[string]interface{}) rs["code"] = 200 return rs }
查询类具体操做(已经实现),结果集以json对象封装,存储在map中
func execQeury(sql string, values []interface{}) map[string]interface{} { var configs interface{} ...//省略数据配置获取代码,请参照之前的文章 dao, err := mysql.Open(dialect, dbUser + ":"+dbPass+"@tcp("+dbHost+":"+dbPort+")/"+dbName+"?charset="+dbCharset) stmt, err := dao.Prepare(sql) rows, err := stmt.Query(values...) columns, err := rows.Columns() //取出字段名称 vs := make([]mysql.RawBytes, len(columns)) scans := make([]interface{}, len(columns)) for i := range vs { //预设取值地址 scans[i] = &vs[i] } var result []map[string]interface{} for rows.Next() { _ = rows.Scan(scans...) //塡入一列值 each := make(map[string]interface{}) for i, col := range vs { if col != nil { each[columns[i]] = string(col) //增值 }else{ each[columns[i]] = nil } } result = append(result, each) } rs["code"] = 200 //data, _ := json.Marshal(result) //这样就能转换为json rs["rows"] = result return rs }
数据库的批量操做,在前面的文章中已经用golang实现,只是还未封装,有兴趣的朋友能够看我前面的文章。
最终目标的入口将是一个网络服务,提供标准的restful服务,如今只是用来测试,再这说明一下愿景。
table := Bock.Bock{ //上体实例 Table: "role", //对role表时行操做 } var params map[string] interface{} //模拟json参数 args := make(map[string] interface{}) //其它参数 db := make([]DB.IBock, 1) //对接口编程 db[0] = &table //接口指向实例对象,这里能够现时处理多个不一样的实例 fields := []string {"id", "name"} args["fields"] = fields rs, _ := db[0].Retrieve(params, args) //在这能够循环处理多个不一样的实例,咱们最终的目标就是在这接受用户的http请求,由路由自动分发不一样的请求,咱们的数据库封装自动生成sql语句完成用户的基本需求。 fmt.Println(rs)
https://github.com/zhoutk/goTools
git clone https://github.com/zhoutk/goTools cd goTools go get go run bock.go go buid bock.go ./bock
通过多种方案的对比,发现go语言做为网络服务的吞吐率是最棒的,因此有了将以往在其它平台上的经验(node.js,java,python3),用go来实现,指望有惊喜,写代码我是认真的。python