8 changed files with 513 additions and 3 deletions
@ -0,0 +1,22 @@ |
|||||
|
package config |
||||
|
|
||||
|
// Config 导出sql所需的配置信息
|
||||
|
type Config struct { |
||||
|
Debug bool // 是否调试模式
|
||||
|
IsExportData bool // 是否导出数据
|
||||
|
ExportDataStep int64 // 导出数据时,每次查询数据量
|
||||
|
IsCreateDB bool // 是否生成建库语句
|
||||
|
OutPath string // 输出sql文件目录-绝对路径-用于导出
|
||||
|
SQLPath string // 导入的sql文件-绝对路径-用于导入
|
||||
|
OutZip bool // 是否导出zip压缩文件
|
||||
|
DbCfg *DbConfig // 数据库连接信息
|
||||
|
} |
||||
|
|
||||
|
// DbConfig 数据库连接配置
|
||||
|
type DbConfig struct { |
||||
|
Address string // 数据库连接地址
|
||||
|
Port int // 数据库端口
|
||||
|
User string // 数据库用户名
|
||||
|
Passwd string // 数据库密码
|
||||
|
DbName string // 数据库名
|
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
module gosql |
||||
|
|
||||
|
go 1.22.1 |
||||
@ -0,0 +1,43 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"log" |
||||
|
"runtime" |
||||
|
"utils/mysqldump" |
||||
|
) |
||||
|
|
||||
|
func main() { |
||||
|
// 全部核心运行程序
|
||||
|
runtime.GOMAXPROCS(runtime.NumCPU()) |
||||
|
// 系统日志显示文件和行号
|
||||
|
log.SetFlags(log.Lshortfile | log.LstdFlags) |
||||
|
|
||||
|
cfg := &mysqldump.Config{ |
||||
|
Debug: true, |
||||
|
IsExportData: true, |
||||
|
IsCreateDB: false, |
||||
|
OutZip: true, |
||||
|
OutPath: "/Users/zuo/gocode/src/github.com/shiguanghuxian/mysqldump/examples/mysqldump/out/", |
||||
|
SQLPath: "/Users/zuo/gocode/src/github.com/shiguanghuxian/mysqldump/examples/mysqldump/out/tslc_test_20180209T084241.sql", |
||||
|
DbCfg: &mysqldump.DbConfig{ |
||||
|
Address: "127.0.0.1", |
||||
|
Port: 3306, |
||||
|
User: "root", |
||||
|
Passwd: "123456", |
||||
|
DbName: "test", |
||||
|
}, |
||||
|
} |
||||
|
dm, err := mysqldump.New(cfg) |
||||
|
if err != nil { |
||||
|
log.Println(err) |
||||
|
return |
||||
|
} |
||||
|
// 导出
|
||||
|
path, err := dm.Export() |
||||
|
log.Println(err) |
||||
|
log.Println(path) |
||||
|
// 导入
|
||||
|
// dm.Import()
|
||||
|
|
||||
|
select {} |
||||
|
} |
||||
@ -0,0 +1,292 @@ |
|||||
|
package utils |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"log" |
||||
|
"os" |
||||
|
"reflect" |
||||
|
"regexp" |
||||
|
"strings" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/mholt/archiver" |
||||
|
) |
||||
|
|
||||
|
// Export 导出数据库所有表
|
||||
|
func (md *Mysqldump) Export() (outFile string, err error) { |
||||
|
if md.isClose == true { |
||||
|
return "", errors.New("已调用Close关闭相关资源,无法进行导出") |
||||
|
} |
||||
|
// 创建导出sql文件
|
||||
|
outFile = fmt.Sprintf("%s/%s_%s.sql", strings.TrimRight(md.cfg.OutPath, "/"), md.cfg.DbCfg.DbName, time.Now().Format("20060102T150405")) |
||||
|
lf, err := os.OpenFile(outFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660) |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
defer func() { |
||||
|
// 关闭文件
|
||||
|
lf.Close() |
||||
|
// 压缩文件
|
||||
|
if md.cfg.OutZip == true { |
||||
|
outZipFile := fmt.Sprintf("%s/%s_%s.zip", strings.TrimRight(md.cfg.OutPath, "/"), md.cfg.DbCfg.DbName, time.Now().Format("20060102T150405")) |
||||
|
err = archiver.Zip.Make(outZipFile, []string{outFile}) |
||||
|
if err == nil { |
||||
|
outFile = outZipFile |
||||
|
} |
||||
|
} |
||||
|
}() |
||||
|
|
||||
|
// 获取建库语句
|
||||
|
createSQL, err := md.GetCreateDbSQL() |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
// 获取数据库字符集
|
||||
|
charSet := "utf8" |
||||
|
var valid = regexp.MustCompile("CHARACTER SET ([a-z0-9A-Z]+) ") |
||||
|
finds := valid.FindAllStringSubmatch(createSQL, -1) |
||||
|
if len(finds) > 0 { |
||||
|
if len(finds[0]) > 1 { |
||||
|
charSet = finds[0][1] |
||||
|
} |
||||
|
} |
||||
|
// 判断是否需要建库语句
|
||||
|
if md.cfg.IsCreateDB == true { |
||||
|
createSQL = fmt.Sprintf("CREATE DATABASE `%s`; /*!40100 DEFAULT CHARACTER SET %s */", md.cfg.DbCfg.DbName, charSet) |
||||
|
} else { |
||||
|
createSQL = "" |
||||
|
} |
||||
|
|
||||
|
// 写入头部信息
|
||||
|
_, err = lf.WriteString(fmt.Sprintf(`/* |
||||
|
中防电信sql导出 |
||||
|
|
||||
|
数据库地址 : %s:%d |
||||
|
数据库类型 : MySQL |
||||
|
数据库名 : %s |
||||
|
|
||||
|
生成时间: %s |
||||
|
*/ |
||||
|
|
||||
|
%s |
||||
|
|
||||
|
SET NAMES %s; |
||||
|
SET FOREIGN_KEY_CHECKS = 0; |
||||
|
|
||||
|
`, |
||||
|
md.cfg.DbCfg.Address, |
||||
|
md.cfg.DbCfg.Port, |
||||
|
md.cfg.DbCfg.DbName, |
||||
|
time.Now().Format("2006-01-02 15:04:05"), |
||||
|
createSQL, |
||||
|
charSet)) |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
|
||||
|
// 查询数据库表列表
|
||||
|
tables, err := md.SelectTableNames() |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
|
||||
|
// 导出数据对象
|
||||
|
tplSqlModel := make([]*TPLSqlModel, 0) |
||||
|
// 循环表名,查询出对应的表创建语句
|
||||
|
for _, table := range tables { |
||||
|
log.Println("导出", table) |
||||
|
// 导出建表语句
|
||||
|
sql, err := md.GetCreateTableSQL(table) |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
tplSqlModel = append(tplSqlModel, &TPLSqlModel{ |
||||
|
TableName: table, |
||||
|
CreateSQL: sql, |
||||
|
}) |
||||
|
log.Println(sql) |
||||
|
// 写入一个表到建表语句
|
||||
|
_, err = lf.WriteString(fmt.Sprintf( |
||||
|
`%s-- ---------------------------- |
||||
|
-- Table structure for %s |
||||
|
-- ---------------------------- |
||||
|
DROP TABLE IF EXISTS %s%s%s; |
||||
|
%s; |
||||
|
%s`, |
||||
|
"\n\n", |
||||
|
table, |
||||
|
"`", |
||||
|
table, |
||||
|
"`", |
||||
|
sql, |
||||
|
"\n")) |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
// 导出数据
|
||||
|
if md.cfg.IsExportData == true { |
||||
|
md.ExportData(lf, table) |
||||
|
} |
||||
|
} |
||||
|
// js, _ := json.Marshal(aa)
|
||||
|
// log.Println(string(js))
|
||||
|
return outFile, nil |
||||
|
} |
||||
|
|
||||
|
// SelectTableNames 查询数据库表列表
|
||||
|
func (md *Mysqldump) SelectTableNames() (tables []string, err error) { |
||||
|
tables = make([]string, 0) |
||||
|
err = md.conn.SQL("SHOW TABLES;").Cols(fmt.Sprintf("Tables_in_%s", md.cfg.DbCfg.DbName)).Find(&tables) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// GetCreateTableSQL 查询创建表语句
|
||||
|
func (md *Mysqldump) GetCreateTableSQL(tableName string) (string, error) { |
||||
|
creates := make([]*CreateTable, 0) |
||||
|
err := md.conn.SQL(fmt.Sprintf("show create table %s", tableName)).Find(&creates) |
||||
|
log.Println(err) |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
if len(creates) == 0 { |
||||
|
return "", errors.New("查询table 创建语句错误") |
||||
|
} |
||||
|
return creates[0].CreateTable, nil |
||||
|
} |
||||
|
|
||||
|
// GetCreateDbSQL 获取创建数据库
|
||||
|
func (md *Mysqldump) GetCreateDbSQL() (string, error) { |
||||
|
createSQLs := make([]*CreateDb, 0) |
||||
|
err := md.conn.SQL(fmt.Sprintf("SHOW CREATE DATABASE %s", md.cfg.DbCfg.DbName)).Find(&createSQLs) |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
if len(createSQLs) == 0 { |
||||
|
return "", errors.New("查询创建数据库语句为空") |
||||
|
} |
||||
|
return createSQLs[0].CreateDatabase, nil |
||||
|
} |
||||
|
|
||||
|
// ExportData 导出数据为
|
||||
|
func (md *Mysqldump) ExportData(w io.Writer, tableName string) (err error) { |
||||
|
log.Println("开始导出数据:", tableName) |
||||
|
// 查询总数据行数
|
||||
|
var count int64 |
||||
|
count, err = md.conn.Table(tableName).Count() |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
log.Println(count) |
||||
|
|
||||
|
columns, xormColumns, err := md.conn.Dialect().GetColumns(tableName) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
var offset int64 |
||||
|
for offset = 0; offset < count; offset += md.cfg.ExportDataStep { |
||||
|
colNames := md.conn.Dialect().Quote(strings.Join(columns, md.conn.Dialect().Quote(", "))) |
||||
|
sql := fmt.Sprintf("select %s from %s limit %d offset %d", colNames, tableName, md.cfg.ExportDataStep, offset) |
||||
|
list, err := md.conn.QueryInterface(sql) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
for _, one := range list { |
||||
|
// 拼接插入语句头部
|
||||
|
installSQL := fmt.Sprintf("\nINSERT INTO %s (%s) VALUES ", |
||||
|
md.conn.Dialect().Quote(tableName), |
||||
|
colNames) |
||||
|
values := make([]string, 0) |
||||
|
for _, column := range columns { |
||||
|
val, ok := one[column] // 读取本行值
|
||||
|
if ok == false { |
||||
|
return errors.New("列名和值无法对应") |
||||
|
} |
||||
|
// 判断是否是时间类型
|
||||
|
if xormColumn, ok := xormColumns[column]; ok == true { |
||||
|
aa[xormColumn.SQLType.Name] = xormColumn.SQLType.Name |
||||
|
if xormColumn.SQLType.IsTime() == true { |
||||
|
isTimeNull := false |
||||
|
if val == nil { |
||||
|
val = "null" |
||||
|
isTimeNull = true |
||||
|
} else { |
||||
|
valTime := val.(time.Time) |
||||
|
// valTime, err := time.ParseInLocation("2006-01-02T15:04:05Z", val.(string), time.UTC)
|
||||
|
if err == nil { |
||||
|
val = valTime.Format("2006-01-02 15:04:05") |
||||
|
} else { |
||||
|
val = "null" |
||||
|
isTimeNull = true |
||||
|
} |
||||
|
} |
||||
|
if isTimeNull == true { |
||||
|
values = append(values, fmt.Sprintf("%v", val)) |
||||
|
} else { |
||||
|
values = append(values, fmt.Sprintf("'%v'", val)) |
||||
|
} |
||||
|
|
||||
|
} else if xormColumn.SQLType.IsBlob() == true { |
||||
|
if val == nil { |
||||
|
val = "false" |
||||
|
} else { |
||||
|
if reflect.TypeOf(val).Kind() == reflect.Slice { |
||||
|
val = md.conn.Dialect().FormatBytes(val.([]byte)) |
||||
|
} else if reflect.TypeOf(val).Kind() == reflect.String { |
||||
|
val = val.(string) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
values = append(values, fmt.Sprintf("%v", val)) |
||||
|
} else if xormColumn.SQLType.IsNumeric() == true { |
||||
|
if val == nil { |
||||
|
val = "null" |
||||
|
} else { |
||||
|
if valByte, ok := val.([]byte); ok == true { |
||||
|
// log.Println(column, "3-1")
|
||||
|
val = string(valByte) |
||||
|
} else { |
||||
|
// log.Println(column, "3-2")
|
||||
|
val = fmt.Sprint(val) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
values = append(values, fmt.Sprintf("%v", val)) |
||||
|
} else { |
||||
|
if val == nil { |
||||
|
val = "" |
||||
|
} else { |
||||
|
if valByte, ok := val.([]byte); ok == true { |
||||
|
// log.Println(column, "3-1")
|
||||
|
val = string(valByte) |
||||
|
} else { |
||||
|
// log.Println(column, "3-2")
|
||||
|
val = fmt.Sprint(val) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
values = append(values, fmt.Sprintf("'%v'", val)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
// 拼接插入语句值部分
|
||||
|
installSQL = fmt.Sprintf("%s (%s);", installSQL, strings.Join(values, ",")) |
||||
|
// 写入数据
|
||||
|
_, err = io.WriteString(w, installSQL) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
var aa map[string]string |
||||
|
|
||||
|
func init() { |
||||
|
aa = make(map[string]string, 0) |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
package utils |
||||
|
|
||||
|
import "errors" |
||||
|
|
||||
|
// Import 导入sql文件到数据库
|
||||
|
func (md *Mysqldump) Import(sqlPath ...string) (err error) { |
||||
|
if md.isClose == true { |
||||
|
return errors.New("已调用Close关闭相关资源,无法进行导入") |
||||
|
} |
||||
|
if len(sqlPath) > 0 { |
||||
|
_, err = md.conn.ImportFile(sqlPath[0]) |
||||
|
} else { |
||||
|
_, err = md.conn.ImportFile(md.cfg.SQLPath) |
||||
|
} |
||||
|
return err |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
package utils |
||||
|
|
||||
|
/* 导出数据时使用到的模型 */ |
||||
|
|
||||
|
// CreateTable 创建table sql查询
|
||||
|
type CreateTable struct { |
||||
|
Table string `xorm:"'Table'"` |
||||
|
CreateTable string `xorm:"'Create Table'"` |
||||
|
} |
||||
|
|
||||
|
// CreateDb 创建数据库 sql查询
|
||||
|
type CreateDb struct { |
||||
|
Database string `xorm:"'Database'"` |
||||
|
CreateDatabase string `xorm:"'Create Database'"` |
||||
|
} |
||||
|
|
||||
|
// TPLSqlModel 导出数据sql部分
|
||||
|
type TPLSqlModel struct { |
||||
|
TableName string // 表名
|
||||
|
CreateSQL string // 创建表sql语句
|
||||
|
InsertSQL string // 插入数据sql语句
|
||||
|
} |
||||
|
|
||||
|
// TPLModel 导出数据结构体
|
||||
|
type TPLModel struct { |
||||
|
MySQL *DbConfig |
||||
|
SQL []*TPLSqlModel |
||||
|
Date string |
||||
|
} |
||||
|
|
||||
|
// TableColumn 用于读取数据库每一列数据
|
||||
|
type TableColumn interface { |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
package utils |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"log" |
||||
|
"os" |
||||
|
"path/filepath" |
||||
|
"time" |
||||
|
|
||||
|
_ "github.com/go-sql-driver/mysql" |
||||
|
"github.com/go-xorm/core" |
||||
|
"github.com/go-xorm/xorm" |
||||
|
) |
||||
|
|
||||
|
// Mysqldump mysql导出数据对象
|
||||
|
type Mysqldump struct { |
||||
|
conn *xorm.Engine |
||||
|
cfg *Config |
||||
|
isClose bool |
||||
|
} |
||||
|
|
||||
|
// New 创建一个Mysqldump对象
|
||||
|
func New(cfg *Config) (*Mysqldump, error) { |
||||
|
if cfg == nil { |
||||
|
return nil, errors.New("配置信息不能为nil") |
||||
|
} |
||||
|
// 处理配置信息
|
||||
|
if cfg.OutPath == "" { |
||||
|
return nil, errors.New("导出sql输出路径不能是空") |
||||
|
} |
||||
|
if cfg.ExportDataStep == 0 { |
||||
|
cfg.ExportDataStep = 1000 |
||||
|
} |
||||
|
|
||||
|
// 创建导出对象
|
||||
|
mysqldump := &Mysqldump{ |
||||
|
cfg: cfg, |
||||
|
isClose: false, |
||||
|
} |
||||
|
// 连接mysql
|
||||
|
err := mysqldump.OpenMysql() |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return mysqldump, nil |
||||
|
} |
||||
|
|
||||
|
// Close 不使用导出功能时,关闭连接资源
|
||||
|
func (md *Mysqldump) Close() error { |
||||
|
md.isClose = true |
||||
|
return md.conn.Close() |
||||
|
} |
||||
|
|
||||
|
// OpenMysql 连接mysql
|
||||
|
func (md *Mysqldump) OpenMysql() error { |
||||
|
// 拼接连接数据库字符串
|
||||
|
connStr := fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8&parseTime=True&loc=UTC", |
||||
|
md.cfg.DbCfg.User, |
||||
|
md.cfg.DbCfg.Passwd, |
||||
|
md.cfg.DbCfg.Address, |
||||
|
md.cfg.DbCfg.Port, |
||||
|
md.cfg.DbCfg.DbName) |
||||
|
|
||||
|
// 连接数据库
|
||||
|
engine, err := xorm.NewEngine("mysql", connStr) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
// 是否开启debug模式
|
||||
|
if md.cfg.Debug { |
||||
|
engine.Logger().SetLevel(core.LOG_DEBUG) // 调试信息
|
||||
|
engine.ShowSQL(true) // 显示sql
|
||||
|
} |
||||
|
engine.SetMaxIdleConns(2) // 空闲连接池数量
|
||||
|
engine.SetMaxOpenConns(8) // 最大连接数
|
||||
|
engine.SetMapper(core.GonicMapper{}) // 命名规则
|
||||
|
|
||||
|
// 设置数据库时区
|
||||
|
engine.DatabaseTZ = time.UTC |
||||
|
engine.TZLocation = time.UTC |
||||
|
|
||||
|
md.conn = engine |
||||
|
|
||||
|
log.Println("连接数据库成功") |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// GetRootDir 获取程序跟目录,返回值尾部包含'/'
|
||||
|
func (md *Mysqldump) GetRootDir() string { |
||||
|
// 文件不存在获取执行路径
|
||||
|
file, err := filepath.Abs(filepath.Dir(os.Args[0])) |
||||
|
if err != nil { |
||||
|
file = fmt.Sprintf(".%s", string(os.PathSeparator)) |
||||
|
} else { |
||||
|
file = fmt.Sprintf("%s%s", file, string(os.PathSeparator)) |
||||
|
} |
||||
|
return file |
||||
|
} |
||||
Loading…
Reference in new issue