xorm 使用

xorm 使用

Posted by 锐玩道 on May 21, 2021

如果❤️我的文章有帮助,欢迎点赞、关注。这是对我继续技术创作最大的鼓励。更多往期文章在我的个人博客

本文介绍xorm。xorm是一个简单但强大的 Go 语言 ORM 库,使用它可以大大简化我们的数据库操作。

安装驱动

先安装:

$ go get xorm.io/xorm

如何还没有安装 MySQL 数据库驱动,需要执行以下命令安装对应的驱动:

$ go get github.com/go-sql-driver/mysql

连接数据库

使用xorm来操作数据库,首先需要使用xorm.NewEngine()创建一个引擎。该方法的参数与sql.Open()参数相同。

package main

import (
  "log"
  "time"

  _ "github.com/go-sql-driver/mysql"
  "xorm.io/xorm"
)

func main() {
  engine, err := xorm.NewEngine("mysql", "root:@/dianping?charset=utf8")

  if err != nil {
    log.Fatal(err)
  }
}

同步数据库表结构

下面代码中,我们演示了xorm的一个非常实用的功能,将数据库中的表与对应 Go 代码中的结构体做同步。 初始状态下,数据库dianping中没有表student,调用Sync2()方法会根据Student的结构自动创建一个student表。

如果表student已经存在,Sync()方法会对比User结构与表结构的不同.

type Student struct {
	Id      int64
	Name    string
	Salt    string
	Age     int
	Passwd  string    `xorm:"varchar(200)"`
	Created time.Time `xorm:"created"`
	Updated time.Time `xorm:"updated"`
}

func main() {
	engine, err := xorm.NewEngine("mysql", "root:@/dianping?charset=utf8")

	if err != nil {
		log.Fatal(err)
	}

	err = engine.Sync(new(Student))
	if err != nil {
		log.Fatal(err)
	}
}

此修改只限于添加字段。 删除表中已有的字段会带来比较大的风险。如果我们User结构的Salt字段删除,然后执行程序。出现下面错误:

[xorm] [warn]  2021/03/29 22:44:38.528784 Table student has column salt but struct has not related field

查询

Get()

Get()方法用于查询单条数据,并使用返回的字段为传入的对象赋值:

func main() {
  engine, err := xorm.NewEngine("mysql", "root:@/dianping?charset=utf8")

	user1 := &Student{}
	has, _ := engine.ID(1).Get(user1)
	if has {
		fmt.Printf("user1:%v\n", user1)
	}

	user2 := &Student{}
	has, _ = engine.Where("name=?", "JJ").Get(user2)
	if has {
		fmt.Printf("user2:%v\n", user2)
	}

	user3 := &Student{Id: 5}
	has, _ = engine.Get(user3)
	if has {
		fmt.Printf("user3:%v\n", user3)
	}

	user4 := &Student{Name: "Alex"}
	has, _ = engine.Get(user4)
	if has {
		fmt.Printf("user4:%v\n", user4)
	}
}

上面演示了 3 种使用Get()的方式:

使用主键:engine.ID(1)查询主键(即id)为 1 的用户; 使用条件语句:engine.Where(“name=?”, “dj”)查询name = “dj”的用户; 使用对象中的非空字段:user3设置了Id字段为 5,engine.Get(user3)查询id = 5的用户;user4设置了字段Name为”pipi”,engine.Get(user4)查询name = “pipi”的用户。 运行程序:

user1:&{1 CJ salt 18 123 2021-03-29 08:35:49 +0800 CST 2021-03-29 08:35:49 +0800 CST}
user3:&{5 cmy.K salt 15 123 2021-03-29 08:35:49 +0800 CST 2021-03-29 08:35:49 +0800 CST}
user4:&{6 Alex salt 19 123 2021-03-29 08:35:49 +0800 CST 2021-03-29 08:35:49 +0800 CST}

Process finished with exit code 0

查询条件的使用不区分调用顺序,但是必须在Get()方法之前调用。 实际上后面介绍的查询&统计方法也是如此,可以在调用实际的方法前添加一些过滤条件。 除此之外xorm支持只返回指定的列xorm.Cols()或忽略特定的列xorm.Omit()

另外,为了便于排查可能出现的问题,xorm提供了ShowSQL()方法设置将执行的 SQL 同时在控制台中输出:

[xorm] [info]  2021/03/30 08:41:01.558169 [SQL] SELECT `id`, `name`, `salt`, `age`, `passwd`, `created`, `updated` FROM `student` WHERE `id`=? LIMIT 1 [1] - 1.1464ms
user1:&{1 CJ salt 18 123 2021-03-29 08:35:49 +0800 CST 2021-03-29 08:35:49 +0800 CST}

有时候,调试信息都输出到控制台并不利于我们查询,xorm可以设置日志选项,将日志输出到文件中:

func main() {
	engine, err := xorm.NewEngine("mysql", "root:@/dianping?charset=utf8")

	f, err := os.Create("sql.log")
	if err != nil {
		panic(err)
	}

	engine.SetLogger(log2.NewSimpleLogger(f))
	engine.Logger().SetLevel(log2.LOG_DEBUG)
	engine.ShowSQL(true)

	user1 := &Student{}
	has, _ := engine.ID(1).Get(user1)
	if has {
		fmt.Printf("user1:%v\n", user1)
	}
}

Iterate()

Iterate() 找到满足条件的所有记录,只不过传入了一个回调去处理每条记录:

    engine.Where("age > ? and age < ?", 12, 30).Iterate(&User{}, func(i int, bean interface{}) error {
      fmt.Printf("user%d:%v\n", i, bean.(*User))
      return nil  // 如果回调返回一个非nil的错误,后面的记录就不会再处理了。
    })

Rows()

Rows()方法与Iterate()类似,不过返回一个Rows对象由我们自己迭代,更加灵活

    rows, _ := engine.Where("age > ? and age < ?", 12, 30).Rows(&User{})
    defer rows.Close()
    
    u := &User{}
    for rows.Next() {
      rows.Scan(u)
      
      fmt.Println(u)
    }

还有更多的 像

  • Get() 方法只能返回单条记录
  • Exist() 方法查询符合条件的记录是否存在
  • Find() 方法返回所有符合条件的记录

    mapUsers := make(map[int64]User) engine.Where(“length(name) = ?”, 3).Find(&mapUsers)

  • Sum() 方法返回某一字段求和 engine.Sum(&Student{}, "total")
  • Count()方法统计满足条件的记录数量 engine.Where("age >= ?", 50).Count(&Student{})

插入数据

使用engine.Insert()方法,可以插入单条数据,也可以批量插入多条数据:

    engine, err := xorm.NewEngine("mysql", "root:@/dianping?charset=utf8")
    user := &Student{Name: "lzy", Age: 50}
    
    affected, _ := engine.Insert(user)

更新数据

更新通过engine.Update()实现,可以传入结构指针或map[string]interface{}。对于传入结构体指针的情况,xorm只会更新非空的字段。 如果一定要更新空字段,需要使用Cols()方法显示指定更新的列。使用Cols()方法指定列后,即使字段为空也会更新:

    engine, err := xorm.NewEngine("mysql", "root:@/dianping?charset=utf8")
    engine.ID(1).Update(&User{Name: "ldj"})
    engine.ID(1).Cols("name", "age").Update(&User{Name: "dj"})
    
    engine.Table(&User{}).ID(1).Update(map[string]interface{}{"age": 18})

删除数据

直接调用engine.Delete()删除符合条件的记录,返回删除的条目数量:

    engine, err := xorm.NewEngine("mysql", "root:@/dianping?charset=utf8")
    
    affected, _ := engine.Where("name = ?", "lzy").Delete(&User{})
    fmt.Printf("%d records deleted", affected)

执行原始SQL

    engine, err := xorm.NewEngine("mysql", "root:@/dianping?charset=utf8")
    
    querySql := "select * from user limit 1"
    reuslts, _ := engine.Query(querySql)
    for _, record := range reuslts {
    for key, val := range record {
      fmt.Println(key, string(val))
    }
    }
    
    updateSql := "update `user` set name=? where id=?"
    res, _ := engine.Exec(updateSql, "ldj", 1)
    fmt.Println(res.RowsAffected())

本文对xorm做了一个简单的介绍,xorm的特性远不止于此。感兴趣可在文档与Github中自行探索。

参考

  • xorm GitHub:https://github.com/go-xorm/xorm
  • xorm 手册:http://gobook.io/read/gitea.com/xorm/manual-zh-CN/