Golang实践录:xorm使用记录

xorm使用记录。主要针对 mysql,也会涉及其它的。

技术总结

提供通用接口,适用于多种数据库,隐藏细节。但需要手动添加数据库驱动(Golang 本身亦如是)。
可以用 Sync2 创建数据表,多次创建只提示警告,不会出错。要自行判断,或者额外创建好表,后续不再创建。
需要预先创建数据库,否则即使 Sync2 成功,也不会有数据表。
引入 xorm 包,再使用 sql 驱动,但使用下划线,表示只引入不使用包内函数。
xorm 的结构体成员要大写。

安装

1
2
3
4
5
6
7
8
9
10
11
12
xorm:
go get github.com/go-xorm/xorm
go get github.com/go-xorm/core
go get github.com/go-xorm/cmd/xorm
// 以上路径已舍弃

驱动:
go get github.com/go-sql-driver/mysql // mysql
go get github.com/mattn/go-sqlite3 //SQLite
go get github.com/denisenkom/go-mssqldb // sqlserver
go get github.com/lunny/godbc // sqlserver

2021.5 更新:
测试发现,如果使用新的路径,Insert 插入数据会提示near ")": syntax error,使用github.com即旧的路径正常,原因未明,从发现问题到最终确认,耗时约半天。后续可能修改代码。

2021.3 更新:
xorm 仓库于2019年已经从 github 移到https://gitea.com/xorm/xorm。包的路径发生了变化。下载及引用如下:

1
2
3
4
5
6
7
go get xorm.io/xorm
go get xorm.io/core
go get xorm.io/cmd/xorm

"github.com/go-xorm/xorm" --> "xorm.io/xorm"
"github.com/go-xorm/core" --> "xorm.io/core"

结构体与字段映射

常用方式:
SnakeMapper: 默认方式,驼峰命名,如 UserInfo -> user_info
SameMapper: 相同。UserInfo -> UserInfo
GonicMapper:在SnakeMapper的基础上增加了特例,ID -> id
PrefixMapper:前缀。
SuffixMapper:后缀。

设置xorm的字段名称和数据库中的一样(包括表名、字段表)
engine.SetMapper(core.SameMapper{})

不建议做太复杂,选择一两种常用的即可。

示例1:

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
结构体:
type User struct {
Id int64 `xorm:"int(20) pk not null autoincr 'id'"`
Email string `xorm:"varchar(255) default null"`
First_name string `xorm:"varchar(255) default null"`
Last_name string `xorm:"varchar(255) default null"`
Username string `xorm:"varchar(255) default null"`
}
对应数据表:
mysql> desc user;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(20) | NO | PRI | NULL | auto_increment |
| email | varchar(255) | YES | | NULL | |
| first_name | varchar(255) | YES | | NULL | |
| last_name | varchar(255) | YES | | NULL | |
| username | varchar(255) | YES | | NULL | |
+------------+------------
注:结构体名即为表名,表名为小写,字段按驼峰命名,变量加下划线,保留。


// 自定义部分字段,即所有字段小写,无下划线
type User2 struct {
Id int64 `xorm:"int(20) pk not null autoincr"`
Email string `xorm:"varchar(255) default null"`
First_name string `xorm:"varchar(255) default null 'firstname'"`
Last_name string `xorm:"varchar(255) default null 'lastname'"`
Username string `xorm:"varchar(255) default null 'username'"`
}
mysql> desc user2;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(20) | NO | PRI | NULL | auto_increment |
| email | varchar(255) | YES | | NULL | |
| firstname | varchar(255) | YES | | NULL | |
| lastname | varchar(255) | YES | | NULL | |
| username | varchar(255) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
注:接上,但在变量中设置了字段名称,变量有下划线,但字段没有。


// 保存完全一致
type User3 struct {
Id int64 `xorm:"int(20) pk not null autoincr"`
Email string `xorm:"varchar(255) default null"`
First_name string `xorm:"varchar(255) default null"`
Last_name string `xorm:"varchar(255) default null"`
Username string `xorm:"varchar(255) default null"`
}
代码:engine.SetMapper(core.SameMapper{})
mysql> desc User3;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| Id | int(20) | NO | PRI | NULL | auto_increment |
| Email | varchar(255) | YES | | NULL | |
| First_name | varchar(255) | YES | | NULL | |
| Last_name | varchar(255) | YES | | NULL | |
| Username | varchar(255) | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+

注:表名和字段名完全一致。

反向导出结构体

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

会自动安装到 Golang 的 bin 目录,在命令行直接输入 xorm 即可。

1
2
3
4
5
xorm reverse mysql name:password@(ip:port)/xxx?charset=utf8 ./templates/goxorm/
windows 示例:
xorm reverse mysql root:root@\(172.18.18.18:3305\)/mydb?charset=utf8 ./templates/goxorm/
注:templates是指$GOPATH/src/github.com/go-xorm/cmd/xorm/templates

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE TABLE `userc` (
`信息id` bigint(20) NOT NULL AUTO_INCREMENT,
`邮箱地址` varchar(255) DEFAULT NULL,
`名` varchar(255) DEFAULT NULL,
`姓` varchar(255) DEFAULT NULL,
`姓名` varchar(255) DEFAULT NULL,
PRIMARY KEY (`信息id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

转换后:
type Userc struct {
true信息id int64 `xorm:"not null pk autoincr BIGINT(20)"`
true邮箱地址 string `xorm:"VARCHAR(255)"`
true名 string `xorm:"VARCHAR(255)"`
true姓 string `xorm:"VARCHAR(255)"`
true姓名 string `xorm:"VARCHAR(255)"`
}

数据库的中文

测试中文

1
2
3
4
5
6
7
8
9
type 学生信息表 struct {
true信息id int64 `xorm:"not null pk autoincr BIGINT(20)"`
true邮箱地址 string `xorm:"VARCHAR(255)"`
true名 string `xorm:"VARCHAR(255)"`
true姓 string `xorm:"VARCHAR(255)"`
true姓名 string `xorm:"VARCHAR(255)"`
}
engine.SetMapper(core.SameMapper{})
engine.Sync2(new(学生信息表))

结论:可以使用中文变量,但查询数据,会出错,故还是按英文进行

所遇问题

如果有小写,会提示:

1
panic: reflect.Value.Interface: cannot return value obtained from unexported field or method

可以用单引号指定特定名称。

遇到空值:

1
Scan failed: sql: Scan error on column index 26, name "姓名": converting driver.Value type string ("NULL") to a float32: invalid syntax

数据库操作

创建

1
engine, err := xorm.NewEngine(driverName, dataSourceName)

sql语句

支持sql语句:

1
2
3
4
5
6
7
8
直接查,返回[]map[string][]byte类型的切片
results, err := engine.Query("select * from user")

// 适用增改查
affected, err := engine.Exec("update user set .... where ...")

engine.Sql("select * from user").Find()

注:方便根据自定义的语句获取结果

1
2
3
4
5
6
7
8
9
10
affected, err := engine.Insert(&struct)
// INSERT INTO struct () values ()
affected, err := engine.Insert(&struct1, &struct2)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values ()
affected, err := engine.Insert(&sliceOfStruct)
// INSERT INTO struct () values (),(),()
affected, err := engine.Insert(&struct1, &sliceOfStruct2)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values (),(),()

1
2
affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ...

1
2
affected, err := engine.Update(&user)
// UPDATE user SET ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
查数量
counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user

// 查询一条记录,返回结构体
has, err := engine.Get(&user)
// SELECT * FROM user LIMIT 1

// 查询多条记录
sliceOfStructs := new(Struct)
err := engine.Find(sliceOfStructs)
// SELECT * FROM user

err := engine.Iterate(...)
// SELECT * FROM user

rows, err := engine.Rows(...)
// SELECT * FROM user
bean := new(Struct)
for rows.Next() {
err = rows.Scan(bean)
}

有条件的数据库操作

Id In

1
2
3
4
5
6
7
8
engine.Id(1).Get(&user) // for single primary key
// SELECT * FROM user WHERE id = 1
engine.Id(core.PK{1, 2}).Get(&user) // for composite primary keys
// SELECT * FROM user WHERE id1 = 1 AND id2 = 2
engine.In("id", 1, 2, 3).Find(&users)
// SELECT * FROM user WHERE id IN (1, 2, 3)
engine.In("id", []int{1, 2, 3})
// SELECT * FROM user WHERE id IN (1, 2, 3)

Where And Or

1
2
engine.Where().And().Or().Find()
// SELECT * FROM user WHERE (.. AND ..) OR ...

OrderBy Asc Desc

1
2
3
4
engine.Asc().Desc().Find()
// SELECT * FROM user ORDER BY .. ASC, .. DESC
engine.OrderBy().Find()
// SELECT * FROM user ORDER BY ..

Limit Top

1
2
3
4
5
engine.Limit().Find()
// SELECT * FROM user LIMIT .. OFFSET ..
engine.Top(5).Find()
// SELECT TOP 5 * FROM user // for mssql
// SELECT * FROM user LIMIT .. OFFSET 0 //for other databases

Cols Omit Distinct

1
2
3
4
5
6
7
8
9
10
engine.Cols("col1, col2").Find()
// SELECT col1, col2 FROM user
engine.Cols("col1", "col2").Where().Update(user)
// UPDATE user set col1 = ?, col2 = ? Where ...
engine.Omit("col1").Find()
// SELECT col2, col3 FROM user
engine.Omit("col1").Insert()
// INSERT INTO table (non-col1) VALUES ()
engine.Distinct("col1").Find()
// SELECT DISTINCT col1 FROM user

Join GroupBy Having

1
2
3
4
engine.GroupBy("name").Having("name='xlw'").Find()
//SELECT * FROM user GROUP BY name HAVING name='xlw'
engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find()
//SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id

综合应用

本小节以一些应用场合为例,记录sql函数及xorm函数的示例。
为方便使用2种方式,可实现2种接口。

1
2
3
4
5
6
sqlstr := fmt.Sprintf("show tables like '%s'", newTableName)
cnt, err := engine.SQL(sqlstr).Count()

sqlstr := fmt.Sprintf("select count(*) from %s where %s > %d", tableName, idName, startID)
err := sqldb.QueryRow(sqlstr).Scan(&totalCnt)

参考:
https://www.cnblogs.com/guhao123/p/4159688.html