美文网首页工具软件
Zorm之国产化数据库实践

Zorm之国产化数据库实践

作者: counting_2ecd | 来源:发表于2021-03-17 13:55 被阅读0次

简介

go(golang)轻量级ORM,零依赖,支持达梦(dm),金仓(kingbase),神通(shentong),南大通用(gbase),mysql,postgresql,oracle,mssql,sqlite数据库.
源码地址:https://gitee.com/chunanyong/zorm
作者博客:https://www.jiagou.com

实践项目

夜莺监控告警项目国产化数据库的支持
夜莺是一套滴滴团队主导开源的分布式高可用的运维监控系统。
源码地址:https://github.com/didi/nightingale

改造方案

夜莺使用的是xorm作为ORM库,不支持国产数据库。尝试过通过ODBC的方式连接达梦,操作时遇到了语法不兼容问题。之后又了解到zorm,这个本身天然支持国产数据库的ORM框架。
面前出现了两种选择: 一是继续尝试ODBC的方式,一库一库的调试,手动解决各种兼容问题。二是替换项目的orm库,改造一次,就可以把国产四库的支持全部搞定。想想还是后者比较香,改造走起!

改造过程

首先数据库初始化,夜莺项目有多个数据库

//声明存储多库连接信息的map,key是dbname,value是DBDao
var DB = map[string]*zorm.DBDao{}

func InitMySQL(names ...string) {
        //读取mysql.yml数据库配置文件
    confdir := path.Join(runner.Cwd, "etc")
    mysqlYml := path.Join(confdir, "mysql.local.yml")
    if !file.IsExist(mysqlYml) {
        mysqlYml = path.Join(confdir, "mysql.yml")
    }

    confs := make(map[string]MySQLConf)
    err := file.ReadYaml(mysqlYml, &confs)
    if err != nil {
        log.Fatalf("cannot read yml[%s]: %v", mysqlYml, err)
    }
        //多库,不同的name
    count := len(names)
    for i := 0; i < count; i++ {
        conf, has := confs[names[i]]
        if !has {
            log.Fatalf("no such mysql conf: %s", names[i])
        }

        dbDaoConfig := zorm.DataSourceConfig{
            //DSN 数据库的连接字符串
            DSN: conf.Addr,
            //数据库驱动名称:mysql,postgres,oci8,sqlserver,sqlite3,dm,kingbase 和DBType对应,处理数据库有多个驱动
            DriverName: "kingbase",
            //数据库类型(方言判断依据):mysql,postgresql,oracle,mssql,sqlite,dm,kingbase 和 DriverName 对应,处理数据库有多个驱动
            DBType: "kingbase",
            //MaxOpenConns 数据库最大连接数 默认50
            MaxOpenConns: 50,
            //MaxIdleConns 数据库最大空闲连接数 默认50
            MaxIdleConns: 50,
            //ConnMaxLifetimeSecond 连接存活秒时间. 默认600(10分钟)后连接被销毁重建.避免数据库主动断开连接,造成死连接.MySQL默认wait_timeout 28800秒(8小时)
            ConnMaxLifetimeSecond: 600,
            //PrintSQL 打印SQL.会使用FuncPrintSQL记录SQL
            PrintSQL: conf.Debug,
        }
    
        // 根据dbDaoConfig创建dbDao, 一个数据库只执行一次,第一个执行的数据库为 defaultDao,后续zorm.xxx方法,默认使用的就是defaultDao
        dbDao, _ := zorm.NewDBDao(&dbDaoConfig)
        DB[names[i]] = dbDao
    }
}

建立连接
通过传入不同的dbName 获取绑定连接的Ctx

func getNewCtx(dbName string) context.Context {
    var ctx = context.Background()
    var dbDao *zorm.DBDao = DB[dbName]
        
    newCtx, err := dbDao.BindContextDBConnection(ctx)
    if err != nil { //标记测试失败
        log.Fatalf("错误:%v", err)
    }

    return newCtx
}

操作
zorm还有一个方便实用的点:

  1. 在codeGenerator.go 中配置好数据库的连接信息
  2. 如果只想初始化单张表,请修改codeGenerator_test.go 中TestCodeGenerator调用的code参数,为表名
  3. 单表初始化:执行go test -v codeGenerator_test.go codeGenerator.go -test.run TestCodeGenerator
    整个库初始化:执行go test -v codeGenerator_test.go codeGenerator.go -test.run TestCodeGeneratorALL

生成器代码简单清晰,可根据自己的需求做一些调整,使最终生成的代码能一步到位。

生成效果
拿一个表举例

//table name
const NodeStructTableName = "node"

// NodeStruct
type Node struct {
    zorm.EntityStruct
    Id int64 `column:"id" json:"id"`
    Pid int64 `column:"pid" json:"pid"`
    Name string `column:"name" json:"name"`
    Path string `column:"path" json:"path"`
    Leaf int `column:"leaf" json:"leaf"`
    Note string `column:"note" json:"note"`

}

func (entity *Node) GetTableName() string {
    return NodeStructTableName
}

//GetPKColumnName 获取数据库表的主键字段名称.因为要兼容Map,只能是数据库的字段名称.
func (entity *Node) GetPKColumnName() string {
    return "id"
}

增删改查
使用原生的sql语句,没有对sql语法做限制.语句使用Finder作为载体

    //查询
    var obj Node
    //has, err := DB["mon"].Where(col+"=?", val).Get(&obj)  这是之前xorm的代码
    //获取对应数据库的连接
    newCtx := getNewCtx("mon")
    finder := zorm.NewSelectFinder(NodeStructTableName) 
    finder.Append("WHERE "+col+"=?", val)
    has, err := zorm.QueryRow(newCtx, finder, &obj)



  //添加
    node := Node{
        Pid:  0,
        Name: "cop",
        Path: "cop",
        Leaf: 0,
        Note: "公司节点",
    }
    //_, err = DB["mon"].Insert(&node)  这是之前xorm的代码
    //手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
    _, err = zorm.Transaction(newCtx, func(newCtx context.Context) (interface{}, error) {
        //具体操作
        _, err = zorm.Insert(newCtx, &node)
        return nil, err
    })
    
    if err != nil {
        log.Fatalln("cannot insert node[cop]")
    }


    //修改
    newCtx := getNewCtx("mon")
    //手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
    _, err := zorm.Transaction(newCtx, func(newCtx context.Context) (interface{}, error) {
        _, err := zorm.Update(newCtx, obj)
        //如果返回的err不是nil,事务就会回滚
        return nil, err

    })
      

    //删除
    //_, err := DB["mon"].Where("id=?", n.Id).Delete(new(Node))
    //手动开启事务,匿名函数返回的error如果不是nil,事务就会回滚
    _, err := zorm.Transaction(newCtx, func(newCtx context.Context) (interface{}, error) {
        finder := zorm.NewDeleteFinder(NodeStructTableName)
        finder.Append("WHERE id=?", n.Id)
        _, err := zorm.UpdateFinder(newCtx, finder)
        return nil, err
    })

改造总结

zorm上手非常简单,改造过程也很顺畅。
以上只是zorm使用的部分示例,除此之外zorm还支持事务传播、批量操作、不方便使用struct的场景,以及读写分离等。更多使用场景请前往 zorm官方
如有使用方面的疑问请下方留言,改造建议或想了解更多,请在最上方源码地址找官方,作者回复也很热情: )

相关文章

网友评论

    本文标题:Zorm之国产化数据库实践

    本文链接:https://www.haomeiwen.com/subject/jiyvqltx.html