Go语言数据库框架——xorm

XORM

1 概念

1.1 ORM

ORM 是 Object Relational Mapping 的缩写,译为“对象关系映射”框架。

ORM 框架是一种数据持久化技术,是一种为了解决面向对象与关系型数据库中数据类型不匹配的技术,它通过描述对象与数据库表之间的映射关系,自动将应用程序中的对象持久化到关系型数据库的表中。

ORM映射关系

Gorm和Xorm都是Go语言的ORM库,其他相关的数据库框架库参考:https://my.oschina.net/u/168737/blog/1531834

执行一下两条命令  安装mysql引擎 以及 安装xorm库
go get -u github.com/go-sql-driver/mysql
go get github.com/go-xorm/xorm

1.2 Mysql

下载安装:https://cloud.tencent.com/developer/article/1636375

启动/停止服务:net start/stop mysql

登录:mysql -u root -p密码

列出数据库:show databases,退出数据库:quit

1.3 Mysql命令大全

Mysql常用命令大全:https://www.cnblogs.com/bluecobra/archive/2012/01/11/2318922.html

CREATE DATABASE IF NOT EXISTS golang;
USE golang;
DROP TABLE IF EXISTS user;

CREATE TABLE user (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(30) NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO classes(id, name) VALUES (1, 'sommer');
INSERT INTO classes(id, name) VALUES (2, 'Jack');
INSERT INTO classes(id, name) VALUES (3, 'hapu');
INSERT INTO classes(id, name) VALUES (4, 'Dim');

select * from user;

2 XORM 的基本使用

参考资源:

2.1 下载xorm

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

2.2 安装驱动

go get github.com/go-sql-driver/mysql  //Mysql
go get github.com/ziutek/mymysql/godrv  //MyMysql
go get github.com/lib/pq  //Postgres
go get github.com/mattn/go-sqlite3  //SQLite
go get github.com/denisenkom/go-mssqldb  //MSSQL

2.3 xorm引擎

Engine 引擎用于对单个数据库进行操作

Engine Group 引擎用于对读写分离的数据库或者负载均衡的数据库进行操作

2.3.1 创建引擎
import (
    _ "github.com/go-sql-driver/mysql"
    "xorm.io/xorm"
)

var engine *xorm.Engine

func main() {
    var err error
    /**
     配置连接数据库信息格式:
         用户名:密码@(数据库服务器地址:端口)/数据库名称?charset=字符集
   */
    engine, err = xorm.NewEngine("mysql", "root:root@(127.0.0.1)/golang?charset=utf8")
    engine.Ping() //连接测试
    defer engine.Close() //延迟关闭数据库
}
2.3.2 打印日志
engine.ShowSQL(true)//在控制台打印出生成的SQL语句;
engine.Logger().SetLevel(core.LOG_DEBUG)//在控制台打印调试及以上的信息;
--------------------------------------------------------------------
// 将日志保存为文件
f, err := os.Create("sql.log")
if err != nil {
    println(err.Error())
    return
}
engine.SetLogger(xorm.NewSimpleLogger(f))
2.3.3 连接池
如果需要设置连接池的空闲数大小,使用 engine.SetMaxIdleConns() 来实现。
如果需要设置最大打开连接数,则使用 engine.SetMaxOpenConns() 来实现。
如果需要设置连接的最大生存时间,则使用 engine.SetConnMaxLifetime() 来实现。
2.3.4 名称映射规则

xorm 内置了三种 Mapper 实现:names.SnakeMappernames.SameMappernames.GonicMapper

SnakeMapper 支持struct为驼峰式命名,表结构为下划线命名之间的转换,这个是默认的Maper; // user_table——UserTable,user_id——UserId
SameMapper 支持结构体名称和对应的表名称以及结构体field名称与对应的表字段名称相同的命名;
GonicMapper 和SnakeMapper很类似,但是对于特定词支持更好,比如ID会翻译成id而不是i_d。
engine.SetMapper(names.GonicMapper{})
// 给表加前缀
tbMapper := names.NewPrefixMapper(names.SnakeMapper{}, "prefix_")
engine.SetTableMapper(tbMapper)
// 加后缀
names.NewSuffixMapper(names.SnakeMapper{}, "suffix")

表名的优先级顺序如下:

engine.Table() 指定的临时表名优先级最高
TableName() string 其次
Mapper 自动映射的表名优先级最后

字段名的优先级顺序如下:

结构体tag指定的字段名优先级较高  xorm:"'column_name'"
Mapper 自动映射的表名优先级较低
type User struct {
   Id   int    //`xorm:"INT 'id'"`
   Name string //`xorm:"VARCHAR(30) 'name'"`
}
2.3.5 数据类型对应规则
Go中的Type类型XORM映射的Type类型
implemented ConversionText
int, int8, int16, int32, uint, uint8, uint16, uint32Int
int64, uint64BigInt
float32Float
float64Double
complex64, complex128Varchar(64)
[]uint8Blob
array, slice, map except []uint8Text
boolBool
stringVarchar(255)
time.TimeDateTime
cascade structBigInt
structText
OthersText
2.3.6 xorm对数据类型的定义
name当前field对应的字段的名称,可选,如不写,则自动根据field名字和转换规则命名
pk是否是Primary Key
autoincr是否是自增
[not ]null 或 notnull是否可以为空
unique是否是唯一
index是否是索引
extends应用于一个匿名成员结构体或者非匿名成员结构体之上
-Field将不进行字段映射
->Field将只写入到数据库而不从数据库读取
<-Field将只从数据库读取,而不写入到数据库
createdField将在Insert时自动赋值为当前时间
updatedField将在Insert或Update时自动赋值为当前时间
deletedField将在Delete时设置为当前时间,并且当前记录不删除
versionField将会在insert时默认为1,每次更新自动加1
default 0或default(0)设置默认值,紧跟的内容如果是Varchar等需要加上单引号
json表示内容将先转成Json格式,然后存储到数据库中,数据库中的字段类型可以为Text或者二进制
comment设置字段的注释(当前仅支持mysql)
//用户表
type UserTable struct {
    UserId   int64 `xorm:"pk autoincr"`  //用户id  主键
    UserName string `xorm:"varchar(32)"` //用户名称
    UserAge  int64 `xorm:"default 1"`    //用户年龄
    UserSex  int64 `xorm:"default 0"`    //用户性别
}
//学生表
type StudentTable struct {
    Id          int64  `xorm:"pk autoincr"` //主键 自增
    StudentName string `xorm:"varchar(24)"` //
    StudentAge  int    `xorm:"int default 0"`
    StudentSex  int    `xorm:"index"` //sex为索引
}

//人类表
type PersonTable struct {
    Id         int64     `xorm:"pk autoincr"`   //主键自增
    PersonName string    `xorm:"varchar(24)"`   //可变字符
    PersonAge  int       `xorm:"int default 0"` //默认值
    PersonSex  int       `xorm:"notnull"`       //不能为空
    City       CityTable `xorm:"-"`             //不映射该字段 那就不会在数据库里面创建该字段
}

2.4 创建引擎组

在xorm中,通过创建引擎组EngineGroup来实现对从数据库(Master/Slave)读写分离的支持。在创建引擎章节中,我们已经介绍过了,在xorm里面,可以同时存在多个Orm引擎,一个Orm引擎称为Engine,一个Engine一般只对应一个数据库,而EngineGroup一般则对应一组数据库。EngineGroup通过调用xorm.NewEngineGroup生成,如:

import (
    _ "github.com/lib/pq"
    "github.com/xormplus/xorm"
)

var eg *xorm.EngineGroup

func main() {
	conns := []string{
		"postgres://postgres:root@localhost:5432/test?sslmode=disable;",
		"postgres://postgres:root@localhost:5432/test1?sslmode=disable;",
		"postgres://postgres:root@localhost:5432/test2?sslmode=disable",
	}
    
    var err error
	eg, err = xorm.NewEngineGroup("postgres", conns)
}

or

import (
    _ "github.com/lib/pq"
    "github.com/xormplus/xorm"
)

var eg *xorm.EngineGroup

func main() {
    var err error
    master, err := xorm.NewEngine("postgres", "postgres://postgres:root@localhost:5432/test?sslmode=disable")
    if err != nil {
		return
	}
    
    slave1, err := xorm.NewEngine("postgres", "postgres://postgres:root@localhost:5432/test1?sslmode=disable")
    if err != nil {
		return
	}
    
    slave2, err := xorm.NewEngine("postgres", "postgres://postgres:root@localhost:5432/test2?sslmode=disable")
    if err != nil {
		return
	}
   
   	slaves := []*xorm.Engine{slave1, slave2}
	eg, err = xorm.NewEngineGroup(master, slaves)
}

创建完成EngineGroup之后,并没有立即连接数据库,此时可以通过eg.Ping()来进行数据库的连接测试是否可以连接到数据库,该方法会依次调用引擎组中每个Engine的Ping方法。另外对于某些数据库有连接超时设置的,可以通过起一个定期Ping的Go程来保持连接鲜活。EngineGroup可以通过eg.Close()来手动关闭,但是一般情况下可以不用关闭,在程序退出时会自动关闭。

  • NewEngineGroup方法
    func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error)
    前两个参数的使用示例如上,有两种模式。
    模式一:通过给定DriverName,DataSourceName来创建引擎组,每个引擎使用相同的Driver。每个引擎的DataSourceNames是[]string类型,第一个元素是Master的DataSourceName,之后的元素是Slave的DataSourceName。
    模式一:通过给定*xorm.Engine[]*xorm.Engine来创建引擎组,每个引擎可以使用不同的Driver。第一个参数为Master的*xorm.Engine,第二个参数为Slave的[]*xorm.Engine

NewEngineGroup方法,第三个参数为policies,为Slave给定负载策略,该参数将在负载策略章节详细介绍,如示例中未指定,则默认为轮询负载策略。

  • Master方法
    func (eg *EngineGroup) Master() *Engine
    返回Master数据库引擎
  • Slave方法
    func (eg *EngineGroup) Slave() *Engine
    依据给定的负载策略返回一个Slave数据库引擎
  • Slaves方法
    func (eg *EngineGroup) Slaves() []*Engine
    返回所以Slave数据库引擎
  • GetSlave方法
    func (eg *EngineGroup) GetSlave(i int) *Engine
    依据一组Slave数据库引擎[]*xorm.Engine下标返回指定Slave数据库引擎。通过给定DriverName,DataSourceName来创建引擎组,则DataSourceName的第二个元素的数据库为下标0的Slave数据库引擎。
  • SetPolicy方法
    func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup
    设置引擎组负载策略

2.5 基本操作

package main

import (
   "fmt"
   _ "github.com/go-sql-driver/mysql" //千万不要忘记导入
   "github.com/go-xorm/xorm"
   "xorm.io/core"
)

var engine *xorm.Engine
func main() {
   
   //1.创建数据库引擎对象
   var err error 
   engine, err = xorm.NewEngine("mysql", "root:root@(127.0.0.1:3306)/golang?charset=utf8mb4")
   engine.Ping() //连接测试,连接正常输出 PING DATABASE mysql
   if err != nil {
      panic(err.Error())
   }
   // 2.延迟关闭数据库引擎
   defer engine.Close()
   //  3.数据库引擎设置
   engine.ShowSQL(true)                     //将sql语句显示在控制台中
   engine.Logger().SetLevel(core.LOG_DEBUG) // 设置日志级别,在控制台打印调试及以上的信息

   // 查询表中所有的数据
   session := engine.Table("user")
   count, err := session.Count()
   if err != nil {
      panic(err.Error())
   }
   fmt.Println(count)

   // 使用原生sql语句进行查询
   result, err := engine.Query("select * from user")
   if err != nil {
      panic(err.Error())
   }
   for key, value := range result {
      fmt.Println(key, value)
   }
}

测试

package main
import (
  "fmt"
  "github.com/go-xorm/xorm"
  "github.com/go-xorm/core"
  _ "github.com/go-sql-driver/mysql"
)
// 对应数据库的tablename必须为student
// 执行mysql时,对应的字段为xxx,yyy,zzz; 也可以省掉,默认的mysql字段为id,username,address
type Student struct {
  Id    int    `xorm:"INT(11) 'xxx'"`
  Username string  `xorm:"VARCHAR(64) 'yyy'"`
  Address string  `xorm:"VARCHAR(256) 'zzz'"`
}
func main() {
  engine, err := xorm.NewEngine("mysql", "root@/taoge?charset=utf8") // dbname是taoge
  if err != nil{
    fmt.Println(err)
    return
  }
  // 如下Ping可以不要
  // if err := engine.Ping(); err != nil{
  //   fmt.Println(err)
  //   return
  // }
  //engine.ShowSQL(true) // 显示SQL的执行, 便于调试分析
  engine.SetTableMapper(core.SnakeMapper{})
  st1 := new(Student)
  st1.Username = "taoge"
  st1.Address = "China"
  affected, err := engine.Insert(st1)
  fmt.Println(affected)
  st2 := new(Student)
  result,err := engine.Where("xxx=?", 1).Get(st2)
  fmt.Println(result)
  fmt.Println(st2.Username)
  fmt.Println(st2.Address)
}

创建数据库:

CREATE TABLE `student` (
 `xxx` int(11) NOT NULL AUTO_INCREMENT,
 `yyy` varchar(64) NOT NULL,
 `zzz` varchar(256) NOT NULL ,
 PRIMARY KEY (`xxx`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

3 XORM数据库操作

3.1 表操作

engine.DBMetas() // 获取数据库中所有的表,字段,索引的信息。
engine.CreateTables() //创建表,参数为一个或多个空的对应Struct的指针。
engine.IsTableEmpty() //判断表是否为空,参数和 CreateTables 相同
engine.IsTableExist() //判断表是否存在
engine.DropTables() //删除表 参数为一个或多个空的对应Struct的指针或者表的名字。如果为string传入,则只删除对应的表,如果传入的为Struct,则删除表的同时还会删除对应的索引。
engine.CreateIndexes //根据struct中的tag来创建索引
engine.CreateUniques //根据struct中的tag来创建唯一索引

err := engine.Sync(new(User), new(Group)) //同步数据库结构
err := engine.Sync2(new(User), new(Group))

// 导出数据库的结构和数据
engine.DumpAll(w io.Writer)
engine.DumpAllToFile(fpath string)
// 执行SQL脚本
engine.Import(r io.Reader)
engine.ImportFile(fpath string)
//判断一个表当中内容是否为空
personEmpty, err := engine.IsTableEmpty(new(PersonTable))
if err != nil {
    panic(err.Error())
}
if personEmpty {
    fmt.Println("人员表是空的!")
}else{
    fmt.Println("人员表不为空!")
}

//判断表结构是否存在
studentExist, err := engine.IsTableExist(new(StudentTable))
if err != nil {
    panic(err.Error())
}
if studentExist {
    fmt.Println("学生表存在!")
}else{
    fmt.Println("学生表不存在!")
}

3.2 增

// 插入单条记录
user1 := new(User)
user1.Id = 6
user1.Name = "Dim"
affected, err := engine.Insert(user1)
fmt.Println("插入单条数据:", affected, err)

// 插入多条数据
users := make([]*User, 3)
users[0] = new(User)
users[0].Id = 11
users[0].Name = "name11"
users[1] = new(User)
users[1].Id = 12
users[1].Name = "name12"
users[2] = new(User)
users[2].Id = 13
users[2].Name = "name13"
affects, errs := engine.Insert(&users)
fmt.Println("插入多条数据:", affects, errs)

3.3 删除

删除数据Delete方法,参数为struct的指针并且成为查询条件。

软删除Deleted,不真正的删除数据,而是标记一个删除时间。

// 删除
affdel, errdel := engine.Delete(user)
fmt.Println(affdel, errdel)

3.4 查改

func main(){
    engine, err := xorm.NewEngine("mysql", "root:root@/elmcms?charset=utf8")
    if err != nil {
        panic(err.Error())
    }
    //条件查询
    //1.ID查询
    //首先你得要有一个结构体来存放查询出来的数据
    var person PersonTable
    //select * from person_table where id = 1  并且get只获取到一条记录哈!
    engine.Id(1).Get(&person)
    fmt.Println(person)
    fmt.Println("----------------------------")

    //2.where多条件查询
    //首先你得要有一个结构体来存放查询出来的数据
    var person1 PersonTable
    //select * from person_table where person_age = 26 and person_sex = 2
    engine.Where(" person_age = ? and person_sex = ?", 30, 1).Get(&person1)
    fmt.Println(person1)
    fmt.Println("----------------------------")

    //3.and条件查询
    //首先你得要有一个结构体来存放查询出来的数据
    var persons []PersonTable
    //select * from person_table where person_age = 26 and person_sex = 2   find 会返回一条或者多条记录
    err = engine.Where(" person_age = ?", 30).And("person_sex = ?", 1).Find(&persons)
    if err != nil {
        panic(err.Error())
    }
    fmt.Println(persons)
    fmt.Println("----------------------------")

    //4.or条件查询
    var personArr []PersonTable
    //select * from person_table where person_age = 26 or person_sex = 1
    err = engine.Where(" person_age = ?", 26).Or("person_sex = ?", 1).Find(&personArr)
    if err != nil {
        panic(err.Error())
    }
    fmt.Println(personArr)
    fmt.Println("----------------------------")

    //5.原生sql语句查询 支持like
    var personNative []PersonTable
    //执行原生的sql语句
    err = engine.SQL(" select * from person_table where person_name like '%i%' ").Find(&personNative)
    if err != nil {
        panic(err.Error())
    }
    fmt.Println(personNative)
    fmt.Println("----------------------------")

    //6.排序条件查询
    var personOrderBy []PersonTable
    //select * from person_table orderby person_age  升序排列
    //engine.OrderBy(" person_age ").Find(&personsOrderBy)  不写desc默认就是asc升序排序
    err = engine.OrderBy(" person_age desc ").Find(&personOrderBy)
    if err != nil {
        panic(err.Error())
    }
    fmt.Println(personOrderBy)
    fmt.Println("----------------------------111")

    //7.查询特定字段
    var personCols []PersonTable
    engine.Cols("person_name", "person_age").Find(&personCols)
    for _,col := range personCols {
        fmt.Println(col)
    }
    //fmt.Println(personCols)
    fmt.Println("----------------------------")

    //增加记录
    //插入你得先有数据  弄到结构体里面去
    personInsert := PersonTable{
        PersonName: "wahaha",
        PersonAge:  40,
        PersonSex:  10,
    }
    rowNum, err := engine.Insert(&personInsert)
    fmt.Println(rowNum)  //rowNum表示受影响的行数
    fmt.Println("----------------------------")


    //删除操作
    personDelete := PersonTable{}
    //根据id删除
    rowNum1, err := engine.Id(4).Delete(&personDelete)
    fmt.Println(rowNum1)  //rowNum1也是表示受影响的行数
    fmt.Println("----------------------------")

    //更新操作
    personUpdate := PersonTable{
        PersonName: "胡绍良",
        PersonAge:  30,
        PersonSex:  1,
    }
    rowNum2, err := engine.Id(1).Update(&personUpdate)
    fmt.Println(rowNum2) // rowNum2也是表示受影响的行数
    fmt.Println("----------------------------")

    //统计功能count
    count, err := engine.Count(new(PersonTable))
    fmt.Println("persontable表总记录数:",count)

    //事务操作
    personsArray := []PersonTable{
        PersonTable{
            PersonName: "Jack",
            PersonAge:  28,
            PersonSex:  1,
        },
        PersonTable{
            PersonName: "Mali",
            PersonAge:  28,
            PersonSex:  1,
        },
        PersonTable{
            PersonName: "Ruby",
            PersonAge:  28,
            PersonSex:  1,
        },
    }

    session := engine.NewSession()
    session.Begin()
    for i:=0;i<len(personsArray);i++{
        _, err = session.Insert(personsArray[i])
        if err != nil {
            session.Rollback()
            session.Close()
        }
    }
    err = session.Commit()
    session.Close()
    if err != nil {
        panic(err.Error())
    }

}

//人员结构表
type PersonTable struct {
    Id                 int64  `xorm:"pk autoincr"`
    PersonName      string `xorm:"varchar(24)"`
    PersonAge       int    `xorm:"int default 0"`
    PersonSex       int     `xorm:"notnull"`
    City            CityTable `xorm:"-"`
}

type CityTable struct {
    CityName      string
    CityLongitude float32
    CityLatitude  float32

}
  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值