package db import ( . "code.hoteas.com/golang/hotime/common" "database/sql" "encoding/json" "errors" "reflect" "strings" ) // md5 生成查询的 MD5 哈希(用于缓存) func (that *HoTimeDB) md5(query string, args ...interface{}) string { strByte, _ := json.Marshal(args) str := Md5(query + ":" + string(strByte)) return str } // Query 执行查询 SQL func (that *HoTimeDB) Query(query string, args ...interface{}) []Map { return that.queryWithRetry(query, false, args...) } // queryWithRetry 内部查询方法,支持重试标记 func (that *HoTimeDB) queryWithRetry(query string, retried bool, args ...interface{}) []Map { // 保存调试信息(加锁保护) that.mu.Lock() that.LastQuery = query that.LastData = args that.mu.Unlock() defer func() { if that.Mode != 0 { that.mu.RLock() that.Log.Info("SQL:"+that.LastQuery, " DATA:", that.LastData, " ERROR:", that.LastErr.GetError()) that.mu.RUnlock() } }() var err error var resl *sql.Rows // 主从数据库切换,只有select语句有从数据库 db := that.DB if that.SlaveDB != nil { db = that.SlaveDB } if db == nil { err = errors.New("没有初始化数据库") that.LastErr.SetError(err) return nil } // 处理参数中的 slice 类型 processedArgs := that.processArgs(args) if that.Tx != nil { resl, err = that.Tx.Query(query, processedArgs...) } else { resl, err = db.Query(query, processedArgs...) } that.LastErr.SetError(err) if err != nil { // 如果还没重试过,尝试 Ping 后重试一次 if !retried { if pingErr := db.Ping(); pingErr == nil { return that.queryWithRetry(query, true, args...) } } return nil } return that.Row(resl) } // Exec 执行非查询 SQL func (that *HoTimeDB) Exec(query string, args ...interface{}) (sql.Result, *Error) { return that.execWithRetry(query, false, args...) } // execWithRetry 内部执行方法,支持重试标记 func (that *HoTimeDB) execWithRetry(query string, retried bool, args ...interface{}) (sql.Result, *Error) { // 保存调试信息(加锁保护) that.mu.Lock() that.LastQuery = query that.LastData = args that.mu.Unlock() defer func() { if that.Mode != 0 { that.mu.RLock() that.Log.Info("SQL: "+that.LastQuery, " DATA: ", that.LastData, " ERROR: ", that.LastErr.GetError()) that.mu.RUnlock() } }() var e error var resl sql.Result if that.DB == nil { err := errors.New("没有初始化数据库") that.LastErr.SetError(err) return nil, that.LastErr } // 处理参数中的 slice 类型 processedArgs := that.processArgs(args) if that.Tx != nil { resl, e = that.Tx.Exec(query, processedArgs...) } else { resl, e = that.DB.Exec(query, processedArgs...) } that.LastErr.SetError(e) // 判断是否连接断开了,如果还没重试过,尝试重试一次 if e != nil { if !retried { if pingErr := that.DB.Ping(); pingErr == nil { return that.execWithRetry(query, true, args...) } } return resl, that.LastErr } return resl, that.LastErr } // processArgs 处理参数中的 slice 类型 func (that *HoTimeDB) processArgs(args []interface{}) []interface{} { processedArgs := make([]interface{}, len(args)) copy(processedArgs, args) for key := range processedArgs { arg := processedArgs[key] if arg == nil { continue } argType := reflect.ValueOf(arg).Type().String() if strings.Contains(argType, "[]") || strings.Contains(argType, "Slice") { argLis := ObjToSlice(arg) // 将slice转为逗号分割字符串 argStr := "" for i := 0; i < len(argLis); i++ { if i == len(argLis)-1 { argStr += ObjToStr(argLis[i]) } else { argStr += ObjToStr(argLis[i]) + "," } } processedArgs[key] = argStr } } return processedArgs } // Row 数据库数据解析 func (that *HoTimeDB) Row(resl *sql.Rows) []Map { dest := make([]Map, 0) strs, _ := resl.Columns() for i := 0; resl.Next(); i++ { lis := make(Map, 0) a := make([]interface{}, len(strs)) b := make([]interface{}, len(a)) for j := 0; j < len(a); j++ { b[j] = &a[j] } err := resl.Scan(b...) if err != nil { that.LastErr.SetError(err) return nil } for j := 0; j < len(a); j++ { if a[j] != nil && reflect.ValueOf(a[j]).Type().String() == "[]uint8" { lis[strs[j]] = string(a[j].([]byte)) } else { lis[strs[j]] = a[j] // 取实际类型 } } dest = append(dest, lis) } return dest }