Xorm学习笔记


与你相识


博主介绍:

– 本人是普通大学生一枚,每天钻研计算机技能,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()可以获取到数据库中所有的表,字段,索引的信息。

表操作

  • CreateTables()

创建表使用engine.CreateTables(),参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine(),如果对应的数据库支持,这两个方法可以在创建表时指定表的字符编码和使用的引擎。Charset()和StoreEngine()当前仅支持Mysql数据库。

  • IsTableEmpty()

判断表是否为空,参数和CreateTables相同

  • IsTableExist()

判断表是否存在

  • DropTables()

删除表使用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来插入数据

使用InsertInsertOne来插入数据

user := new(User)
user.Name = "myname"
affected, err := engine.Insert(user)
// INSERT INTO user (name) values (?)
  • 批量插入会自动生成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种方式
//SqlMap中key为 "sql_i_1" 配置的Sql语句为:insert into config(key,value) values (?, ?)
sql_i_1 := "sql_i_1" 
affected, err := engine.SqlMapClient(sql_i_1, "config_1", "1").Execute()

//SqlMap中key为 "sql_i_2" 配置的Sql语句为:insert into config(key,value) values (?key, ?value)
sql_i_2 := "sql_i_2" 
paramMap_i := map[string]interface{}{"key": "config_2", "value": "2"}
affected, err := engine.SqlMapClient(sql_i_2, &paramMap_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, &paramMap_i_t).Execute()

创建时间Created

我感觉这个特性很有意思。它可以让你在数据插入到数据库的时候自动将对应的字段设置为当前的时间,需要在xorm标记中使用created标记。 字段可以是time.Timeintint64int32等int类型。

只需要在后面加上xorm的映射即可,当添加记录的时候,created标记的字段就会被自动更新为当前时间。

type User struct {
    Id int64
    Name string
    CreatedAt time.Time `xorm:"created"`
}

查询和统计数据

ORM方式查询和统计数据

ORM所有的查询条件不区分调用顺序,但必须在调用Get,Find,Count,Iterate,Rows之前调用。

查询单条语句用Get,查询多条语句用Find

RowsIterate方法可以把查询的结果逐条的展示出来,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)
// SELECT * FROM user WHERE id = ?
engine.Id(1).Update(&user)
// UPDATE user SET ..., version = version + 1 WHERE id = ? AND version = ?

更新时间

可以通过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种方式
//SqlMap中key为 "sql_i_1" 配置的Sql语句为:update user set age = ? where name = ?
sql_i_1 := "sql_i_1" 
affected, err := engine.SqlMapClient(sql_i_1, 1, "xorm").Execute()

//SqlMap中key为 "sql_i_2" 配置的Sql语句为:update user set age = ?age where name = ?name
sql_i_2 := "sql_i_2" 
paramMap_i := map[string]interface{}{"age": 1, "name": "xorm"}
affected, err := engine.SqlMapClient(sql_i_2, &paramMap_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, &paramMap_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"`
    // 通过Cols来指定只更新某列
    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种方式
//SqlMap中key为 "sql_i_1" 配置的Sql语句为:delete from user where id = ?
sql_i_1 := "sql_i_1" 
affected, err := engine.SqlMapClient(sql_i_1, 1).Execute()

//SqlMap中key为 "sql_i_2" 配置的Sql语句为:delete from user where id = ?id
sql_i_2 := "sql_i_2" 
paramMap_i := map[string]interface{}{"id": 1}
affected, err := engine.SqlMapClient(sql_i_2, &paramMap_i).Execute()
第4种方式
sql_i_3 := "insert.example.stpl"
paramMap_i_t := map[string]interface{}{"id": 1}
affected, err := engine.SqlTemplateClient(sql_i_3, &paramMap_i_t).Execute()

事务

简单事务使用

session := engine.NewSession()
defer session.Close()
// add Begin() before any action
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
}

// add Commit() after all actions
err = session.Commit()
if err != nil {
    return
}

嵌套事务使用

在通常情况下,简单事务够我们使用了。 它与简单事务的不同在于,简单事务在同一个session下工作,而嵌套事物则会返回Transaction实例,后续操作则在同一个实例下操作。

session := engine.NewSession()
defer session.Close()
// add BeginTrans() before any action
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
}

// add CommitTrans() after all actions
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()来实现。

参考资料


欢迎评论区讨论,或指出问题。 如果觉得写的不错,欢迎点赞,转发,收藏。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jacob_云飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值