先通过数据表结构同步功能将源数据库和目标数据库的结构同步。
从config/config.json获取源数据库配置信息、目的数据库配置信息,创建fromDSN和toDsn,打开数据库之后获取到fromDb和toDb用于后续的SQL执行。
其中config.json格式为:
{
"sqlType":"mysql",
"fromDb" : {
"host":"localhost",
"port":3306,
"userName":"fromdb-username",
"password":"fromdb-pwd",
"database":"fromdb-database-name",
"charset":"utf8"
},
"toDb":{
"host":"localhost",
"port":3306,
"userName":"todb-username",
"password":"todb-pwd",
"database":"todb-database-name",
"charset":"utf8"
}
}
在程序所在目录下创建success.txt和tableList.txt文档,success.txt中保存导出成功的表名,tableList.txt中自行填入所要导出的数据表名,每个表名占据一行。
使用make([][]byte, len(cols))方式获取原来的表各个字段名称,再创建一个scans变量用于读取每个字段对应的内容,动态匹配每个表,不用每张表都创建一个结构体。
vals := make([][]byte, len(cols))
scans := make([]interface{}, len(cols))
这样做的优势是一旦你所要导出的表较多的时候,能够非常省心,否则维护每个表的struct都将会成为你的负担,尤其是当一些表的字段有增删的时候。
下面是完整代码。如有疑问请留言咨询,我会一一解答。
package main
import (
"bufio"
"database/sql"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
_ "github.com/go-sql-driver/mysql"
)
var cfg Config
var fromDb *sql.DB
var toDb *sql.DB
func readConfig() {
bts, err := ioutil.ReadFile("./config/config.json")
if err != nil {
log.Panicln("ReadFile error:", err)
}
err = json.Unmarshal(bts, &cfg)
if err != nil {
log.Panicln("Unmarshal config file error:", err)
}
}
func connectSQL() {
fromDSN := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", cfg.FromDb.UserName, cfg.FromDb.Password,
cfg.FromDb.Host, cfg.FromDb.Port, cfg.FromDb.Database, cfg.FromDb.Charset)
fDb, err := sql.Open(cfg.SqlType, fromDSN)
if err != nil {
log.Panicln("sql Open error:", err)
}
toDSN := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", cfg.ToDb.UserName, cfg.ToDb.Password,
cfg.ToDb.Host, cfg.ToDb.Port, cfg.ToDb.Database, cfg.ToDb.Charset)
tDb, err := sql.Open(cfg.SqlType, toDSN)
if err != nil {
fDb.Close()
log.Panicln("sql Open error:", err)
}
fromDb = fDb
toDb = tDb
}
func getTables() []string {
sf, err := os.Open("./success.txt")
if err != nil {
log.Panicln("Open success file error:", err)
}
defer sf.Close()
tableMap := make(map[string]bool)
scanner1 := bufio.NewScanner(sf)
for scanner1.Scan() {
line := scanner1.Text()
if len(line) < 1 {
continue
}
tableMap[line] = true
}
f, err := os.Open("./tableList.txt")
if err != nil {
log.Panicln("Open table list error:", err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
var tables []string
for scanner.Scan() {
line := scanner.Text()
if len(line) < 1 {
continue
}
if tableMap[line] {
continue
}
tables = append(tables, line)
}
return tables
}
func importData(table string) {
querySql := "select * from " + table + ";"
stmt, err := fromDb.Prepare(querySql)
if err != nil {
log.Panicln("import data from db Prepare error:", err)
}
rows, err := stmt.Query()
if err != nil {
log.Panicln("import data from db Query error:", err)
}
cols, err := rows.Columns()
if err != nil {
log.Panicln("import data from db Columns error:", err)
}
vals := make([][]byte, len(cols))
scans := make([]interface{}, len(cols))
for i := range vals {
scans[i] = &vals[i]
}
//先删除再插入
truncateSql := "truncate " + table + ";"
//先清空表再插入
truncateStmt, err := toDb.Prepare(truncateSql)
if err != nil {
log.Panicln("import data to db Prepare truncate error:", err)
}
_, err = truncateStmt.Exec()
if err != nil {
log.Panicln("import data to db Exec truncate sql error:", err)
}
//根据字段数量拼接insert语句
insertSql := "insert into " + table + " ("
insertTail := ""
for i := range cols {
//使用``将字段括起来防止出现表的字段和关键字一样时报1064 sql错误
insertSql += "`" + cols[i] + "`"
insertTail += "?"
if i+1 == len(cols) {
break
}
insertSql += ","
insertTail += ","
}
insertSql += ") values (" + insertTail + ");"
insertStmt, err := toDb.Prepare(insertSql)
if err != nil {
log.Panicln("import data to db Prepare error:", err)
}
for rows.Next() {
//动态获取字段信息
err = rows.Scan(scans...)
if err != nil {
log.Panicln("import data from db Scan error:", err)
}
//动态插入数据
_, err = insertStmt.Exec(scans...)
if err != nil {
log.Panicln("import data to db Exec error:", err)
}
}
}
func main() {
readConfig()
connectSQL()
defer fromDb.Close()
defer toDb.Close()
tables := getTables()
f, err := os.OpenFile("./success.txt", os.O_APPEND, 0666)
if err != nil {
log.Panicln("Open success file error:", err)
}
defer f.Close()
for index := range tables {
log.Println("import data to table:", tables[index])
importData(tables[index])
log.Println("import data to table:", tables[index], " success")
f.WriteString(tables[index] + "\r\n")
}
}