2021-05-08 18:00:33 +08:00
|
|
|
|
package application
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2021-07-28 18:03:19 +08:00
|
|
|
|
"database/sql"
|
|
|
|
|
|
"fmt"
|
2023-07-26 10:24:32 +08:00
|
|
|
|
"mayfly-go/internal/common/consts"
|
2022-09-09 18:26:08 +08:00
|
|
|
|
"mayfly-go/internal/db/domain/entity"
|
|
|
|
|
|
"mayfly-go/internal/db/domain/repository"
|
|
|
|
|
|
"mayfly-go/internal/machine/infrastructure/machine"
|
2022-06-02 17:41:11 +08:00
|
|
|
|
"mayfly-go/pkg/biz"
|
|
|
|
|
|
"mayfly-go/pkg/cache"
|
2023-09-02 17:24:18 +08:00
|
|
|
|
"mayfly-go/pkg/logx"
|
2022-06-02 17:41:11 +08:00
|
|
|
|
"mayfly-go/pkg/model"
|
2023-07-21 17:07:04 +08:00
|
|
|
|
"mayfly-go/pkg/utils/collx"
|
2022-06-16 15:55:18 +08:00
|
|
|
|
"reflect"
|
2022-03-24 17:50:44 +08:00
|
|
|
|
"strconv"
|
2021-07-28 18:03:19 +08:00
|
|
|
|
"strings"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
"time"
|
2021-05-08 18:00:33 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
type Db interface {
|
2022-06-16 15:55:18 +08:00
|
|
|
|
// 分页获取
|
2023-07-01 14:34:42 +08:00
|
|
|
|
GetPageList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any]
|
2021-05-08 18:00:33 +08:00
|
|
|
|
|
2022-10-26 20:49:29 +08:00
|
|
|
|
Count(condition *entity.DbQuery) int64
|
2021-09-11 14:04:09 +08:00
|
|
|
|
|
2021-05-08 18:00:33 +08:00
|
|
|
|
// 根据条件获取
|
|
|
|
|
|
GetDbBy(condition *entity.Db, cols ...string) error
|
|
|
|
|
|
|
|
|
|
|
|
// 根据id获取
|
|
|
|
|
|
GetById(id uint64, cols ...string) *entity.Db
|
|
|
|
|
|
|
|
|
|
|
|
Save(entity *entity.Db)
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 删除数据库信息
|
|
|
|
|
|
Delete(id uint64)
|
|
|
|
|
|
|
|
|
|
|
|
// 获取数据库连接实例
|
2022-05-08 14:10:57 +08:00
|
|
|
|
// @param id 数据库实例id
|
|
|
|
|
|
// @param db 数据库
|
2023-09-05 14:41:12 +08:00
|
|
|
|
GetDbConnection(dbId uint64, dbName string) *DbConnection
|
2021-05-08 18:00:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-05 14:41:12 +08:00
|
|
|
|
func newDbApp(dbRepo repository.Db, dbSqlRepo repository.DbSql, dbInstanceApp Instance) Db {
|
2022-09-09 18:26:08 +08:00
|
|
|
|
return &dbAppImpl{
|
2023-09-05 14:41:12 +08:00
|
|
|
|
dbRepo: dbRepo,
|
|
|
|
|
|
dbSqlRepo: dbSqlRepo,
|
|
|
|
|
|
dbInstanceApp: dbInstanceApp,
|
2022-09-09 18:26:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
type dbAppImpl struct {
|
2023-09-05 14:41:12 +08:00
|
|
|
|
dbRepo repository.Db
|
|
|
|
|
|
dbSqlRepo repository.DbSql
|
|
|
|
|
|
dbInstanceApp Instance
|
2021-05-08 18:00:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 分页获取数据库信息列表
|
2023-07-01 14:34:42 +08:00
|
|
|
|
func (d *dbAppImpl) GetPageList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] {
|
2021-05-08 18:00:33 +08:00
|
|
|
|
return d.dbRepo.GetDbList(condition, pageParam, toEntity, orderBy...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-10-26 20:49:29 +08:00
|
|
|
|
func (d *dbAppImpl) Count(condition *entity.DbQuery) int64 {
|
2021-09-11 14:04:09 +08:00
|
|
|
|
return d.dbRepo.Count(condition)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-08 18:00:33 +08:00
|
|
|
|
// 根据条件获取
|
2021-07-28 18:03:19 +08:00
|
|
|
|
func (d *dbAppImpl) GetDbBy(condition *entity.Db, cols ...string) error {
|
2021-05-08 18:00:33 +08:00
|
|
|
|
return d.dbRepo.GetDb(condition, cols...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据id获取
|
2021-07-28 18:03:19 +08:00
|
|
|
|
func (d *dbAppImpl) GetById(id uint64, cols ...string) *entity.Db {
|
2021-05-08 18:00:33 +08:00
|
|
|
|
return d.dbRepo.GetById(id, cols...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
func (d *dbAppImpl) Save(dbEntity *entity.Db) {
|
|
|
|
|
|
// 查找是否存在该库
|
2023-08-30 22:41:42 +08:00
|
|
|
|
oldDb := &entity.Db{Name: dbEntity.Name}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
err := d.GetDbBy(oldDb)
|
|
|
|
|
|
|
|
|
|
|
|
if dbEntity.Id == 0 {
|
2023-08-30 22:41:42 +08:00
|
|
|
|
biz.IsTrue(err != nil, "该数据库资源已存在")
|
2021-07-28 18:03:19 +08:00
|
|
|
|
d.dbRepo.Insert(dbEntity)
|
2022-05-08 14:10:57 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果存在该库,则校验修改的库是否为该库
|
|
|
|
|
|
if err == nil {
|
2023-08-30 22:41:42 +08:00
|
|
|
|
biz.IsTrue(oldDb.Id == dbEntity.Id, "该数据库资源已存在")
|
2022-05-08 14:10:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbId := dbEntity.Id
|
|
|
|
|
|
old := d.GetById(dbId)
|
|
|
|
|
|
|
2023-06-01 12:31:32 +08:00
|
|
|
|
var oldDbs []any
|
2022-05-08 14:10:57 +08:00
|
|
|
|
for _, v := range strings.Split(old.Database, " ") {
|
2022-07-20 23:25:52 +08:00
|
|
|
|
// 关闭数据库连接
|
|
|
|
|
|
CloseDb(dbEntity.Id, v)
|
2022-05-08 14:10:57 +08:00
|
|
|
|
oldDbs = append(oldDbs, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-01 12:31:32 +08:00
|
|
|
|
var newDbs []any
|
2022-05-08 14:10:57 +08:00
|
|
|
|
for _, v := range strings.Split(dbEntity.Database, " ") {
|
|
|
|
|
|
newDbs = append(newDbs, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
// 比较新旧数据库列表,需要将移除的数据库相关联的信息删除
|
2023-07-21 17:07:04 +08:00
|
|
|
|
_, delDb, _ := collx.ArrayCompare(newDbs, oldDbs, func(i1, i2 any) bool {
|
2022-05-08 14:10:57 +08:00
|
|
|
|
return i1.(string) == i2.(string)
|
|
|
|
|
|
})
|
|
|
|
|
|
for _, v := range delDb {
|
|
|
|
|
|
// 删除该库关联的所有sql记录
|
|
|
|
|
|
d.dbSqlRepo.DeleteBy(&entity.DbSql{DbId: dbId, Db: v.(string)})
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
2022-05-08 14:10:57 +08:00
|
|
|
|
|
|
|
|
|
|
d.dbRepo.Update(dbEntity)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (d *dbAppImpl) Delete(id uint64) {
|
2022-05-08 14:10:57 +08:00
|
|
|
|
db := d.GetById(id)
|
|
|
|
|
|
dbs := strings.Split(db.Database, " ")
|
|
|
|
|
|
for _, v := range dbs {
|
|
|
|
|
|
// 关闭连接
|
|
|
|
|
|
CloseDb(id, v)
|
|
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
d.dbRepo.Delete(id)
|
|
|
|
|
|
// 删除该库下用户保存的所有sql信息
|
|
|
|
|
|
d.dbSqlRepo.DeleteBy(&entity.DbSql{DbId: id})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-18 17:57:33 +08:00
|
|
|
|
var mutex sync.Mutex
|
|
|
|
|
|
|
2023-09-05 14:41:12 +08:00
|
|
|
|
func (d *dbAppImpl) GetDbConnection(dbId uint64, dbName string) *DbConnection {
|
|
|
|
|
|
cacheKey := GetDbCacheKey(dbId, dbName)
|
2022-11-18 17:52:30 +08:00
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// Id不为0,则为需要缓存
|
2023-09-05 14:41:12 +08:00
|
|
|
|
needCache := dbId != 0
|
2021-07-28 18:03:19 +08:00
|
|
|
|
if needCache {
|
2022-11-18 17:52:30 +08:00
|
|
|
|
load, ok := dbCache.Get(cacheKey)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
if ok {
|
2023-09-05 08:13:32 +08:00
|
|
|
|
return load.(*DbConnection)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-07-26 18:32:45 +08:00
|
|
|
|
mutex.Lock()
|
2022-07-23 16:41:04 +08:00
|
|
|
|
defer mutex.Unlock()
|
2021-08-18 17:57:33 +08:00
|
|
|
|
|
2023-09-05 14:41:12 +08:00
|
|
|
|
db := d.GetById(dbId)
|
2023-08-30 22:41:42 +08:00
|
|
|
|
biz.NotNil(db, "数据库信息不存在")
|
2023-09-05 15:22:16 +08:00
|
|
|
|
biz.IsTrue(strings.Contains(" "+db.Database+" ", " "+dbName+" "), "未配置数据库【%s】的操作权限", dbName)
|
2023-08-30 22:41:42 +08:00
|
|
|
|
|
2023-09-05 14:41:12 +08:00
|
|
|
|
instance := d.dbInstanceApp.GetById(db.InstanceId)
|
2022-12-22 18:41:34 +08:00
|
|
|
|
// 密码解密
|
2023-08-30 22:41:42 +08:00
|
|
|
|
instance.PwdDecrypt()
|
2022-07-20 23:25:52 +08:00
|
|
|
|
|
2023-08-30 22:41:42 +08:00
|
|
|
|
dbInfo := NewDbInfo(db, instance)
|
|
|
|
|
|
dbInfo.Database = dbName
|
2023-09-05 08:13:32 +08:00
|
|
|
|
dbi := &DbConnection{Id: cacheKey, Info: dbInfo}
|
2021-08-18 17:57:33 +08:00
|
|
|
|
|
2023-08-30 22:41:42 +08:00
|
|
|
|
conn, err := getInstanceConn(instance, dbName)
|
2022-07-20 23:25:52 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
dbi.Close()
|
2023-08-30 22:41:42 +08:00
|
|
|
|
logx.Errorf("连接db失败: %s:%d/%s", dbInfo.Host, dbInfo.Port, dbName)
|
2022-07-20 23:25:52 +08:00
|
|
|
|
panic(biz.NewBizErr(fmt.Sprintf("数据库连接失败: %s", err.Error())))
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 最大连接周期,超过时间的连接就close
|
2023-08-30 22:41:42 +08:00
|
|
|
|
// conn.SetConnMaxLifetime(100 * time.Second)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 设置最大连接数
|
2023-08-30 22:41:42 +08:00
|
|
|
|
conn.SetMaxOpenConns(5)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 设置闲置连接数
|
2023-08-30 22:41:42 +08:00
|
|
|
|
conn.SetMaxIdleConns(1)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2023-08-30 22:41:42 +08:00
|
|
|
|
dbi.db = conn
|
|
|
|
|
|
logx.Infof("连接db: %s:%d/%s", dbInfo.Host, dbInfo.Port, dbName)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
if needCache {
|
2022-05-08 14:10:57 +08:00
|
|
|
|
dbCache.Put(cacheKey, dbi)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
return dbi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-10-16 14:22:19 +08:00
|
|
|
|
//---------------------------------------- db instance ------------------------------------
|
|
|
|
|
|
|
2022-11-18 17:52:30 +08:00
|
|
|
|
type DbInfo struct {
|
|
|
|
|
|
Id uint64
|
|
|
|
|
|
Name string
|
|
|
|
|
|
Type string // 类型,mysql oracle等
|
|
|
|
|
|
Host string
|
|
|
|
|
|
Port int
|
|
|
|
|
|
Network string
|
|
|
|
|
|
Username string
|
|
|
|
|
|
TagPath string
|
|
|
|
|
|
Database string
|
2023-03-06 16:59:57 +08:00
|
|
|
|
SshTunnelMachineId int
|
2022-11-18 17:52:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-30 22:41:42 +08:00
|
|
|
|
func NewDbInfo(db *entity.Db, instance *entity.Instance) *DbInfo {
|
|
|
|
|
|
return &DbInfo{
|
|
|
|
|
|
Id: db.Id,
|
|
|
|
|
|
Name: db.Name,
|
|
|
|
|
|
Type: instance.Type,
|
|
|
|
|
|
Host: instance.Host,
|
|
|
|
|
|
Port: instance.Port,
|
|
|
|
|
|
Username: instance.Username,
|
|
|
|
|
|
TagPath: db.TagPath,
|
|
|
|
|
|
SshTunnelMachineId: instance.SshTunnelMachineId,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-11-18 17:52:30 +08:00
|
|
|
|
// 获取记录日志的描述
|
|
|
|
|
|
func (d *DbInfo) GetLogDesc() string {
|
|
|
|
|
|
return fmt.Sprintf("DB[id=%d, tag=%s, name=%s, ip=%s:%d, database=%s]", d.Id, d.TagPath, d.Name, d.Host, d.Port, d.Database)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-05 14:41:12 +08:00
|
|
|
|
// db实例连接信息
|
2023-09-05 08:13:32 +08:00
|
|
|
|
type DbConnection struct {
|
2022-11-18 17:52:30 +08:00
|
|
|
|
Id string
|
|
|
|
|
|
Info *DbInfo
|
|
|
|
|
|
|
|
|
|
|
|
db *sql.DB
|
2022-10-16 14:22:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 执行查询语句
|
|
|
|
|
|
// 依次返回 列名数组,结果map,错误
|
2023-09-05 08:13:32 +08:00
|
|
|
|
func (d *DbConnection) SelectData(execSql string) ([]string, []map[string]any, error) {
|
2023-09-07 11:15:11 +08:00
|
|
|
|
return selectDataByDb(d.db, execSql)
|
2022-10-16 14:22:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 将查询结果映射至struct,可具体参考sqlx库
|
2023-09-05 08:13:32 +08:00
|
|
|
|
func (d *DbConnection) SelectData2Struct(execSql string, dest any) error {
|
2023-09-07 11:15:11 +08:00
|
|
|
|
return select2StructByDb(d.db, execSql, dest)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// WalkTableRecord 遍历表记录
|
|
|
|
|
|
func (d *DbConnection) WalkTableRecord(selectSql string, walk func(record map[string]any, columns []string)) error {
|
|
|
|
|
|
return walkTableRecord(d.db, selectSql, walk)
|
2022-10-16 14:22:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 执行 update, insert, delete,建表等sql
|
|
|
|
|
|
// 返回影响条数和错误
|
2023-09-05 08:13:32 +08:00
|
|
|
|
func (d *DbConnection) Exec(sql string) (int64, error) {
|
2022-10-16 14:22:19 +08:00
|
|
|
|
res, err := d.db.Exec(sql)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return 0, err
|
|
|
|
|
|
}
|
|
|
|
|
|
return res.RowsAffected()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取数据库元信息实现接口
|
2023-09-07 11:15:11 +08:00
|
|
|
|
func (d *DbConnection) GetMeta() DbMetadata {
|
|
|
|
|
|
dbType := d.Info.Type
|
2022-10-16 14:22:19 +08:00
|
|
|
|
if dbType == entity.DbTypeMysql {
|
2023-09-07 11:15:11 +08:00
|
|
|
|
return &MysqlMetadata{di: d}
|
2022-10-16 14:22:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
if dbType == entity.DbTypePostgres {
|
2023-09-07 11:15:11 +08:00
|
|
|
|
return &PgsqlMetadata{di: d}
|
2022-10-16 14:22:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭连接
|
2023-09-05 08:13:32 +08:00
|
|
|
|
func (d *DbConnection) Close() {
|
2022-10-16 14:22:19 +08:00
|
|
|
|
if d.db != nil {
|
|
|
|
|
|
if err := d.db.Close(); err != nil {
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Errorf("关闭数据库实例[%s]连接失败: %s", d.Id, err.Error())
|
2022-10-16 14:22:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
d.db = nil
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
//------------------------------------------------------------------------------
|
2021-05-08 18:00:33 +08:00
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
// 客户端连接缓存,指定时间内没有访问则会被关闭, key为数据库实例id:数据库
|
2023-07-26 10:24:32 +08:00
|
|
|
|
var dbCache = cache.NewTimedCache(consts.DbConnExpireTime, 5*time.Second).
|
2021-08-18 17:57:33 +08:00
|
|
|
|
WithUpdateAccessTime(true).
|
2023-06-01 12:31:32 +08:00
|
|
|
|
OnEvicted(func(key any, value any) {
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Info(fmt.Sprintf("删除db连接缓存 id = %s", key))
|
2023-09-05 08:13:32 +08:00
|
|
|
|
value.(*DbConnection).Close()
|
2021-08-18 17:57:33 +08:00
|
|
|
|
})
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
func init() {
|
2023-03-06 16:59:57 +08:00
|
|
|
|
machine.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
|
2022-09-22 11:56:21 +08:00
|
|
|
|
// 遍历所有db连接实例,若存在db实例使用该ssh隧道机器,则返回true,表示还在使用中...
|
2022-07-23 16:41:04 +08:00
|
|
|
|
items := dbCache.Items()
|
|
|
|
|
|
for _, v := range items {
|
2023-09-05 08:13:32 +08:00
|
|
|
|
if v.Value.(*DbConnection).Info.SshTunnelMachineId == machineId {
|
2022-07-23 16:41:04 +08:00
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-08 14:10:57 +08:00
|
|
|
|
func GetDbCacheKey(dbId uint64, db string) string {
|
|
|
|
|
|
return fmt.Sprintf("%d:%s", dbId, db)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-07 11:15:11 +08:00
|
|
|
|
func selectDataByDb(db *sql.DB, selectSql string) ([]string, []map[string]any, error) {
|
|
|
|
|
|
// 列名用于前端表头名称按照数据库与查询字段顺序显示
|
|
|
|
|
|
var colNames []string
|
|
|
|
|
|
result := make([]map[string]any, 0, 16)
|
|
|
|
|
|
err := walkTableRecord(db, selectSql, func(record map[string]any, columns []string) {
|
|
|
|
|
|
result = append(result, record)
|
|
|
|
|
|
if colNames == nil {
|
|
|
|
|
|
colNames = make([]string, 0, len(columns))
|
|
|
|
|
|
copy(colNames, columns)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2021-07-28 18:03:19 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
}
|
2023-09-07 11:15:11 +08:00
|
|
|
|
return colNames, result, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func walkTableRecord(db *sql.DB, selectSql string, walk func(record map[string]any, columns []string)) error {
|
|
|
|
|
|
tx, err := db.Begin()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
|
|
|
|
|
|
|
rows, err := tx.Query(selectSql)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// rows对象一定要close掉,如果出错,不关掉则会很迅速的达到设置最大连接数,
|
|
|
|
|
|
// 后面的链接过来直接报错或拒绝,实际上也没有起效果
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if rows != nil {
|
|
|
|
|
|
rows.Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
2023-09-07 11:15:11 +08:00
|
|
|
|
|
|
|
|
|
|
colTypes, err := rows.ColumnTypes()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
lenCols := len(colTypes)
|
|
|
|
|
|
// 列名用于前端表头名称按照数据库与查询字段顺序显示
|
|
|
|
|
|
colNames := make([]string, lenCols)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 这里表示一行填充数据
|
2023-09-07 11:15:11 +08:00
|
|
|
|
scans := make([]any, lenCols)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 这里表示一行所有列的值,用[]byte表示
|
2023-09-07 11:15:11 +08:00
|
|
|
|
values := make([][]byte, lenCols)
|
|
|
|
|
|
for k, colType := range colTypes {
|
|
|
|
|
|
colNames[k] = colType.Name()
|
|
|
|
|
|
// 这里scans引用values,把数据填充到[]byte里
|
|
|
|
|
|
scans[k] = &values[k]
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
|
// 不Scan也会导致等待,该链接实际处于未工作的状态,然后也会导致连接数迅速达到最大
|
2023-09-07 11:15:11 +08:00
|
|
|
|
if err := rows.Scan(scans...); err != nil {
|
|
|
|
|
|
return err
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 每行数据
|
2023-09-07 11:15:11 +08:00
|
|
|
|
rowData := make(map[string]any, lenCols)
|
|
|
|
|
|
// 把values中的数据复制到row中
|
|
|
|
|
|
for i, v := range values {
|
|
|
|
|
|
rowData[colTypes[i].Name()] = valueConvert(v, colTypes[i])
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
2023-09-07 11:15:11 +08:00
|
|
|
|
walk(rowData, colNames)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
2023-09-07 11:15:11 +08:00
|
|
|
|
|
|
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-28 21:40:59 +08:00
|
|
|
|
// 将查询的值转为对应列类型的实际值,不全部转为字符串
|
2023-06-01 12:31:32 +08:00
|
|
|
|
func valueConvert(data []byte, colType *sql.ColumnType) any {
|
2022-09-28 21:40:59 +08:00
|
|
|
|
if data == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
// 列的数据库类型名
|
|
|
|
|
|
colDatabaseTypeName := strings.ToLower(colType.DatabaseTypeName())
|
|
|
|
|
|
|
|
|
|
|
|
// 如果类型是bit,则直接返回第一个字节即可
|
|
|
|
|
|
if strings.Contains(colDatabaseTypeName, "bit") {
|
|
|
|
|
|
return data[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 这里把[]byte数据转成string
|
|
|
|
|
|
stringV := string(data)
|
|
|
|
|
|
if stringV == "" {
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
colScanType := strings.ToLower(colType.ScanType().Name())
|
|
|
|
|
|
|
|
|
|
|
|
if strings.Contains(colScanType, "int") {
|
|
|
|
|
|
// 如果长度超过16位,则返回字符串,因为前端js长度大于16会丢失精度
|
|
|
|
|
|
if len(stringV) > 16 {
|
|
|
|
|
|
return stringV
|
|
|
|
|
|
}
|
|
|
|
|
|
intV, _ := strconv.Atoi(stringV)
|
|
|
|
|
|
switch colType.ScanType().Kind() {
|
|
|
|
|
|
case reflect.Int8:
|
|
|
|
|
|
return int8(intV)
|
|
|
|
|
|
case reflect.Uint8:
|
|
|
|
|
|
return uint8(intV)
|
|
|
|
|
|
case reflect.Int64:
|
|
|
|
|
|
return int64(intV)
|
|
|
|
|
|
case reflect.Uint64:
|
|
|
|
|
|
return uint64(intV)
|
|
|
|
|
|
case reflect.Uint:
|
|
|
|
|
|
return uint(intV)
|
|
|
|
|
|
default:
|
|
|
|
|
|
return intV
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-02-06 17:14:16 +08:00
|
|
|
|
if strings.Contains(colScanType, "float") || strings.Contains(colDatabaseTypeName, "decimal") {
|
2022-09-28 21:40:59 +08:00
|
|
|
|
floatV, _ := strconv.ParseFloat(stringV, 64)
|
|
|
|
|
|
return floatV
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return stringV
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-10-16 14:22:19 +08:00
|
|
|
|
// 查询数据结果映射至struct。可参考sqlx库
|
2023-09-07 11:15:11 +08:00
|
|
|
|
func select2StructByDb(db *sql.DB, selectSql string, dest any) error {
|
2022-10-16 11:01:45 +08:00
|
|
|
|
rows, err := db.Query(selectSql)
|
|
|
|
|
|
if err != nil {
|
2022-10-16 14:22:19 +08:00
|
|
|
|
return err
|
2022-10-16 11:01:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
// rows对象一定要close掉,如果出错,不关掉则会很迅速的达到设置最大连接数,
|
|
|
|
|
|
// 后面的链接过来直接报错或拒绝,实际上也没有起效果
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if rows != nil {
|
|
|
|
|
|
rows.Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
2022-10-16 14:22:19 +08:00
|
|
|
|
return scanAll(rows, dest, false)
|
|
|
|
|
|
}
|
2022-10-16 11:01:45 +08:00
|
|
|
|
|
2022-10-16 14:22:19 +08:00
|
|
|
|
// 删除db缓存并关闭该数据库所有连接
|
|
|
|
|
|
func CloseDb(dbId uint64, db string) {
|
|
|
|
|
|
dbCache.Delete(GetDbCacheKey(dbId, db))
|
2022-10-16 11:01:45 +08:00
|
|
|
|
}
|