Eli's Blog

1. xorm简介

1.1 安装

1
go get github.com/go-xorm/xorm

1.2 模型定义

1
2
3
4
5
6
type Account struct {
Id int64
Name string `xorm:"unique"`
Balance float64
Version int `xorm:"version"` // 乐观锁
}

1.3 创建引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import (
_ "github.com/mattn/go-sqlite3" // 导入驱动包
"github.com/go-xorm/xorm"
)

var x *xorm.Engine

func init() {
var err error
x, err = xorm.NewEngine("sqlite3", "./bank.db") // 注册驱动,创建ORM引擎
if err != nil {
log.Fatalf("Fail to create engine: %v", err)
}

// 自动同步表结构
if err = x.Sync(new(Account)); err != nil {
log.Fatalf("Fail to sync database: %v", err)
}
}

1.4 增、删、改操作

1
2
3
4
5
6
7
8
9
10
11
12
13
// 新增
_, err := x.Insert(&Account{Name: name, Balance: balance})

// 删除
_, err := x.Delete(&Account{Id: id})

// 获取
a := &Account{}
exist, err := x.Id(id).Get(a)

// 修改
a.Balance += 100
_, err := x.Update(a)

1.5 排序操作

1
2
as := []*Account{}
err := x.Desc("balance").Find(&as)

1.6 事务及回滚

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建session对象
s := x.NewSession()

// 启动事务
err := s.Begin()

// 更新操作
s.Update(&Account{})

// 回滚操作
s.Rollback()

// 提交操作
err = s.Commit()

1.7 统计记录

1
2
3
4
5
// 统计所有数据
count, err := x.Count(new(Account))

// 链式操作过滤
count, err := x.Where("id > 10").Count(new(Account))

1.8 迭代查询

1
2
3
4
5
6
7
8
// 迭代查询表中符合条件的所有记录
err := x.Iterate(new(Account), func(idx int, bean interface{}) error {
fmt.Printf("%d: %#v\n", idx, bean.(*Account))
return nil
})

// 使用Rows对象
rows, err := x.Rows(new(Account))

1.9 查询方法

1
2
3
4
5
6
7
8
// 只选取某个字段
x.Cols("name").Iterate(new(Account), ...)

// 忽略某个字段
x.Omit("name").Iterate(new(Account), ...)

// 分页
x.Limit(3, 2).Iterate(new(Account), ...)

1.10 日志记录

1
2
3
4
5
6
7
8
9
10
11
12
func init_log() {
x.ShowSQL(true) // 开启日志

// 将日志保存到文件中
f, err := os.Create("sql.log")
if err != nil {
log.Fatalf("Fail to create log file: %v\n", err)
return
}

x.SetLogger(xorm.NewSimpleLogger(f))
}

1.11 LRU 缓存

1
2
cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
x.SetDefaultCacher(cacher)

1.12 事件钩子

1
2
3
4
5
6
7
func (a *Account) BeforeInsert() {
log.Printf("before insert: %s\n", a.Name)
}

func (a *Account) AfterInsert() {
log.Printf("afer insert: %s\n", a.Name)
}

2. 实例

2.1 model定义

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package models

import (
"log"
"os"

"fmt"

"github.com/pkg/errors"

"github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3"
)

type Account struct {
Id int64
Name string `xorm:"unique"`
Balance float64
Version int `xorm:"version"`
}

func (a *Account) BeforeInsert() {
log.Printf("before insert: %s\n", a.Name)
}

func (a *Account) AfterInsert() {
log.Printf("afer insert: %s\n", a.Name)
}

var x *xorm.Engine

func init() {
var err error
x, err = xorm.NewEngine("sqlite3", "./bank.db")
if err != nil {
log.Fatalf("Fail to create engine: %v", err)
}

if err = x.Sync(new(Account)); err != nil {
log.Fatalf("Fail to sync database: %v", err)
}

init_log()

cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
x.SetDefaultCacher(cacher)
}

func init_log() {
x.ShowSQL(true)

f, err := os.Create("sql.log")
if err != nil {
log.Fatalf("Fail to create log file: %v\n", err)
return
}

x.SetLogger(xorm.NewSimpleLogger(f))
}

func NewAccount(name string, balance float64) error {
_, err := x.Insert(&Account{Name: name, Balance: balance})
return err
}

func GetAccount(Id int64) (*Account, error) {
a := &Account{}
has, err := x.Id(Id).Get(a)
if err != nil {
return nil, err
} else if !has {
return nil, errors.New("Account not found")
}

return a, nil
}

func MakeDeposit(Id int64, deposit float64) (*Account, error) {
a, err := GetAccount(Id)
if err != nil {
return nil, err
}

a.Balance += deposit
_, err = x.Update(a)
return a, err
}

func MakeWithdraw(Id int64, withdraw float64) (*Account, error) {
a, err := GetAccount(Id)
if err != nil {
return nil, err
}

if a.Balance <= withdraw {
return nil, errors.New("Not enough balance")
}

a.Balance -= withdraw
_, err = x.Update(a)
return a, err
}

func MakeTransfer(Id1 int64, transfer float64, Id2 int64) error {
a1, err := GetAccount(Id1)
if err != nil {
return err
}

a2, err := GetAccount(Id2)
if err != nil {
return err
}

if a1.Balance <= transfer {
return errors.New("Not enough balance")
}

a1.Balance -= transfer
a2.Balance += transfer

s := x.NewSession()
defer s.Close()

if err = s.Begin(); err != nil {
return err
}

if _, err = s.Update(a1); err != nil {
s.Rollback()
return err
}

if _, err = s.Update(a2); err != nil {
s.Rollback()
return err
}

return s.Commit()
}

func GetAccountsSortedById() (as []*Account, err error) {
err = x.Asc("id").Find(&as)
return as, err
}

func GetAccountsSortedByNameDesc() (as []*Account, err error) {
err = x.Desc("name").Find(&as)
return as, err
}

func DeleteAccount(id int64) error {
_, err := x.Delete(&Account{Id: id})
return err
}

func GetAccountCount() (int64, error) {
return x.Count(new(Account))
}

func PrintAccounts() error {
err := x.Iterate(new(Account), func(idx int, bean interface{}) error {
fmt.Printf("%d: %#v\n", idx, bean.(*Account))
return nil
})

return err
}

func PrintAccounts2() error {
rows, err := x.Rows(new(Account))
if err != nil {
return err
}
defer rows.Close()

a := new(Account)
for rows.Next() {
if err = rows.Scan(a); err != nil {
return err
} else {
fmt.Printf("%#v\n", a)
}
}

return nil
}

2.2 main函数

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package main

import (
"fmt"

"gin.test/xorm/models"
)

const prompt = `Please enter number of operation:
1. Create new account
2. Show detail of account
3. Deposit
4. Withdraw
5. Make transfer
6. List account by Id
7. List account by Balance
8. Delete account
9. Get total number of account
10. Get all accounts
11. Exit`

func main() {
fmt.Println("Welcome bank of xorm")

Exit:
for {
fmt.Println(prompt)

var num int
fmt.Scanf("%d\n", &num)

switch num {
case 1:
fmt.Println("Please enter <name> <balance>: ")
var name string
var balance float64
fmt.Scanf("%s %f\n", &name, &balance)

if err := models.NewAccount(name, balance); err != nil {
fmt.Println(err)
}
case 2:
fmt.Println("Please enter <Id>: ")
var Id int64
fmt.Scanf("%d\n", &Id)

a, err := models.GetAccount(Id)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%#v\n", a)
}
case 3:
fmt.Println("Please enter <Id> <deposit>: ")
var Id int64
var deposit float64
fmt.Scanf("%d %f\n", &Id, &deposit)

a, err := models.MakeDeposit(Id, deposit)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%#v\n", a)
}
case 4:
fmt.Println("Please enter <Id> <withdraw>: ")
var Id int64
var withdraw float64
fmt.Scanf("%d %f\n", &Id, &withdraw)

a, err := models.MakeWithdraw(Id, withdraw)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%#v\n", a)
}
case 5:
fmt.Println("Please enter <Id> <transfer> <Id>: ")
var Id1 int64
var transfer float64
var Id2 int64
fmt.Scanf("%d %f %d\n", &Id1, &transfer, &Id2)

err := models.MakeTransfer(Id1, transfer, Id2)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Transfer succeeded.")
}
case 6:
as, err := models.GetAccountsSortedById()
if err != nil {
fmt.Println(err)
} else {
for i, a := range as {
fmt.Printf("%d: %v\n", i, a)
}
}
case 7:
as, err := models.GetAccountsSortedByNameDesc()
if err != nil {
fmt.Println(err)
} else {
for i, a := range as {
fmt.Printf("%d: %v\n", i, a)
}
}
case 8:
fmt.Println("Please enter <Id>: ")
var Id int64
fmt.Scanf("%d\n", &Id)

err := models.DeleteAccount(Id)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Delete account succeeded")
}
case 9:
count, err := models.GetAccountCount()
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Total number of account: %d\n", count)
}
case 10:
err := models.PrintAccounts2()
if err != nil {
fmt.Println(err)
}
case 11:
break Exit
}
}
}