• 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
    • 3

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2 XORM 的基本使用

    参考资源:

    2.1 下载xorm

    go get github.com/go-xorm/xorm
    go get github.com/go-xorm/cmd
    
    • 1
    • 2

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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() //延迟关闭数据库
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.3.3 连接池

    如果需要设置连接池的空闲数大小,使用 engine.SetMaxIdleConns() 来实现。
    如果需要设置最大打开连接数,则使用 engine.SetMaxOpenConns() 来实现。
    如果需要设置连接的最大生存时间,则使用 engine.SetConnMaxLifetime() 来实现。
    
    • 1
    • 2
    • 3

    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。
    
    • 1
    • 2
    • 3
    engine.SetMapper(names.GonicMapper{})
    // 给表加前缀
    tbMapper := names.NewPrefixMapper(names.SnakeMapper{}, "prefix_")
    engine.SetTableMapper(tbMapper)
    // 加后缀
    names.NewSuffixMapper(names.SnakeMapper{}, "suffix")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    表名的优先级顺序如下:

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

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

    结构体tag指定的字段名优先级较高  xorm:"'column_name'"
    Mapper 自动映射的表名优先级较低
    
    • 1
    • 2
    type User struct {
       Id   int    //`xorm:"INT 'id'"`
       Name string //`xorm:"VARCHAR(30) 'name'"`
    }
    
    • 1
    • 2
    • 3
    • 4

    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:"-"`             //不映射该字段 那就不会在数据库里面创建该字段
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    创建完成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)
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    测试

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    创建数据库:

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    //判断一个表当中内容是否为空
    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("学生表不存在!")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.3 删除

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

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

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

    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
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
  • 相关阅读:
    Vue3中diff算法比对新老节点孩子数组
    Apache-tomcat-8.5.82下载安装以及环境变量配置
    Spring复习大纲:依赖注入Ioc+Beans+注解+数据访问+AOP+MVC等
    ITSS认证从申报到获得证书需要多长时间?
    UOS设备管理器中的信息获取(无须提权)
    2022年10月29日 20点 程序爱生活 纳指和恒指可能会小破前高,然后有概率继续破新低,但是破完就需要考虑一次较大级别反弹的概率了! 第一次不再完全看空!
    如何使用iPhone15在办公室观看家里电脑上的4k电影,实现公网访问本地群晖!
    jquery || js 实现阴影背景弹窗
    前端学成在线项目详细解析一
    【40. 石子合并(区间DP)】
  • 原文地址:https://blog.csdn.net/qq_42647903/article/details/126143046