实现一个数据库迁移的案子。有些知识点值得记录。

技术框架

github.com/go-xorm/xorm:数据库操作

github.com/denisenkom/go-mssqldb:sqlserver驱动

github.com/go-sql-driver/mysql:mysql驱动

方案设计

使用 sql 语句查询原数据库数据,再插入新数据库。

查询新数据库表最后一条记录。

根据条件是否创建新数据表,再查询新数据库最后一条记录的 ID 值,以此为起点查询旧数据库,因为迁移只需要从已导入的最后一条开始即可,如果表不存在,则从 0 开始。

使用回调函数,获取旧数据库,处理数据(或舍弃,或修改,等),再插入到新数据库,直接用 xorm 的结构体即可。xorm 可批量插入,但是旧数据库无法做到批量获取,查询并存储到切片中,到达一定数量(如3000条),再插入新数据库。

选择记录

原数据库为 sqlserver,表和列部分有中文,不符合 xorm 要求。只能使用 sql 语句操作。

新数据库为 mysql,全英文,可用 xorm 结构体映射,查询、插入较方便。

试过将 xorm 的结构体成员变量改为中文,在 Golang 中使用中文作为变量名是可以的,只是反射不成功。

实践过程及知识点

数据库连接

// mysql

root:root@tcp(8.8.8.8:3305)/mydb?charset=utf8&interpolateParams=true&parseTime=true&loc=Local

// mssql

server=8.8.8.8;user id=latelee;password=123456;database=mydb;encrypt=disable;

注意,使用github.com/denisenkom/go-mssqldb时,需要添加encrypt=disable;,否则加不上。

xorm小知识

设置时区:

engine.DatabaseTZ = time.UTC // time.Local

engine.TZLocation = time.UTC

是否显示 sql 语句:

//engine.ShowSQL(true)

engine.ShowSQL(false)

xorm结构体映射

xorm 使用结构体与数据库字段映射,各种操作十分方便。

设置完全映射:

engine.SetMapper(core.SameMapper{})

定义示例:

type TheData struct {

Id int `xorm:"int pk not null autoincr 'id'"` // autoincr

Money sql.NullFloat64 `xorm:"float default null"`

}

第一成员带 id,在数据库即为此名,第二成员不带,将映射为 Money。

结构体成员需大写,否则反射失败。

新数据库创建

一般创建表使用其它的方法,但为了方便使用,直接在代码中创建。

var sqlstr string

//sqlstr = "show tables like 'xxx'"

sqlstr = fmt.Sprintf("show tables like '%s'", newTableName)

_, err = engine.SQL(sqlstr).Count()

if err != nil { // 没有找到现成函数判断,用返回值

log.Printf("table %v exist\n", newTableName)

} else {

log.Printf("create table for %v\n", newTableName)

err = engine.Sync2(vnewtable)

if err != nil {

log.Println("create table failed: ", err.Error(), "will exit")

return

}

}

代码先判断是否存在数据表,不存在再调用engine.Sync2创建,该函数可同时创建多个数据表。不过似乎没有直接的 API 接口判断是否存在数据表,只好用 sql 语句查询,判断其返回值。

数据库为空值

如果读取到数据库中的空值,会返回错误:

Scan failed: sql: Scan error on column index 26, name "测试空值": converting driver.Value type string ("NULL") to a float32: invalid syntax

此问题可用 sql 包提供的类型解决。如sql.NullString、sql.NullFloat64等。这些类型实际是结构体,包括了Valid成员,通过该值可判断。

代码重用

由于迁移过程是完全一样的,只是数据库、表名、字段等不同。可以将共用部分抽象成函数,具体操作使用回调函数解决。

至于不同之处,则由调用者将其传递到共用函数中即可。

回调函数定义和使用

type DataCb func (rows *sql.Rows, engine *xorm.Engine, totalCnt int64) int

func MigrateDB(olddb, oldtable, newdb string, vnewtable interface{}, datacb DataCb) {

...

datacb(rows, engin, cnt)

}

在回调函数中,根据旧数据库字段扫描,示例如:

for rows.Next() {

err := rows.Scan(&a, &b, &c, &d)

}

注意,Scan 参数个数和数据库的列数相同,否则出错。可直接用新数据库结构体成员,如果不需要,可以使用其它变量。

结构体映射

结构体用指针传递,这样可进行通用处理,即 MigrateDB 不再与具体结构体关联。

// 获取新表结构体名称

atype := reflect.TypeOf(vnewtable)

if atype.Kind() == reflect.Ptr { // 如果是结构体指针,再获取结构体

atype = atype.Elem()

}

// 如果还不是结构体,出错

if atype.Kind() != reflect.Struct {

log.Println("Check type error not Struct")

return

}

newTableName := atype.Name()

// 取第N个字段及值

idx := 0

var newIDName string = atype.Field(idx).Name// 此处为ID值,如果获取不到tag,选择变量名

// 查找单引号,得到tag

xormStr := atype.Field(idx).Tag.Get("xorm")

idx1 := strings.Index(xormStr, "'")

idx2 := strings.LastIndex(xormStr, "'")

if idx1 != -1 && idx2 != -1 && idx1 < idx2 {

newIDName = xormStr[idx1+1:idx2]

}

log.Println("debug new table: ", newTableName, newIDName)

功能有二:获取结构体名称,因为 sql 语句需要使用该名称。获取第 0 个字段名称,结合实际情况,数据库第 0 个字段为 ID,故设计上,将结构体第 0 个字段置为 ID。

注意,这里使用结构体指针形式传递,不需要额外声明结构体变量,代码较整洁。

go 获得 mysql 实际运行 SQL,Golang实践录:一个数据库迁移的代码记录相关推荐

  1. Golang实践录:一个数据库迁移的代码记录

    实现一个数据库迁移的案子.有些知识点值得记录. 技术框架 github.com/go-xorm/xorm:数据库操作 github.com/denisenkom/go-mssqldb:sqlserve ...

  2. Golang实践录:命令行cobra库实例再三优化

    本文是上一文章<Golang实践录:命令行cobra库实例优化> 的优化,主要的子命令的业务实现的整理. 起因 上一版本实现的方式,还是有点不满意,格式也不对齐,重要的是,似乎不是正规的方 ...

  3. Golang实践录:命令行cobra库实例优化

    本文上一文章<Golang实践录:命令行cobra库实例> 的优化,主要的子命令的业务实现的整理. 起因 旧版本中,每个子命令的入口函数,均需一一判断传入参数,并调用对应的业务实现函数,编 ...

  4. mysql通过拷贝数据文件的方式进行数据库迁移

    mysql通过拷贝数据文件的方式进行数据库迁移 --环境windows 将源机器A数据库拷贝到目标机器B: 我先在目标机器B上安装MySQL,停止mysql服务,然后将源机器A的data下关于数据库的 ...

  5. Golang实践录:oracle数据库实践

    本文在 Windows 7 64bit 系统上使用 golang 连接查询 oracle 数据库. 环境准备 前置条件: 安装mingw(取其gcc及库,因为要用cgo编译),安装git(取其bash ...

  6. mysql优化和sql优化一样吗_mysql数据库的sql优化原则和常见误区

    gistfile1.txt 优化目标 1.减少 IO 次数 IO永远是数据库最容易瓶颈的地方,这是由数据库的职责所决定的,大部分数据库操作中超过90%的时间都是 IO 操作 所占用的,减少 IO 次数 ...

  7. Golang实践录:使用gin框架实现转发功能:管理后端服务

    近段时间需要实现一个转发 post 请求到指定后端服务的小工具,由于一直想学习 gin 框架,所以就使用这个框架进行尝试,预计会产生几篇文章.本文研究如何管理后端服务. 思路 在启动 gin 服务前, ...

  8. Laravel 实践之路: 数据库迁移与数据填充

    数据库迁移实际上就是对数据库库表的结构变化做版本控制,之前对数据库库表结构做修改的方式比较原始,比如说对某张库表新增了一个字段,都是直接在库表中执行alter table xxx add .. 的方式 ...

  9. mysql查询一个数据库所有表的记录数,mysql 查看数据库中所有表的记录数

    mysql使用select count(*) from table_name可以查询某个表的总记录数.想快速的知道数据库中所有表的记录数信息怎么办?如果使用mysql的版本在5.0及以上,可以通过查询 ...

最新文章

  1. CreateWindow创建指定宽和高的client区域窗口的方法
  2. undefined reference to 'typeinfo for android::Thread'
  3. 判断一个数是不是2的指数幂
  4. 面向对象简述--对象、引用、指针
  5. K8S批量scale deploy的副本为0,结合xargs -I使用
  6. 控制台输出红色字体异常提示err
  7. C语言设备管理器作业,你知道到吗,C语言竟是如何调用硬件的?
  8. java中wait和sleep的区别
  9. java 桥 word_java导出word的6种方式(转发)
  10. 第五章数理统计--样本和抽样分布
  11. 香港年轻人买房压力有多大
  12. 人工智能行业有哪些岗位_电力人有哪些岗位将被人工智能取代?
  13. C语言 FileStreaming buffer
  14. WebRTC直播技术方案
  15. pku 2186 Popular Cows (tarjan缩点)
  16. C++ 访问成员 “->“还是“.“
  17. 分享一些前端优质的掘金小册,学完技术感觉已经超神了
  18. 点餐系统源码|点餐系统小程序源码
  19. 【STP】生成树协议及STP 802.1D (上)
  20. android 跳转京东app,第三方应用跳转到京东app

热门文章

  1. 《JavaScript 每周导读》【第一期】
  2. PHP Parse error: parse error, unexpected T_OBJECT_OPERATOR
  3. 用gnuplot画出c产生数据的波形图
  4. stm32 中bootloader、startup_stm32f10x_md.s的作用
  5. Linux 动态库的显示调用
  6. 系列笔记 | 深度学习连载(6):卷积神经网络基础
  7. ModuleNotFoundError: No module named 'mpl_toolkits.basemap'
  8. hangfire 过期记录_韩剧丨顶楼、空洞、再次十八岁、僵尸侦探、青春记录
  9. DCFNET: DISCRIMINANT CORRELATION FILTERS NETWORK FOR VISUAL TRACKING
  10. 白话详细解读(七)----- CBAM:Convolutional Block Attention Module