Go之XORM操作MySQL

作者: 太阳上的雨天 分类: Go 发布时间: 2022-02-24 11:21

XORM

xorm 一个简单强大的Go语言ORM库。通过它让操作数据库变得更加简单。

[XORM操作指南]

XORM关系映射,只是Go操作数据库的其中之一。比如还有GORM。至于两者的区别,一搜一大堆,不再介绍。

这里只介绍一下XORM的基本使用。

导入包

go get -u github.com/go-sql-driver/mysql
go get -u github.com/xormplus/xorm

定义结构体

名称映射规则

  • 指责

    结构体名称 —– 表名

    结构体字段 —— 表字段

结构体字段属性 —– 对象的表子段属性

go type’s kind value method xorm type
implemented Conversion Conversion.ToDB / Conversion.FromDB Text
int, int8, int16, int32, uint, uint8, uint16, uint32 Int
int64, uint64 BigInt
float32 Float
float64 Double
complex64, complex128 json.Marshal / json.UnMarshal Varchar(64)
[]uint8 Blob
array, slice, map except []uint8 json.Marshal / json.UnMarshal Text
string Varchar(255)
time.Time DateTime
cascade struct primary key field value BigInt
struct json.Marshal / json.UnMarshal Text
Others Text
bool 1 or 0 Bool

前缀映射、后缀映射、缓存映射

通过 core.NewPrefixMapper(core.SnakeMapper{}, "prefix") 可以创建一个在SnakeMapper的基础上在命名中添加统一的前缀

例如,如果希望所有的表名都在结构体自动命名的基础上加一个前缀而字段名不加前缀,则可以在engine创建完成后执行以下语句:

tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, "pre_")
engine.SetTableMapper(tbMapper)

执行之后,结构体 type User struct 默认对应的表名就变成了 pre_user 了,而之前默认的是 user

  • 通过 core.NewSufffixMapper(core.SnakeMapper{}, "suffix") 可以创建一个在SnakeMapper的基础上在命名中添加统一的后缀
  • 通过 core.NewCacheMapper(core.SnakeMapper{}) 可以创建一个组合了其它的映射规则,起到在内存中缓存曾经映射过的命名映射

创建xorm引擎

在xorm里面,可以同时存在多个Orm引擎,一个Orm引擎称为Engine,一个Engine一般只对应一个数据库

db/db.go

package db

import (
    "fmt"

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

var engine *xorm.Engine

func Init() *xorm.Engine {
    engine, err := xorm.NewEngine("mysql", "root:root@/test?charset=utf8mb4")
    if err != nil {
        panic(err)
    }

    err = engine.Ping()
    if err != nil {
        fmt.Printf("connect ping failed: %v", err)
    }

    engine.ShowSQL(true)

    return engine
}

写入数据库

先创建两张表,表比较简单,只是做演示使用

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) NOT NULL,
  `password` varchar(100) NOT NULL,
  `email` varchar(100) NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  `deleted_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

CREATE TABLE `address` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `addr` varchar(100) DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  `deleted_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ORM方式写入

同一张表的操作

  • 写入一条数据

    type Users struct {
    Username  string        xorm:"username"
    Password  string        xorm:"password""
    Email     string           xorm:"email"
    CreatedAt time.Time   xorm:"created"
    UpdatedAt time.Time  xorm:"updated"
    DeletedAt time.Time   xorm:"deleted"
    }
    
    func main() {
    
    engine := db.Init()
    
    u := &Users{}
    u.Username = "test"
    u.Password = "123456"
    u.Email = "test@sina.com"
    
    affected, err := engine.Insert(u)
    if err != nil {
        log.Fatal(fmt.Printf("insert into failed, err: %v", err))
    }
    
    fmt.Println(affected)
    }
    
  • 批量写入

    1. 使用slice
    func main() {
    
        engine := db.Init()
    
        u := make([]Users, 2)
        u[0].Username = "test2"
        u[0].Password = "123456"
        u[0].Email = "test2@sina.com"
    
        u[1].Username = "test3"
        u[1].Password = "123456"
        u[1].Email = "test3@sina.com"
    
        affected, err := engine.Insert(u)
        if err != nil {
            log.Fatal(fmt.Printf("insert into failed, err: %v", err))
        }
    
        fmt.Println(affected)
    }
    1. 使用slice指针批量写入
    func main() {
    
        engine := db.Init()
    
        u := make([]*Users, 2)
        u[0] = &Users{}
        u[0].Username = "test4"
        u[0].Password = "123456"
        u[0].Email = "test4@sina.com"
    
        u[1] = &Users{}
        u[1].Username = "test5"
        u[1].Password = "123456"
        u[1].Email = "test5@sina.com"
    
        affected, err := engine.Insert(u)
        if err != nil {
            log.Fatal(fmt.Printf("insert into failed, err: %v", err))
        }
    
        fmt.Println(affected)
    }

    多张表的操作

    • 写入一条记录

      type Users struct {
      Username  string    xorm:"username"
      Password  string    xorm:"password""
      Email     string    xorm:"email"
      CreatedAt time.Time xorm:"created"
      UpdatedAt time.Time xorm:"updated"
      DeletedAt time.Time xorm:"deleted"
      }
      
      type Address struct {
      Addr      string    xorm:"Addr"
      CreatedAt time.Time xorm:"created"
      UpdatedAt time.Time xorm:"updated"
      DeletedAt time.Time xorm:"deleted"
      }
      
      func main() {
      
      engine := db.Init()
      
      u := &Users{}
      u.Username = "test4"
      u.Password = "123456"
      u.Email = "test4@sina.com"
      
      a := &Address{}
      a.Addr = "杭州"
      
      affected, err := engine.Insert(u, a)
      if err != nil {
          log.Fatal(fmt.Printf("insert into failed, err: %v", err))
      }
      
      fmt.Println(affected)
      }
    • 批量写入

      func main() {
      
      engine := db.Init()
      
      u := make([]*Users, 2)
      u[0] = &Users{}
      u[0].Username = "test6"
      u[0].Password = "123456"
      u[0].Email = "test4@sina.com"
      
      u[1] = &Users{}
      u[1].Username = "test7"
      u[1].Password = "123456"
      u[1].Email = "test5@sina.com"
      
      a := make([]*Address, 2)
      a[0] = &Address{}
      a[0].Addr = "杭州"
      
      a[1] = &Address{}
      a[1].Addr = "上海"
      
      affected, err := engine.Insert(u, a)
      if err != nil {
          log.Fatal(fmt.Printf("insert into failed, err: %v", err))
      }
      
      fmt.Println(affected)
      }

      Note: 这里的多表写入并没有使用事务。如果部分成功,部分失败不支持回滚

执行原生sql写入数据

func main() {

    engine := db.Init()

    sql := "INSERT INTO users(username, password, email) values (?, ?, ?)"
    res, err := engine.Exec(sql, "original", "123", "123@sina.com")

    if err != nil {
        log.Fatal(fmt.Printf("insert into failed, err: %v", err))
    }

    fmt.Println(res)
}

Note: 执行原生sql写入数据,created_at、updated_at时间的值不写的则为空。

原生sql写入还有其余三种方式,这里不再赘述

删除数据

ORM方式删除 – 软删除

在Delete()时,deleted标记的字段将会被自动更新为当前时间而不是去删除该条记录

func main() {

    engine := db.Init()

    var u = &Users{}
    u.Username = "test1"
    affected, err := engine.Delete(u)
    if err != nil {
        log.Fatal(fmt.Printf("deleter into failed, err: %v", err))
    }

    fmt.Println(affected)
}

执行原生sql删除 – 物理删除

func main() {

    engine := db.Init()

    sql := "DELETE FROM users where id = ?"
    affected, err := engine.Exec(sql, 1)
    if err != nil {
        log.Fatal(fmt.Printf("deleter into failed, err: %v", err))
    }

    fmt.Println(affected)
}

Note: 也可以使用原生sql update更新deleted_at时间为当前时间戳,实现软删除。

更新数据

  • update方式

    更新数据使用engine.Update方法,update的参数可以是一个结构体指针或者一个Map[string]interface{}类型。

    1. 当传入的为结构体指针时,只有非空和0的field才会被作为更新的字段。如果非要更新空字段,需要使用Cols方法显示指定更新的列
    2. 当传入的为Map类型时,key为数据库Column的名字,value为要更新的内容。且需要使用engine.Table方法指定表名
func main() {

    engine := db.Init()

    engine.ID(13).Update(&Users{Username: "test"})
    engine.ID(13).Cols("username", "email").Update(&Users{Username: "test2"}) // 会更新username和email两个子段,email为空

    // map类型
    affected, err := engine.Table(&Users{}).ID(13).Update(map[string]interface{}{
        "username": "update_original",
    })
    if err != nil {
        log.Fatal(fmt.Printf("update username failed, err: %v", err))
    }

    fmt.Println(affected)
}
  • 执行原生sql更新数据

    func main() {
    
    engine := db.Init()
    
    sql := "UPDATE users SET username = ?, updated_at = ? WHERE id = ?"
    res, err := engine.Exec(sql, "aaa", time.Now().Format("2006-01-02 15:04:05"), 13)
    
    if err != nil {
        log.Fatal(fmt.Printf("update username failed, err: %v", err))
    }
    
    fmt.Println(res)
    }

查询数据

ORM

  1. 查询一条数据 – GET方法

    func main() {
    
    engine := db.Init()
    
    // SELECT * FROM user LIMIT 1
    user1 := &Users{}
    has, _ := engine.ID(1).Get(user1)
    if has {
        fmt.Printf("user1:%v\n", user1)
    }
    
    // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
    user2 := &Users{}
    has, _ = engine.Where("username = ?", "aaa").Desc("id").Get(user2)
    if has {
        fmt.Printf("user1:%v\n", user1)
    }
    }
    
  2. 查询多条数据 – Find 方法

Find()需要传入对象切片的指针或 map 的指针

func main() {

    engine := db.Init()

    slicUsers := make([]Users, 0)
    _ = engine.Find(&slicUsers)
    fmt.Println(slicUsers)

    mapUsers := make([]Users, 0)
    engine.Where("username = ?", "aaa").Find(&mapUsers)
    fmt.Println(mapUsers)
}
  1. Iterate 效果与Find方法一样,对了一个回调函数处理每条记录

    func main() {
    
    engine := db.Init()
    
    engine.Where("username = ?", "aaa").Iterate(new(Users), func(i int, bean interface{}) error {
        users := bean.(*Users)
        fmt.Println(users)
        return nil
    })
    }
  2. Count 统计满足条件的数量,参数为struct指针

    func main() {
    
    engine := db.Init()
    
    count, _ := engine.Where("length(username) > ?", 3).Count(&Users{})
    fmt.Println(count)
    }
  3. Rows方法 和 Iterate方法类似。

    func main() {
    
    engine := db.Init()
    
    u := &Users{}
    rows, _ := engine.Where("id > ?", 5).Rows(u)
    
    defer rows.Close()
    
    for rows.Next() {
        rows.Scan(u)
        fmt.Println(u)
    }
    }

原生sql查询

写sql语句,然后执行即可。和更新删除类似,不再赘述。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注