1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

func difference(a []string, b []string) (diff []string) {
mapB := make(map[string]bool)
for _, v := range b {
mapB[v] = true
}
for _, v := range a {
if _, ok := mapB[v]; !ok {
diff = append(diff, v)
}
}
return
}


func (engine *Engine) Migrate(value interface{}) error {
_, err := engine.Transaction(func(s *session.Session) (result interface{}, err error) {
if !s.Model(value).HasTable() {
log.Infof("table %s doesn't exist", s.RefTable().Name)
return nil, s.CreateTable()
}
table := s.RefTable()
rows, _ := s.Raw(fmt.Sprintf("SELECT * FROM %s LIMIT 1", table.Name)).QueryRows()
columns, _ := rows.Columns()
addCols := difference(table.FieldNames, columns)
delCols := difference(columns, table.FieldNames)
log.Infof("added cols %v, deleted cols %v", addCols, delCols)

for _, col := range addCols {
f := table.GetField(col)
sqlStr := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s %s;", table.Name, f.Name, f.Type)
if _, err = s.Raw(sqlStr).Exec(); err != nil {
return
}
}

if len(delCols) == 0 {
return
}
tmp := "tmp_" + table.Name
fieldStr := strings.Join(table.FieldNames, ", ")
s.Raw(fmt.Sprintf("CREATE TABLE %s AS SELECT %s from %s;", tmp, fieldStr, table.Name))
s.Raw(fmt.Sprintf("DROP TABLE %s;", table.Name))
s.Raw(fmt.Sprintf("ALTER TABLE %s RENAME TO %s;", tmp, table.Name))
_, err = s.Exec()
return
})
return err
}

GeeORM 的整体实现比较粗糙,比如数据库的迁移仅仅考虑了最简单的场景。实现的特性也比较少,比如结构体嵌套的场景,外键的场景,复合主键的场景都没有覆盖。ORM 框架的代码规模一般都比较大,如果想尽可能地逼近数据库,就需要大量的代码来实现相关的特性;二是数据库之间的差异也是比较大的,实现的功能越多,数据库之间的差异就会越突出,有时候为了达到较好的性能,就不得不为每个数据做特殊处理;还有些 ORM 框架同时支持关系型数据库和非关系型数据库,这就要求框架本身有更高层次的抽象,不能局限在 SQL 这一层。

GeeORM 仅 800 左右的代码是不可能做到这一点的。不过,GeeORM 的目的并不是实现一个可以在生产使用的 ORM 框架,而是希望尽可能多地介绍 ORM 框架大致的实现原理,例如