与你相识
博主介绍:
– 本人是普通大学生一枚,每天钻研计算机技能,CSDN主要分享一些技术内容,因我常常去寻找资料,不经常能找到合适的,精品的,全面的内容,导致我花费了大量的时间,所以会将摸索的内容全面细致记录下来。另外,我更多关于管理,生活的思考会在简书中发布,如果你想了解我对生活有哪些反思,探索,以及对管理或为人处世经验的总结,我也欢迎你来找我。 – 目前的学习专注于Go语言,辅学算法,前端领域。也会分享一些校内课程的学习,例如数据结构,计算机组成原理等等,如果你喜欢我的风格,请关注我,我们一起成长。
XORM学习笔记
我直接看的官方说明文档。
对xorm的说明
我现在所看的xorm 库,是基于原版xorm库的定制增强版。
这是因为本定制版有第三方库依赖,而就是为了维持原版xorm对第三方库零依赖性,所以单独开了一个定制增强版本的xorm库 。
相关核心功能和原版xorm 一致并随原版xorm 更新。
安装
go get -u github.com/xormplus/xorm
创建orm引擎
xorm可以同时存在多个orm引擎,一个orm引擎称为Engine ,一个Engine 一般只对应一个数据库。
Engine 通过调用xorm.NewEngine 生成。
import (
_ "github.com/go-sql-driver/mysql"
"github.com/xormplus/xorm"
)
var engine *xorm.Engine
func main() {
var err error
engine, err = xorm.NewEngine("mysql", "root:123@/test?charset=utf8")
}
engine可以通过engine.Close来手动关闭,但是一般情况下可以不用关闭,在程序退出的时候会自动关闭。
在engine创建完成之后可以设置一些选项:
日志是一个接口,通过设置日志,可以显示SQL,警告以及错误等,默认的显示级别为INFO。
- engine.ShowSQL(true),则会在控制台打印出生成的SQL语句;
- engine.Logger().SetLevel(core.LOG_DEBUG),则会在控制台打印调试及以上的信息;
如果希望将信息不仅打印到控制台,而是保存为文件,那么可以通过类似如下的代码实现,NewSimpleLogger(w io.Writer)接收一个io.Writer接口来将数据写入到对应的设施中。
f, err := os.Create("sql.log")
if err != nil {
println(err.Error())
return
}
engine.SetLogger(xorm.NewSimpleLogger(f))
定义表的结构体
统一前缀,后缀
可以通过一种方式在表的字段前加上统一的前缀,后缀或缓存映射,但是表字段不会改变。
tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, "prefix_")
engine.SetTableMapper(tbMapper)
ore.NewSufffixMapper(core.SnakeMapper{}, "suffix")
Column属性
可以在列中对Column的一些属性进行定义
type User struct {
Id int64
Name string `xorm:"varchar(25) notnull unique 'usr_name'"`
}
还有很多其它的内容,参考官方操作手册。
表结构操作
获取数据库表结构信息
- DBMetas()
xorm支持获取表结构信息,通过调用engine.DBMetas()可以获取到数据库中所有的表,字段,索引的信息。
表操作
创建表使用engine.CreateTables(),参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine(),如果对应的数据库支持,这两个方法可以在创建表时指定表的字符编码和使用的引擎。Charset()和StoreEngine()当前仅支持Mysql数据库。
判断表是否为空,参数和CreateTables相同
判断表是否存在
删除表使用engine.DropTables(),参数为一个或多个空的对应Struct的指针或者表的名字。如果为string传入,则只删除对应的表,如果传入的为Struct,则删除表的同时还会删除对应的索引。
同步数据库结构
同步能够部分智能的根据结构体的变动检测表结构的变动,并自动同步。目前有两个实现:
Sync
Sync将进行如下的同步操作:
- 自动检测和创建表,这个检测是根据表的名字
- 自动检测和新增表中的字段,这个检测是根据字段名
- 自动检测和创建索引和唯一索引,这个检测是根据索引的一个或多个字段名,而不根据索引名称
err := engine.Sync(new(User), new(Group))
Sync2
对sync进行了改进,目前推荐使用。
下面的警告信息需要将engine.ShowWarn 设置为true 才会实现
- 自动检测和创建表,这个检测是根据表的名字
- 自动检测和新增表中的字段,这个检测是根据字段名,同时对表中多余的字段给出警告信息
- 自动检测,创建和删除索引和唯一索引,这个检测是根据索引的一个或多个字段名,而不根据索引名称。因此这里需要注意,如果在一个有大量数据的表中引入新的索引,数据库可能需要一定的时间来建立索引。
- 自动转换varchar字段类型到text字段类型,自动警告其它字段类型在模型和数据库之间不一致的情况。
- 自动警告字段的默认值,是否为空信息在模型和数据库之间不匹配的情况
err := engine.Sync2(new(User), new(Group))
导入导出sql脚本
导入:
如果你需要将保存在文件或者其它存储设施中的SQL脚本执行,那么可以调用
engine.Import(r io.Reader)
和
engine.ImportFile(fpath string)
导出:
如果需要在程序中Dump数据库的结构和数据可以调用
engine.DumpAll(w io.Writer)
和
engine.DumpAllFile(fpath string)
DumpAll方法接收一个io.Writer接口来保存Dump出的数据库结构和数据的SQL语句,这个方法导出的SQL语句并不能通用。只针对当前engine所对应的数据库支持的SQL。
插入数据
使用xorm api来插入数据
使用Insert 或InsertOne 来插入数据
user := new(User)
user.Name = "myname"
affected, err := engine.Insert(user)
- 批量插入会自动生成
Insert into table values (),(),() 的语句,因此各个数据库对SQL语句有长度限制,因此这样的语句有一个最大的记录数,根据经验测算在150条左右。大于150条后,生成的sql语句将太长可能导致执行失败。因此在插入大量数据时,目前需要自行分割成每150条插入一次。
使用SQL命令来插入数据
第1种方式
sql ="insert into config(key,value) values (?, ?)"
res, err := engine.Exec(sql, "OSCHINA", "OSCHINA")
第2种方式
sql_2 := "insert into config(key,value) values (?, ?)"
affected, err := engine.Sql(sql_4, "OSCHINA", "OSCHINA").Execute()
第3种方式
sql_i_1 := "sql_i_1"
affected, err := engine.SqlMapClient(sql_i_1, "config_1", "1").Execute()
sql_i_2 := "sql_i_2"
paramMap_i := map[string]interface{}{"key": "config_2", "value": "2"}
affected, err := engine.SqlMapClient(sql_i_2, ¶mMap_i).Execute()
第4种方式
sql_i_3 := "insert.example.stpl"
paramMap_i_t := map[string]interface{}{"key": "config_3", "value": "3"}
affected, err := engine.SqlTemplateClient(sql_i_3, ¶mMap_i_t).Execute()
创建时间Created
我感觉这个特性很有意思。它可以让你在数据插入到数据库的时候自动将对应的字段设置为当前的时间,需要在xorm标记中使用created标记。 字段可以是time.Time 、int 、int64 、int32 等int类型。
只需要在后面加上xorm的映射即可,当添加记录的时候,created标记的字段就会被自动更新为当前时间。
type User struct {
Id int64
Name string
CreatedAt time.Time `xorm:"created"`
}
查询和统计数据
ORM方式查询和统计数据
ORM所有的查询条件不区分调用顺序,但必须在调用Get,Find,Count,Iterate,Rows 之前调用。
查询单条语句用Get ,查询多条语句用Find
Rows 和Iterate 方法可以把查询的结果逐条的展示出来,Rows更加灵活。
更新数据
update方法
使用如下的方式来更新
user := new(User)
user.Name = "myname"
affected, err := engine.Id(id).Update(user)
乐观锁
可以使用如下方式添加乐观锁,在insert的时候,version标记的字段将会被设置为1。
type User struct {
Id int64 Name string
Version int xorm:"version"
}
在update的时候,update结构体中的内容必须包含原version的值
var user User
engine.Id(1).Get(&user)
engine.Id(1).Update(&user)
更新时间
可以通过updated标记,当调用Insert(), InsertOne(), Update()方法的时候,会自动的将对应自动设置为当前时间,对应的字段可以为time.Time或者自定义的time.Time或者int,int64等int类型。
type User struct {
Id int64
Name string
UpdatedAt time.Time `xorm:"updated"`
}
通过sql命令更新数据
第1种方式
sql ="update user set age = ? where name = ?"
res, err := engine.Exec(sql, 1, "xorm")
第2种方式
sql_2 := "update user set age = ? where name = ?"
affected, err := engine.Sql(sql_2, 1, "xorm").Execute()
第3种方式
sql_i_1 := "sql_i_1"
affected, err := engine.SqlMapClient(sql_i_1, 1, "xorm").Execute()
sql_i_2 := "sql_i_2"
paramMap_i := map[string]interface{}{"age": 1, "name": "xorm"}
affected, err := engine.SqlMapClient(sql_i_2, ¶mMap_i).Execute()
第4种方式
sql_i_3 := "insert.example.stpl"
paramMap_i_t := map[string]interface{}{"age": 1, "name": "xorm"}
affected, err := engine.SqlTemplateClient(sql_i_3, ¶mMap_i_t).Execute()
删除数据
Delete方法
第一个参数为删除的记录数,第二个为执行错误
user := new(User)
affected, err := engine.Id(id).Delete(user)
注意:当删除时,如果user中包含有bool,float64或者float32类型,有可能会使删除失败。具体请查看 FAQ
软删除 Deleted
在我以前的使用中,软删除都是要自己去做的,但是XORM通过标记给我们做好了。
type User struct {
Id int64
Name string
DeletedAt time.Time `xorm:"deleted"`
affected, err := engine.Id(id).Cols("age").Update(&user)
}
通过sql语句删除数据
第1种方式
sql ="delete from user where id = ?"
res, err := engine.Exec(sql, 1)
第2种方式
sql_2 := "delete from user where id = ?"
affected, err := engine.Sql(sql_2, 1).Execute()
第3种方式
sql_i_1 := "sql_i_1"
affected, err := engine.SqlMapClient(sql_i_1, 1).Execute()
sql_i_2 := "sql_i_2"
paramMap_i := map[string]interface{}{"id": 1}
affected, err := engine.SqlMapClient(sql_i_2, ¶mMap_i).Execute()
第4种方式
sql_i_3 := "insert.example.stpl"
paramMap_i_t := map[string]interface{}{"id": 1}
affected, err := engine.SqlTemplateClient(sql_i_3, ¶mMap_i_t).Execute()
事务
简单事务使用
session := engine.NewSession()
defer session.Close()
err := session.Begin()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
_, err = session.Insert(&user1)
if err != nil {
session.Rollback()
return
}
user2 := Userinfo{Username: "yyy"}
_, err = session.Where("id = ?", 2).Update(&user2)
if err != nil {
session.Rollback()
return
}
_, err = session.Exec("delete from userinfo where username = ?", user2.Username)
if err != nil {
session.Rollback()
return
}
err = session.Commit()
if err != nil {
return
}
嵌套事务使用
在通常情况下,简单事务够我们使用了。 它与简单事务的不同在于,简单事务在同一个session 下工作,而嵌套事物则会返回Transaction实例,后续操作则在同一个实例下操作。
session := engine.NewSession()
defer session.Close()
tx, err := session.BeginTrans()
if err != nil {
return
}
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
_, err = tx.Session().Insert(&user1)
if err != nil {
tx.Rollback()
return
}
user2 := Userinfo{Username: "yyy"}
_, err = tx.Session().Where("id = ?", 2).Update(&user2)
if err != nil {
tx.RollbackTrans()
return
}
_, err = tx.Session().Exec("delete from userinfo where username = ?", user2.Username)
if err != nil {
tx.RollbackTrans()
return
}
_, err = tx.Session().SqlMapClient("delete.userinfo", user2.Username).Execute()
if err != nil {
tx.RollbackTrans()
return
}
err = tx.CommitTrans()
if err != nil {
...
return
}
数据导出
查询结果集导出csv、tsv、xml、json、xlsx、yaml、html
xorm查询结果集支持导出csv、tsv、xml、json、xlsx、yaml、html七种文件格式
以导出xlsx文件格式为例,代码如下
err := engine.Sql("select * from category").Query().SaveAsXLSX("1.xlsx", []string{"id", "name", "counts", "orders", "createtime", "pid", "lastupdatetime", "status"}, 0777)
if err != nil {
t.Fatal(err)
}
- SaveAsCSV(filename string, headers []string, perm os.FileMode)
导出CSV文件,filename为完整路径,headers为每列的列名,需要结果集中有该字段,此处[]string是为了确定列的顺序,perm为文件权限位
- SaveAsTSV(filename string, headers []string, perm os.FileMode)
导出TSV文件,filename为完整路径,headers为每列的列名,需要结果集中有该字段,此处[]string是为了确定列的顺序,perm为文件权限位
- SaveAsHTML(filename string, headers []string, perm os.FileMode)
导出HTML文件,filename为完整路径,headers为每列的列名,需要结果集中有该字段,此处[]string是为了确定列的顺序,perm为文件权限位
- SaveAsXML(filename string, headers []string, perm os.FileMode)
导出XML文件,filename为完整路径,headers为每列的列名,需要结果集中有该字段,此处[]string是为了确定列的顺序,perm为文件权限位
- SaveAsXMLWithTagNamePrefixIndent(tagName string, prifix string, indent string, filename string, headers []string, perm os.FileMode)
导出指定格式化的XML文件,filename为完整路径,headers为每列的列名,需要结果集中有该字段,此处[]string是为了确定列的顺序,perm为文件权限位
- SaveAsYAML(filename string, headers []string, perm os.FileMode)
导出YAML文件,filename为完整路径,headers为每列的列名,需要结果集中有该字段,此处[]string是为了确定列的顺序,perm为文件权限位
- SaveAsJSON(filename string, headers []string, perm os.FileMode)
导出JSON文件,filename为完整路径,headers为每列的列名,需要结果集中有该字段,此处[]string是为了确定列的顺序,perm为文件权限位
- SaveAsXLSX(filename string, headers []string, perm os.FileMode)
导出XLSX文件,filename为完整路径,headers为每列的列名,需要结果集中有该字段,此处[]string是为了确定列的顺序,perm为文件权限位
多查询集导出到单一文件
样例代码:
_, results, err := engine.Sqls(map[string]string{"category": "select * from category", "category-16-17": "select * from category where id in (16,17)"}).Execute()
if err != nil {
t.Fatal(err)
}
databook, err := xorm.NewDatabookWithData(
map[string]string{
"category": "category",
"category-16-17": "category-16-17"},
results,
true,
map[string][]string{
"category": []string{"id", "name", "counts", "orders", "createtime", "pid", "lastupdatetime", "status"},
"category-16-17": []string{"id", "name", "counts", "orders", "createtime", "pid", "lastupdatetime", "status"}})
if err != nil {
t.Fatal(err)
}
err = databook.SaveAsXLSX("c:/2.xlsx", 0777)
if err != nil {
t.Fatal(err)
}
err = databook.SaveAsHTML("c:/2.html", 0777)
if err != nil {
t.Fatal(err)
}
err = databook.SaveAsJSON("c:/2.json", 0777)
if err != nil {
t.Fatal(err)
}
err = databook.SaveAsXML("c:/2.xml", 0777)
if err != nil {
t.Fatal(err)
}
err = databook.SaveAsYAML("c:/2.yaml", 0777)
if err != nil {
t.Fatal(err)
}
连接池
engine内部支持连接池接口和对应的函数。
- 如果需要设置连接池的空闲数大小,可以使用engine.SetMaxIdleConns()来实现。
- 如果需要设置最大打开连接数,则可以使用engine.SetMaxOpenConns()来实现。
参考资料
欢迎评论区讨论,或指出问题。 如果觉得写的不错,欢迎点赞,转发,收藏。
|