Go & ORM

7490b4e3e9cb85a1f7dc0c8ea01a86e5?s=47 Yo-An Lin
August 05, 2013

Go & ORM

ORM in Go: Design & Implementation

7490b4e3e9cb85a1f7dc0c8ea01a86e5?s=128

Yo-An Lin

August 05, 2013
Tweet

Transcript

  1. Go & ORM Yo-An Lin (c9s) Go 吧你 Monday, August

    5, 13
  2. • Yo-An Lin • Twitter: @c9s • GitHub: c9s •

    Go, PHP, Perl Monday, August 5, 13
  3. What’s Go? Monday, August 5, 13

  4. Go aims to combine the safety and performance of a

    statically typed compiled language with the expressiveness and convenience of a dynamically typed interpreted language. - Rob Pike Monday, August 5, 13
  5. History • End of 2007 start by Rob Pike, Ken

    Thompson and Robert Griesemer. • Implementation started after design in mid 2008. • Going public in November 2009. • Release of Go 1 in March 2012. Monday, August 5, 13
  6. Background • Rob Pike • Unix, Plan 9, Inferno, Limbo,

    UTF-8 • Ken Thompson • Multics, Unix, Plan 9, ed, UTF-8 • Turning Award Monday, August 5, 13
  7. Pros & Cons • Performance • Concurrency • Statically typed

    & Dynamically typed. • Safety • Low Memory Footprint (compared to Java) Monday, August 5, 13
  8. Reduce Energy Costs and Go Green How We Went from

    30 Servers to 2: Go http://blog.iron.io/2013/03/how-we-went-from-30-servers-to-2-go.html Monday, August 5, 13
  9. Success Stories • Airbrake: From Ruby to Go • Go

    at Heroku • Organizations using Go: Canonical, BBC, Heroku, SmugMug, SoundCloud, Bitly.... Monday, August 5, 13
  10. Overview of Go Monday, August 5, 13

  11. Hello World package main import "fmt" func main() { !fmt.Println("Hello

    World") } Monday, August 5, 13
  12. func CreateUser(id int64, name string) { ! // ... }

    Function Monday, August 5, 13
  13. Struct Type package main type User struct { !Id int64

    !Name string } Monday, August 5, 13
  14. package main type User struct { } func (self *

    User) Create() int64 { } Type Method Monday, August 5, 13
  15. user := User{} pUser := &User{} Allocation Monday, August 5,

    13
  16. type Sequence []int // Methods required by sort.Interface. func (s

    Sequence) Len() int { return len(s) } func (s Sequence) Less(i, j int) bool { return s[i] < s[j] } func (s Sequence) Swap(i, j int) { s[i], s[j] = s[j], s[i] } Interface Monday, August 5, 13
  17. Type Assertion type Stringer interface { String() string } var

    value interface{} // Value provided by caller. switch str := value.(type) { case string: return str case Stringer: return str.String() } Monday, August 5, 13
  18. Why Do We Need An ORM for Go? Monday, August

    5, 13
  19. More on Effective Go http://golang.org/doc/effective_go.html Monday, August 5, 13

  20. Real World Usage • 10+ Tables • A lot of

    CRUD operations in different methods. • We want performance for hot spots, for other situations, we don’t. Monday, August 5, 13
  21. Read Operation (1) ! query := `SELECT id, name, phone,

    cell_phone, address FROM users WHERE id = $1` ! userId := 10 ! r, err := db.Query(query, userId) ! if err != nil { ! ! t.Fatal(err) ! } ! defer r.Close() Monday, August 5, 13
  22. Read Operation (2) ! if !r.Next() { ! ! if

    r.Err() != nil { ! ! ! t.Fatal(r.Err()) ! ! } ! ! t.Fatal("expected row") ! } Monday, August 5, 13
  23. Read Operation (3) ! var id int64 ! var name

    string ! var phone string ! var cell_phone string ! var address string ! err = r.Scan(&id, &name, &phone, &cell_phone, &address) ! if err != nil { ! ! t.Fatal(err) ! } Monday, August 5, 13
  24. And That Is Only For Selecting Record(s).... Monday, August 5,

    13
  25. Let’s say you have 60+ tables Monday, August 5, 13

  26. And you need to do CRUD operations on all of

    them. Monday, August 5, 13
  27. 60 X 4 (CRUD) = 240 (Methods) Monday, August 5,

    13
  28. 240 X 20 (Avg. Lines) = 4800 (Lines) Monday, August

    5, 13
  29. We need something more dynamic & flexible Monday, August 5,

    13
  30. type Staff struct { ! Id int64 `field:"id"` ! Name

    string `field:"name"` ! Gender string `field:"gender"` ! StaffType string `field:"staff_type"` ! Phone string `field:"phone"` } put model fields in struct Monday, August 5, 13
  31. Reflection Monday, August 5, 13

  32. Reflection • Retrieve information dynamically (in the runtime): • Type

    Name • Struct Fields • Struct Tag • Struct Field Type • Function Type • ....etc Monday, August 5, 13
  33. Struct Metadata type Staff struct { ! Id int64 `json:"id"`

    ! Name string `json:"name"` ! Gender string `json:"gender"` ! StaffType string `json:"staff_type"` ! Phone string `json:"phone"` } Type Name Fields Field Type Struct Tag Tag Key Monday, August 5, 13
  34. Reflection import "reflect" staff := new(Staff) // get reflection value

    of pointer t := reflect.ValueOf(staff) // get reflection value t := reflect.ValueOf(staff).Elem() Monday, August 5, 13
  35. Reflection t := reflect.ValueOf(val).Elem() typeOfT := t.Type() for i :=

    0; i < t.NumField(); i++ { // do something } Monday, August 5, 13
  36. var tag reflect.StructTag = typeOfT.Field(i).Tag var json := tag.Get(“json”) Reflection

    Monday, August 5, 13
  37. Reflection t.Field(i).SetInt(keyValue) Struct Value Type Struct Field Monday, August 5,

    13
  38. SQLUtil Monday, August 5, 13

  39. SQLUtil • Table name conversion by inflector http://bitbucket.org/chrisfarms/inflect • Select,

    Insert, Update, Delete.. basic SQL clause generation. • https://github.com/c9s/gatsby/sqlutils Monday, August 5, 13
  40. SQLUtil: Using Inflector package sqlutils import "reflect" import "github.com/c9s/inflect" func

    GetTableNameFromTypeName(typeName string) string { ! if cache, ok := tableNameCache[typeName]; ok { ! ! return cache ! } ! tableNameCache[typeName] = inflect.Tableize(typeName) ! return tableNameCache[typeName] } Monday, August 5, 13
  41. func GetTableName(val interface{}) string { ! typeName := reflect.ValueOf(val).Elem().Type().Name() !

    return GetTableNameFromTypeName(typeName) } SQLUtil: Using Inflector Monday, August 5, 13
  42. SQLUtil: Select Clause // Given a struct object, return a

    "SELECT ... FROM {tableName}" SQL clause. func BuildSelectClause(val interface{}) string { ! tableName := GetTableName(val) ! return "SELECT " + BuildSelectColumnClauseFromStruct(val) + " FROM " + tableName } Monday, August 5, 13
  43. ActiveRecord Pattern Monday, August 5, 13

  44. staff := Staff{} staff.Name = "John" staff.Address = "...." staff.Phone

    = "076458882" result := staff.Create() Monday, August 5, 13
  45. No Problem Monday, August 5, 13

  46. Gatsby go  get  github.com/c9s/gatsby Monday, August 5, 13

  47. Gatsby ORM • Support PostgreSQL • Transaction support. • Dynamic

    CRUD Monday, August 5, 13
  48. import "gatsby" type Staff struct { ! Id int64 `field:",primary,serial"`

    ! Name string `field:",required"` ! Gender string `field:"gender"` ! Phone string `field:"phone"` ! gatsby.BaseRecord } Embedded Type Monday, August 5, 13
  49. func (self *Staff) Init() { ! self.SetTarget(self) } Call gatsby.BaseRecord.SetTarget

    呼叫 Parent method 時, Parent method 拿不到 current instance, 只能拿到 BaseRecord ⾃自⼰己 Monday, August 5, 13
  50. result := staff.Create() gatsby.Create() # not found gatsby.BaseRecord.Create() gatsby.BaseRecord.GetTarget() ....

    Monday, August 5, 13
  51. res := staff.Create() if res.Error != nil { !log.Println(res.Sql) !panic(res.Error)

    } Error Handling Monday, August 5, 13
  52. ⼯工商服務時間 Monday, August 5, 13

  53. GoTray http://gotray.extremedev.org Monday, August 5, 13

  54. Thank you Questions? Monday, August 5, 13