技术经验分享:Go开源项目

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用版 2核4GB 50GB
简介: 技术经验分享:Go开源项目

gorp 是一个Go开源ORM框架.


Go关系型数据库持久化


我很含糊的称gorp是一个ORM框架. Go还没有真正的对象, 至少没有一个经典的感觉和Smalltalk/Java一样的,这是"O". gorp不知道任何你struct之间的关系(以后会实现,现在还在TODO list中). 所以"R"也是有质疑的(但是我使用这个名字,因为感觉上更贴切).


"M"是没有问题的. 给定一些Go结构和一个数据库, gorp应该可以减少你的重复代码.


我希望gorp能节省你的时间并简化从数据库获取数据的苦活来帮助你把精力投入到算法上而不是基础设施上面.


数据库驱动


gorp使用Go1 database/sql包. 一个完整可用的兼容驱动程序如下:


遗憾的是SQL数据库很多不一样的问题. gorp提供一个应该被每个数据库厂商实现的Dialect接口, Dialect支持如下数据库:


MySQL


PostgreSQL


sqlite3


这三个数据库都通过了测试, 请查看gorp_test.go, 例如针对这三个数据库的DSN.


特点


通过API或tag绑定struct字段到表的列


支持事务


从struct建立db架构正向工程(来做更好的单元测试)


在insert/update/delete的前后提供hook


自动为struct生成generate insert/update/delete语句


在insert后自动绑定自增主键到struct


通过主键删除


通过主键选择


可选的SQL跟踪日志


绑定任意SQL查询到struct


可通过一个version column来为update和delete实现乐观锁


待办事项


支持内嵌struct


安装


# install the library:


go get github.com/coopernurse/gorp


// use in your .go code:


import (


"github.com/coopernurse/gorp"


)


运行测试


现在测试测试包括了MySQL.我需要为测试工具添加额外的驱动, 但是现在你可以克隆repo并设置一个环境变量来为运行"go test"做准备


# Set env variable with dsn using mymysql format. From the mymysql docs,


# the format can be of 3 types:


#


# DBNAME/USER/PASSWD


# unix:SOCKPATHDBNAME/USER/PASSWD


# tcp:ADDRDBNAME/USER/PASSWD


#


# for example, on my box I use:


export GORP_TEST_DSN=gomysql_test/gomysql_test/abc123


# run the tests


go test


# run the tests and benchmarks


go test -bench="Bench" -benchtime 10


性能


gorp使用反射来构造SQL查询和绑定参数. 在gorp_test.go中查看BenchmarkNativeCrud 对 BenchmarkGorpCrud的一个简单的性能测试. 在我的MacBook Pro上它比手写SQL慢大约2%-3%.


示例


首先定义一些类型:


type Invoice struct {


Id int64


Created int64


Updated int64


Memo string


PersonId int64


}


type Person struct {


Id int64


Created int64


//代码效果参考:http://www.jhylw.com.cn/224639297.html

Updated int64

FName string


LName string


}


// Example of using tags to alias fields to column names


// The 'db' value is the column name


//


// A hyphen will cause gorp to skip this field, similar to the


// Go json package.


//


// This is equivalent to using the ColMap methods:


//


// table := dbmap.AddTableWithName(Product{}, "product")


// table.ColMap("Id").Rename("product_id")


// table.ColMap("Price").Rename("unit_price")


// table.ColMap("IgnoreMe").SetTransient(true)


//


type Product struct {


Id int64 db:"product_id"


Price int64 db:"unit_price"


IgnoreMe string db:"-"


}


然后创建一个映射器, 一般你再app启动时做一次.


// connect to db using standard Go database/sql API


// use whatever database/sql driver you wish


db, err := sql.Open("mymysql", "tcp:localhost:3306mydb/myuser/mypassword")


// construct a gorp DbMap


dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}


// register the structs you wish to use with gorp


// you can also use the shorter dbmap.AddTable() if you


// don't want to override the table name


//


// SetKeys(true) means we have a auto increment primary key, which


// will get automatically bound to your struct post-insert


//


t1 := dbmap.AddTableWithName(Invoice{}, "invoice_test").SetKeys(true, "Id")


t2 := dbmap.AddTableWithName(Person{}, "person_test").SetKeys(true, "Id")


t3 := dbmap.AddTableWithName(Product{}, "product_test").SetKeys(true, "Id")


自动创建删除已注册的表


// create all registered tables


dbmap.CreateTables()


// drop


dbmap.DropTables()


可选项:你可以传入一个log.Logger来跟踪全部的SQL语句:


// Will log all SQL statements + args as they are run


// The first arg is a string prefix to prepend to all log messages


dbmap.TraceOn("【gorp】", log.New(os.Stdout, "myapp:", log.Lmicroseconds))


// Turn off tracing


dbmap.TraceOff()


然后保存一些数据:


// Must declare as pointers so optional callback hooks


// can operate on your data, not copies


inv1 := &Invoice{0, 100, 200, "first order", 0}


inv2 := &Invoice{0, 100, 200, "second order", 0}


// Insert your rows


err := dbmap.Insert(inv1, inv2)


// Because we called SetKeys(true) on Invoice, the Id field


// will be populated after the Insert() automatically


fmt.Printf("inv1.Id=%d inv2.Id=%d\n", inv1.Id, inv2.Id)


你可以执行原始的SQL语句.尤其是批量操作时.


res, err := dbmap.Exec("delete from invoice_test where PersonId=?", 10)


想要做join? 只写SQL和struct, gorp将绑定他们:


// Define a type for your join


// It must contain all the columns in your SELECT statement


//


// The names here should match the aliased column names you specify


// in your SQL - no additional binding work required. simple.


//


type InvoicePersonView struct {


InvoiceId int64


PersonId int64


Memo string


FName string


}


// Create some rows


p1 := &Person{0, 0, 0, "bob", "smith"}


dbmap.Insert(p1)


// notice how we can wire up p1.Id to the invoice easily


inv1 := &Invoice{0, 0, 0, "xmas order", p1.Id}


dbmap.Insert(inv1)


// Run your query


query := "select i.Id InvoiceId, p.Id PersonId, i.Memo, p.FName " +


"from invoice_test i, person_test p " +


"where i.PersonId = p.Id"


list, err := dbmap.Select(InvoicePersonView{}, query)


// this should test true


expected := &InvoicePersonView{inv1.Id, p1.Id, inv1.Memo, p1.FName}


if reflect.DeepEqual(list【0】, expected) {


fmt.Println("Woot! My join worked!")


}


你也可以在一个事务中批量操作:


func InsertInv(dbmap DbMap, inv Invoice, per Person) error {


// Start a new transaction


trans := dbmap.Begin()


trans.Insert(per)


inv.PersonId = per.Id


trans.Insert(inv)


// if the commit is successful, a nil error is returned


return trans.Commit()


}


在更新数据到数据库(前/后)使用hook, 对时间戳很有效:


// implement the PreInsert and PreUpdate hooks


func (i Invoice) PreInsert(s gorp.SqlExecutor) error {


i.Created = time.Now().UnixNano()


i.Updated = i.Created


return nil


}


func (i Invoice) PreUpdate(s gorp.SqlExecutor) error {


i.Updated = time.Now().UnixNano()


return nil


}


// You can use the SqlExecutor to cascade additional SQL


// Take care to avoid cycles. gorp won't prevent them.


//


// Here's an example of a cascading delete


//


func (p Person) PreDelete(s gorp.SqlExecutor) error {


query := "delete from invoice_test where PersonId=?"


err := s.Exec(query, p.Id); if err != nil {


return err


}


return nil


}


你可以实现以下的hook


PostGet


PreInsert


PostInsert


PreUpdate


PostUpdate


PreDelete


PostDelete


All have the same signature. for example:


func (p MyStruct) PostUpdate(s gorp.SqlExecutor) error


乐观锁 (有点像JPA)


// Version is an auto-incremented number, managed by gorp


// If this property is present on your struct, update


// operations will be constrained


//


// For example, say we defined Person as:


type Person struct {


Id int64


Created int64


Updated int64


FName string


LName string


// automatically used as the Version col


// use table.SetVersionCol("columnName") to map a different


// struct field as the version field


Version int64


}


p1 := &Person{0, 0, 0, "Bob", "Smith", 0}


dbmap.Insert(p1) // Version is now 1


obj, err := dbmap.Get(Person{}, p1.Id)


p2 := obj.(*Person)


p2.LName = "Edwards"


dbmap.Update(p2) // Version is now 2


p1.LName = "Howard"


// Raises error because p1.Version == 1, which is out of date


count, err := dbmap.Update(p1)


_, ok := err.(gorp.OptimisticLockError)


if ok {


// should reach this statement


// in a real app you might reload the row and retry, or


// you might propegate this to the user, depending on the desired


// semantics


fmt.Printf("Tried to update row with stale data: %v\n", err)


} else {


// some other db error occurred - log or return up the stack


fmt.Printf("Unknown db err: %v\n", err)


}


至此结束.

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
Java Go 开发者
Docker容器技术简介及其与Go语言的结合点
【2月更文挑战第23天】本文首先概述了Docker容器技术的核心概念和优势,接着探讨了Go语言与Docker容器技术的结合点。通过阐述Docker的轻量级、可移植性和版本控制等特性,以及Go语言在容器化应用中的优势,本文旨在说明两者结合能够实现更高效、灵活的应用开发和部署。
|
1月前
|
运维 网络协议 安全
长连接网关技术专题(十):百度基于Go的千万级统一长连接服务架构实践
本文将介绍百度基于golang实现的统一长连接服务,从统一长连接功能实现和性能优化等角度,描述了其在设计、开发和维护过程中面临的问题和挑战,并重点介绍了解决相关问题和挑战的方案和实践经验。
147 1
|
1月前
|
安全 Go 数据处理
Go语言CSP编程实战:通道通信技术
Go语言CSP编程实战:通道通信技术
59 0
|
1月前
|
Go
高效Go语言编程:os包实用技术大揭示
高效Go语言编程:os包实用技术大揭示
52 0
|
3天前
|
编译器 Go C++
必知的技术知识:go语言快速入门教程
必知的技术知识:go语言快速入门教程
|
1月前
|
大数据 Java Go
Go语言在大数据处理中的核心技术与工具
【2月更文挑战第22天】本文深入探讨了Go语言在大数据处理领域的核心技术与工具。通过分析Go语言的并发编程模型、内存管理、标准库以及第三方工具库等方面,展现了其在大数据处理中的优势和实际应用。同时,本文也讨论了如何使用这些技术与工具构建高效、稳定的大数据处理系统,为开发者提供了有价值的参考。
|
1月前
|
消息中间件 Go
Go语言高级并发编程技术:深入探索同步原语与复杂并发模式
【2月更文挑战第6天】在Go语言中,除了基础的goroutine和channel之外,还提供了丰富的同步原语和高级并发模式。本文将深入探讨Go语言中的sync包、atomic包以及更复杂的并发模式,如管道、消息队列和生产者消费者模型。通过理解这些高级并发编程技术,我们可以更好地构建高性能、可扩展的并发系统。
|
1月前
|
编译器 Go 开发者
go run 命令核心技术详解
go run 命令核心技术详解
212 0
|
7月前
|
存储 安全 编译器
深入解析Go非类型安全指针:技术全解与最佳实践2
深入解析Go非类型安全指针:技术全解与最佳实践2
212 0
|
8天前
|
安全 测试技术 Go
Go语言在高并发场景下的应用
在当今互联网高速发展的时代,高并发已成为众多应用系统面临的核心问题。本文探讨了Go语言在高并发场景下的优势,并通过具体实例展示了其在实际应用中的效果和性能表现。