go 语言操作数据库 CRUD

文章分享leyafo 发表了文章 • 2 个评论 • 327 次浏览 • 2017-09-07 18:38 • 来自相关话题

文章原链:http://www.leyafo.com/post/2017-09-07-go-db-crud/


go 语言标准库已经提供数据库访问通用接口,不同数据库需搭配相应连接 Driver。标准库里面 database/sql 实现基本数据类型 Scan, 基本的 Transaction 以及 sql 参数化。用这些东西去操作数据库完全够用,只是随着项目代码增长,sql string 会蔓延到项目的各个角落。若要修改数据库表结构,这些 sql string 就是噩梦一般存在。流行做法是使用 ORM(Object Relational Mapping) 去解决这个问题。ORM 封装好一些 CRUD 基本操作,可以避免大量手写 sql string.


go 编译型语言,无法做到像 Ruby 里面的 Method Missing 这样动态特性,可以为一个复合类型的 struct 随时添加一个 field. go 社区使用的 ORM 还是需要事先手动添加好 Field. 这样做需要对数据库和 struct 的成员做很多约定,写好对应的访问 tag。这些仍然无法避免修改数据库表时要一齐修改 go 代码。


我们可以仔细想想 ORM 真的是唯一的选择吗?ORM 实际上无法完美操作数据库,它做的事情无非就是这些事情:



  1. 参数化 sql string。

  2. 封装一些简单的 CRUD 操作。

  3. 序列化 sql 查询结果。


对于一些复杂的数据库查询语句,任何强大的 ORM 库,灵活的动态语言特性都无法解决。况且 ORM 也未必是好东西,它简化了操作,却间接隐藏了数据库的一些功能。如果不脱离 ORM 依赖我们无法去更好的操作数据库,去自己优化查询语句。以 go 语言目前的特性来看,这个社区永远也不可能会做出能像动态语言那样灵活的 ORM。关于 ORM 与 go 语言更详细的吐槽请见这里


如果不用 ORM 我们碰到的无非是上面提到的 3 个问题而已。把这个三个问题掰开处理,第一个问题是不存在的。参数化 sql string 这件事情我们只需要小心的处理就能防止 sql 注入这样的安全问题。剩下的就是 2 和 3 的问题,而第二个问题我们暂时也不需要解决,我们一开始就去写一些重复代码好了。而问题 3 我们可以手动自己去序列化,也可以用社区的一些库去解决这个问题。社区已经有了不错的解决方案,我用的就是 sqlx 去解决的这个问题。


当初项目开始时我考察不少 ORM 库后,果断抛弃使用 ORM。项目经过一段时间迭代后,不用 ORM 没有特别明显的不便。唯一的问题就是我需要手动为每一张表做好 table 到 go struct 之间的映射。需要写非常多重复性的 CRUD 操作代码。这些代码很难使用统一方法减少重复,只好任其膨胀。直到有一天我看到这篇文章,原来 go 语言需要写重复代码这个问题在其他问题领域也存在,而社区老司机们解决这个问题的方式是使用机器生成代码。社区大范围这么做的原因是 go 语言的语法非常简单,很容易用 go 生成 go 代码。


写 CRUD 生成器思路很简单,只需要去数据库里查询 table 的详细信息,为不同的 column 数据类型映射 go 数据类型。最后用 go 的 template 实现一些简单的 CRUD 方法。对于一般的 select 查询语句我这里没有去做实现,因为不同的表查询条件是不一样的,这种差异用函数传参解决会更好。生成器做 column 映射和拼接查询语句是极好的。


最后在项目里面把生成器生成的代码和手写的数据库相关代码分离开来,以方便数据库表结构变动后可以让生成器重新生成代码。这里是我写的这个简单的生成器,参照了一部分 xo 项目的代码,由于我只支持 Postgresql, 没有去支持 join 和 has_many 这样的操作,代码相对他们要简单很多。


事情到这里并没有完,我们还需要序列化后的查询结果做一些反序列化的工作,go 语言的 struct tag 对这方面有很好的支持。可是我们用机器生成的代码是无法控制外界需要的各种反序列化的字段。下面以序列化 json 为例,我们并不想把数据库的 ID 字段暴露给外界。如果用生成器去控制这些配置,生成器需要做更多的配置,数据库表字段需要更多约定,这无疑会增加生成器的难度。我们必须要为每一张表手动单独做一个 Export 的结构体与方法,以满足各种其他序列化的要求。


type UserExport struct {
Name string `json:"name"`
Email string `json:"email"`
UpdatedAt string `json:"updated_at"`
}

func (u User) Export() UserExport {
return UserExport{
Name: u.Name,
Email: u.Email,
UpdatedAt: u.UpdatedAt,
}
}

func (u User) MarshalJSON() ([]byte, error) {
return json.Marshal(u.Export())
}

如上所示,我们可以为 User 表配置不同 field 和 tag。可以单独为不同的序列化数据类型写一个 Marshal 方法,这些都是手动可以控制。为了灵活我们还可以把 Export 这个结构体直接嵌入到其他结构体中,不用担心 Marshal 会继承式覆盖后面 Marshal 方法。

北京-招聘Go工程师,可接受实习生

招聘应聘小井 发表了文章 • 1 个评论 • 278 次浏览 • 2017-09-07 18:18 • 来自相关话题

我公司14年成立,完成两轮融资,目前正在招聘Go工程师1名,待遇:每月15-30K左右。周末法定都休息,五险一金,带薪年假,年终奖金。优秀实习生也可。 要求:1、熟悉 Go 语言。2、有高负载 Server 实战经验。3、对计算机技术有发自内心的兴趣。 ... 查看全部

我公司14年成立,完成两轮融资,目前正在招聘Go工程师1名,待遇:每月15-30K左右。周末法定都休息,五险一金,带薪年假,年终奖金。优秀实习生也可。
要求:1、熟悉 Go 语言。2、有高负载 Server 实战经验。3、对计算机技术有发自内心的兴趣。 欢迎大家推荐,有丰厚推荐奖金。 联系:QQ 3407909729

关于控制CPU使用率曲线的问题

有问必答Xanthus 回复了问题 • 3 人关注 • 4 个回复 • 294 次浏览 • 2017-09-07 16:18 • 来自相关话题

如何判断 param ...interface中数据的类

有问必答laoge 回复了问题 • 1 人关注 • 2 个回复 • 128 次浏览 • 2017-09-07 16:02 • 来自相关话题

GoCN每日新闻(2017-09-07)

回复

每日新闻astaxie 发起了问题 • 1 人关注 • 0 个回复 • 534 次浏览 • 2017-09-07 08:44 • 来自相关话题

goroutine 奇怪的输出顺序

有问必答trigged 回复了问题 • 3 人关注 • 3 个回复 • 265 次浏览 • 2017-09-06 23:47 • 来自相关话题

beego orm join 联合查询的问题

回复

有问必答kggg 回复了问题 • 1 人关注 • 1 个回复 • 182 次浏览 • 2017-09-06 22:12 • 来自相关话题

有人碰到过 runtime.gcAssistAlloc 运行耗时的问题吗?

有问必答plain 回复了问题 • 2 人关注 • 1 个回复 • 170 次浏览 • 2017-09-06 14:48 • 来自相关话题

GoCN每日新闻(2017-09-06)

回复

每日新闻astaxie 发起了问题 • 1 人关注 • 0 个回复 • 571 次浏览 • 2017-09-06 08:00 • 来自相关话题

小白求教,使用goroutine为啥每次运行结果不一样?

有问必答gomaster_me 回复了问题 • 7 人关注 • 9 个回复 • 544 次浏览 • 2017-09-05 22:43 • 来自相关话题

用反射怎么给结构体内的属性赋值?具体请看正文

有问必答zhaohu 回复了问题 • 4 人关注 • 3 个回复 • 220 次浏览 • 2017-09-05 13:48 • 来自相关话题

[上海]招募Golang工程师

招聘应聘JennyZhou 回复了问题 • 5 人关注 • 7 个回复 • 1589 次浏览 • 2017-09-05 11:02 • 来自相关话题

govendor 如何添加本地的package

有问必答zhangxxxww 回复了问题 • 2 人关注 • 2 个回复 • 181 次浏览 • 2017-09-05 10:50 • 来自相关话题

Jetbrains 家族利器之 Gogland 简明教程

文章分享bingohuang 发表了文章 • 8 个评论 • 570 次浏览 • 2017-09-05 10:03 • 来自相关话题

gogland.png

喜欢用 IDE 做开发的同... 查看全部

gogland.png


喜欢用 IDE 做开发的同学必定不能错过 Jetbrains 家族的 IDE,款款精品,可谓都是 IDE 中的神兵利器。


这里介绍该家族 又一款新的 IDE —— 用于开发 Go 语言 的 Gogland,喜欢折腾 Go 语言 IDE 的同学一定要试试看。


一、下载 Gogland


该步骤非常简单,访问 Gogland 的下载页面,下载相应平台的安装包即可,三大平台(Mac、Linux、Windows)都有支持。


注:当前 Gogland 还是预发布版,想了解更多 Gogland 信息,请参考附录二


二、安装 Go 环境


接下来需要安装相应的 Go 环境,才能在 Gogland 中开发 Go 程序。


安装方法多样,首先可参考官网安装指南,但如果你有 Go 多版本的需求(比如新老版本共存),你想简化 Go 配置过程(省去GOPATH、GOROOT等的配置),你还希望它支持跨平台(支持Mac和Linux),那么我特别推荐这款 Go 环境安装工具:GVM —— 详情可参考我的写的这篇文章《Go 语言多版本安装及管理利器 - GVM》


二、设置工作空间


用过 Eclipse 的同学必不陌生 Workspace (工作空间),Go 也有自己的工作空间,建议将 Go 的代码放在一个单独的空间,类似布局如下:


- workspace
- bin
- pkg
- src
- github.com
- user_name
- project1
- project2

然后将该工作空间(workspace 所在目录)设置到 GOPATH 当中。GOPATH 可用于 Go 导入、安装、构建和更新,还会被 Gogland 自动识别(见第四节)。


注:如果你采用上述说的 GVM 的安装方式,将自动创建一个 Workspace,并配置好 GOPATH 等相关环境变量,这也是 GVM 方便的地方。


三、设置 Gogland 的 GOROOT


在 Gogland 中,需要配置当前项目的 GOROOT,用来编译运行 Go 代码。配置起来也非常方便,打开 Settings → Go → GOROOT 设置即可:


gogland-goroot.png


如果你本地安装了多个版本的 Go,也可以在右侧下拉选择相应的版本,这依赖于你本地有多个版本的 Go 环境了。


四、设置 Gogland 的 GOPATH


Gogland 中的 GOPATH 设置功能非常实用和强大,你既可以配置多个全局的 GOPATH (IDE 会自动识别环境变量中的 GOPATH,可不勾选),也可以配置多个项目级别的 GOPATH,甚至还可以配置多个模块级别的 GOPATH。打开 Settings → Go → GOPATH 设置如下:
gogland-gopath.png


五、建立新的 Go 项目


这个很简单,在主菜单选择 File → New → Project, 继而弹出 New Project 设置向导:
gogland-new-project.png


此处就需要选择你在上面配置好的 GOROOT,新建的项目会自动关联全局 GOPATH,你还可以参照第四节说是设置你项目的 GOPATH


五、导入已有 Go 项目


如果你本地已有 Go 项目代码,只需在主菜单选择 File → Open,打开你的项目目录即可。


最新版的 Gogland有一个非常体贴的小功能,会自动匹配你当前设置好的全局 GOROOT。当然,你也可以在设置中更换。


接下来会开始建立索引(index),第一次建立的时候可能会比较慢,CPU消耗比较大,耗时长短依赖于你工作空间的代码量,但后续用起来就非常快捷了,索引的建立也是增量的。


注: 但也有一个问题,每次升级 gogland 或者安装更新插件,也会重新建立索引,这个确实不友好,希望 Jetbrains 后续能改善这点。


七、运行/调试/测试程序


当你有了一个 Go 项目工程,二话不说,先跑跑看(前提是你要有一个可执行入口,在 main package 下的 main 函数)。


为了在 Gogland 运行一个 Go 程序,你需要用到 Run Configuration。使用方法如下:



  • 在主菜单栏或工具栏打开:Run → Edit Configurations

  • 点击 Edit Configurations,打开 Run/Debug Configuration 对话框

  • 点击 + 号按钮,选择你需要的运行配置,Go 用到的配置类型如下(按使用频率解释):
    gogland-run-config.png

    1. Go Application:相当于执行 go build 和运行可执行文件命令,该配置会生成可执行文件,也可执行debug

    2. Go Single File:相当于 go run 命令,该配置不会生成可执行文件,不能执行 debug

    3. Go Test:用于运行测试代码,相当于 go test,有三种测试框架可供选择:gotest,gocheck 和 gobench

    4. Go Remote:提供了 Go 的远程调试支持,你只需要设置要远程连接的 Host 和 Port,并且保证你要调试的程序是通过 Delve 启动的

    5. Go App Engine:允许你将程序部署到 Google AppEngine,前提是你有使用 Google 云,并且你的程序模块加载了 Go AppEngine SDK



以上就是 Go 工程在运行/调试/测试过程中会用到的配置类型,特别是前三项,最为常用。


如果你要运行程序,推荐使用1和2。而 Gogland 智能的地方在于,你可以通过鼠标右击这样快捷的方式来运行和配置,如下,在有 main 函数的地方右击即可:
gogland-run.png


如果你要调试程序,本地调试可用1,远程调试请使用4。


如果你要测试程序,请使用第3种方式。


同时,在测试程序的基础上,你还可以执行调试和代码覆盖率统计,功能十分强大!


gogland-run-coverage.png


总的来说,Gogland 继承了 Jetbrains 家族的基因,完全可以作为 Go 语言编程的神兵利器,还不赶紧来试试看


注:提供两个附录,让大家更全面的了解 Gogland。


附录一:常用辅助快捷方式:以 Mac 为例



  1. 查看提示帮助:默认快捷键是 ⌥⏎。最常用的快捷键之一,从 Eclipse 转过来的同学对该快捷键肯定不陌生,很多地方都可以用上该快捷键,特别是有错误的时候,有时还会有意想不到的好效果哦。

  2. 查看声明:按住 Cmd 健(Windows 下是 Ctrl键),鼠标左键点击相关标识。最常用的快捷键之一,跳转声明、查看源码必不可少。

  3. 查看函数参数:⌘P。直接在当前函数下查看,当然你也可以用上面的查看声明方式跳转过去查看。

  4. 代码重构:在你需要重构的地方,右击选择 Refactor 即可。

  5. 查看使用率:在你需要查看使用率的地方,右击选择 Find Usages 即可。

  6. 还有一个非常赞的功能,就是设置 live template,使用方式就是:缩写 + Tab 键。配置方式在 Settings → Editor → Live Templates 中,Go 也内置了不少快捷模板哦。


以上快捷键你都可以在 Settings → Editor 中查找或重置,更多 Intellij IDE 的使用小技巧可以查看:Discover IntelliJ IDEA


附录二:常见问题


1、Gogland 代表什么?


Gogland 是一个代号,并不是最终的产品名称。灵感来自于芬兰湾的一座小岛,离芬兰湾另一座小岛 Kotlin(也是 Jetbrains 推出的一门语言) 不远。


2、Gogland 是否会开源?


当前没计划开源。


3、Gogland 是否免费?


当前预览版免费,正式版还是要收费的。


4、Gogland 中的 Go 插件是否能用于其他基于 IntelliJ 的 IDE?


可以的,这个 Go 官方插件 和 Gogland 所带的 Go 相关功能是一致的,可用于 IntellIJ IDEA 极限版和其它付费 IDE,不过还不能用于社区版。


5、Gogland 绑定了哪些其他 IntelliJ 插件?


Git, Terminal, Textmate, JavaScript, CSS, HTML, Database Tools 和 Coverage 等。


6、Gogland 什么时候正式发布?


还没有确切时间,预计每个月会发布一个 EAP build 版本。欢迎多多给我们反馈


7、我在哪里可以提交 issues 和功能需求?


请使用 Gogland 的 issue 跟踪:https://youtrack.jetbrains.com/issues/GO

Node.js 之父 Ryan 推薦大家使用 Go 語言,而不是 Node.js

文章分享appleboy 发表了文章 • 4 个评论 • 797 次浏览 • 2017-09-05 09:34 • 来自相关话题

『Node.js 之父 Ryan 目前在 Google Brain 一手建立 Node.js 世界,但是在訪談中,竟然推薦大家使用 Go 語言,而不要使用 Node.js 當作後端 (假如您想要建構一個龐大的系統)。內文中提到 Node.js Callb... 查看全部

『Node.js 之父 Ryan 目前在 Google Brain 一手建立 Node.js 世界,但是在訪談中,竟然推薦大家使用 Go 語言,而不要使用 Node.js 當作後端 (假如您想要建構一個龐大的系統)。內文中提到 Node.js Callback 的缺陷在 Node.js 出現了 async 了有效了解決大量使用 Callback。Ryan 也非常驚訝地說,他沒想到在 Node.js Client 端竟然如此熱門。要建立小量的開發系統,他非常推薦使用 Node.js,但是如果您要建立龐大的分散式 DNS 系統,我覺得不會推薦你使用 Node。』


說到 Client Side 工具,在 Node.js 世界的確在 Web 領域佔有一席之地,就像我現在在寫 Go,也是大量使用 Node.js 工具 (像是 Webpack, Gulp, Imagemin 等圖片壓縮工具) 來幫助建置 Web 網站。


詳細原文可以參考: https://www.mappingthejourney.com/single-post/2017/08/31/episode-8-interview-with-ryan-dahl-creator-of-nodejs/