gf/g/database/gdb/gdb.go

369 lines
12 KiB
Go
Raw Normal View History

// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
2017-12-29 16:03:30 +08:00
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
2017-12-31 18:19:58 +08:00
2019-01-15 23:27:47 +08:00
// Package gdb provides ORM features for popular relationship databases.
2019-01-16 13:35:16 +08:00
//
// 数据库ORM,
2018-08-08 20:09:52 +08:00
// 默认内置支持MySQL, 其他数据库需要手动import对应的数据库引擎第三方包.
2017-11-23 10:21:28 +08:00
package gdb
import (
2019-06-19 09:06:52 +08:00
"database/sql"
"errors"
"fmt"
"time"
2019-06-19 09:06:52 +08:00
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gring"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/util/grand"
2017-11-23 10:21:28 +08:00
)
// 数据库操作接口
2018-12-14 18:35:51 +08:00
type DB interface {
2019-06-19 09:06:52 +08:00
// 建立数据库连接方法(开发者一般不需要直接调用)
Open(config *ConfigNode) (*sql.DB, error)
2018-12-17 19:26:59 +08:00
// SQL操作方法 API
2018-12-14 18:35:51 +08:00
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
2019-06-19 09:06:52 +08:00
Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error)
2018-08-08 09:09:28 +08:00
2019-06-19 09:06:52 +08:00
// 内部实现API的方法(不同数据库可覆盖这些方法实现自定义的操作)
doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error)
doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error)
doPrepare(link dbLink, query string) (*sql.Stmt, error)
doInsert(link dbLink, table string, data interface{}, option int, batch ...int) (result sql.Result, err error)
doBatchInsert(link dbLink, table string, list interface{}, option int, batch ...int) (result sql.Result, err error)
doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
doDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error)
2018-08-08 09:09:28 +08:00
// 数据库查询
2018-12-14 18:35:51 +08:00
GetAll(query string, args ...interface{}) (Result, error)
GetOne(query string, args ...interface{}) (Record, error)
GetValue(query string, args ...interface{}) (Value, error)
2019-06-19 09:06:52 +08:00
GetCount(query string, args ...interface{}) (int, error)
GetStruct(objPointer interface{}, query string, args ...interface{}) error
GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error
GetScan(objPointer interface{}, query string, args ...interface{}) error
2019-06-19 09:06:52 +08:00
// 创建底层数据库master/slave链接对象
Master() (*sql.DB, error)
Slave() (*sql.DB, error)
2018-08-08 09:09:28 +08:00
2019-06-19 09:06:52 +08:00
// Ping
2018-08-08 09:09:28 +08:00
PingMaster() error
PingSlave() error
// 开启事务操作
Begin() (*TX, error)
2018-08-08 09:09:28 +08:00
// 数据表插入/更新/保存操作
2019-06-19 09:06:52 +08:00
Insert(table string, data interface{}, batch ...int) (sql.Result, error)
Replace(table string, data interface{}, batch ...int) (sql.Result, error)
Save(table string, data interface{}, batch ...int) (sql.Result, error)
2018-08-08 09:09:28 +08:00
// 数据表插入/更新/保存操作(批量)
2019-06-19 09:06:52 +08:00
BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error)
BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error)
BatchSave(table string, list interface{}, batch ...int) (sql.Result, error)
2018-08-08 09:09:28 +08:00
// 数据修改/删除
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
2019-07-04 19:22:11 +08:00
// 创建链式操作对象
2018-08-08 09:09:28 +08:00
From(tables string) *Model
2019-07-04 19:22:11 +08:00
Table(tables string) *Model
2018-08-08 09:09:28 +08:00
2018-12-14 18:35:51 +08:00
// 设置管理
2019-06-19 09:06:52 +08:00
SetDebug(debug bool)
SetSchema(schema string)
GetQueriedSqls() []*Sql
2019-06-01 20:31:29 +08:00
GetLastSql() *Sql
2019-06-19 09:06:52 +08:00
PrintQueriedSqls()
SetMaxIdleConnCount(n int)
SetMaxOpenConnCount(n int)
SetMaxConnLifetime(n int)
2018-12-14 18:35:51 +08:00
// 内部方法接口
2019-06-19 09:06:52 +08:00
getCache() *gcache.Cache
2018-12-14 18:35:51 +08:00
getChars() (charLeft string, charRight string)
getDebug() bool
setSchema(sqlDb *sql.DB, schema string) error
2019-06-19 09:06:52 +08:00
filterFields(table string, data map[string]interface{}) map[string]interface{}
2019-07-22 15:10:40 +08:00
convertValue(fieldValue []byte, fieldType string) interface{}
2019-06-19 09:06:52 +08:00
getTableFields(table string) (map[string]string, error)
rowsToResult(rows *sql.Rows) (Result, error)
handleSqlBeforeExec(sql string) string
2017-11-23 10:21:28 +08:00
}
// 执行底层数据库操作的核心接口
type dbLink interface {
2019-06-19 09:06:52 +08:00
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string) (*sql.Stmt, error)
}
2017-11-23 10:21:28 +08:00
// 数据库链接对象
2018-12-14 18:35:51 +08:00
type dbBase struct {
db DB // 数据库对象
group string // 配置分组名称
debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性
sqls *gring.Ring // (debug=true时有效)已执行的SQL列表
cache *gcache.Cache // 数据库缓存,包括底层连接池对象缓存及查询缓存;需要注意的是,事务查询不支持查询缓存
2019-06-19 09:06:52 +08:00
schema *gtype.String // 手动切换的数据库名称
tables map[string]map[string]string // 数据库表结构
maxIdleConnCount *gtype.Int // 连接池最大限制的连接数
2019-06-19 09:06:52 +08:00
maxOpenConnCount *gtype.Int // 连接池最大打开的连接数
maxConnLifetime *gtype.Int // (单位秒)连接对象可重复使用的时间长度
}
// 执行的SQL对象
type Sql struct {
2018-08-08 09:09:28 +08:00
Sql string // SQL语句(可能带有预处理占位符)
Args []interface{} // 预处理参数值列表
Error error // 执行结果(nil为成功)
Start int64 // 执行开始时间(毫秒)
End int64 // 执行结束时间(毫秒)
2018-12-14 18:35:51 +08:00
Func string // 执行方法
2017-11-23 10:21:28 +08:00
}
// 返回数据表记录值
type Value = *gvar.Var
// 返回数据表记录Map
2018-08-08 09:09:28 +08:00
type Record map[string]Value
// 返回数据表记录List
2018-08-08 09:09:28 +08:00
type Result []Record
2017-11-23 10:21:28 +08:00
// 关联数组,绑定一条数据表记录(使用别名)
2019-06-19 09:06:52 +08:00
type Map = map[string]interface{}
// 关联数组列表(索引从0开始的数组),绑定多条记录(使用别名)
2018-08-08 09:09:28 +08:00
type List = []Map
2017-11-23 10:21:28 +08:00
const (
2019-06-19 09:06:52 +08:00
OPTION_INSERT = 0
OPTION_REPLACE = 1
OPTION_SAVE = 2
OPTION_IGNORE = 3
gDEFAULT_BATCH_NUM = 10 // Per count for batch insert/replace/save
gDEFAULT_CONN_MAX_LIFE_TIME = 30 // Max life time for per connection in pool.
)
var (
2019-06-19 09:06:52 +08:00
// Instance map.
instances = gmap.NewStrAnyMap()
)
// New creates ORM DB object with global configurations.
2019-06-11 20:57:43 +08:00
// The parameter <name> specifies the configuration group name,
// which is DEFAULT_GROUP_NAME in default.
func New(name ...string) (db DB, err error) {
group := configs.defaultGroup
if len(name) > 0 {
2019-06-19 09:06:52 +08:00
group = name[0]
2018-08-08 09:09:28 +08:00
}
configs.RLock()
defer configs.RUnlock()
2018-08-08 09:09:28 +08:00
if len(configs.config) < 1 {
2018-08-08 09:09:28 +08:00
return nil, errors.New("empty database configuration")
}
if _, ok := configs.config[group]; ok {
2019-06-19 09:06:52 +08:00
if node, err := getConfigNodeByGroup(group, true); err == nil {
base := &dbBase{
group: group,
debug: gtype.NewBool(),
cache: gcache.New(),
schema: gtype.NewString(),
maxIdleConnCount: gtype.NewInt(),
maxOpenConnCount: gtype.NewInt(),
maxConnLifetime: gtype.NewInt(gDEFAULT_CONN_MAX_LIFE_TIME),
}
switch node.Type {
case "mysql":
base.db = &dbMysql{dbBase: base}
case "pgsql":
base.db = &dbPgsql{dbBase: base}
case "mssql":
base.db = &dbMssql{dbBase: base}
case "sqlite":
base.db = &dbSqlite{dbBase: base}
case "oracle":
base.db = &dbOracle{dbBase: base}
default:
return nil, errors.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type))
}
return base.db, nil
} else {
return nil, err
}
2018-08-08 09:09:28 +08:00
} else {
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
2018-08-08 09:09:28 +08:00
}
2017-11-23 10:21:28 +08:00
}
// Instance returns an instance for DB operations.
2019-06-11 20:57:43 +08:00
// The parameter <name> specifies the configuration group name,
// which is DEFAULT_GROUP_NAME in default.
func Instance(name ...string) (db DB, err error) {
2019-06-19 09:06:52 +08:00
group := configs.defaultGroup
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
db, err = New(group)
return db
})
if v != nil {
return v.(DB), nil
}
return
}
// 获取指定数据库角色的一个配置项,内部根据权重计算负载均衡
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
2019-06-19 09:06:52 +08:00
if list, ok := configs.config[group]; ok {
// 将master, slave集群列表拆分出来
masterList := make(ConfigGroup, 0)
slaveList := make(ConfigGroup, 0)
for i := 0; i < len(list); i++ {
if list[i].Role == "slave" {
slaveList = append(slaveList, list[i])
} else {
masterList = append(masterList, list[i])
}
}
if len(masterList) < 1 {
return nil, errors.New("at least one master node configuration's need to make sense")
}
if len(slaveList) < 1 {
slaveList = masterList
}
if master {
2019-07-06 16:14:45 +08:00
return getConfigNodeByWeight(masterList), nil
2019-06-19 09:06:52 +08:00
} else {
2019-07-06 16:14:45 +08:00
return getConfigNodeByWeight(slaveList), nil
2019-06-19 09:06:52 +08:00
}
} else {
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
}
}
2017-11-23 10:21:28 +08:00
// 按照负载均衡算法(优先级配置)从数据库集群中选择一个配置节点出来使用
// 算法说明举例,
// 1、假如2个节点的priority都是1那么随机大小范围为[0, 199]
// 2、那么节点1的权重范围为[0, 99]节点2的权重范围为[100, 199]比例为1:1
// 3、假如计算出的随机数为99;
// 4、那么选择的配置为节点1;
2019-07-06 16:14:45 +08:00
func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
2018-08-08 09:09:28 +08:00
if len(cg) < 2 {
return &cg[0]
}
var total int
for i := 0; i < len(cg); i++ {
2019-07-06 16:14:45 +08:00
total += cg[i].Weight * 100
2018-08-08 09:09:28 +08:00
}
// 如果total为0表示所有连接都没有配置priority属性那么默认都是1
if total == 0 {
2019-06-19 09:06:52 +08:00
for i := 0; i < len(cg); i++ {
2019-07-06 16:14:45 +08:00
cg[i].Weight = 1
total += cg[i].Weight * 100
2019-06-19 09:06:52 +08:00
}
}
2018-08-08 09:09:28 +08:00
// 不能取到末尾的边界点
r := grand.Rand(0, total)
if r > 0 {
r -= 1
}
min := 0
max := 0
for i := 0; i < len(cg); i++ {
2019-07-06 16:14:45 +08:00
max = min + cg[i].Weight*100
2018-08-08 09:09:28 +08:00
//fmt.Printf("r: %d, min: %d, max: %d\n", r, min, max)
if r >= min && r < max {
return &cg[i]
} else {
min = max
}
}
return nil
2017-11-23 10:21:28 +08:00
}
// 获得底层数据库链接对象
func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
2019-06-19 09:06:52 +08:00
// 负载均衡
node, err := getConfigNodeByGroup(bs.group, master)
if err != nil {
return nil, err
}
// 默认值设定
if node.Charset == "" {
node.Charset = "utf8"
}
// 缓存连接对象(该对象其实是一个连接池对象)
2019-06-19 09:06:52 +08:00
v := bs.cache.GetOrSetFuncLock(node.String(), func() interface{} {
sqlDb, err = bs.db.Open(node)
if err != nil {
return nil
}
// 接口对象可能会覆盖这些连接参数,所以这里优先判断有误设置连接池属性。
// 若无设置则使用配置节点的连接池参数
2019-06-19 09:06:52 +08:00
if n := bs.maxIdleConnCount.Val(); n > 0 {
sqlDb.SetMaxIdleConns(n)
} else if node.MaxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(node.MaxIdleConnCount)
}
if n := bs.maxOpenConnCount.Val(); n > 0 {
sqlDb.SetMaxOpenConns(n)
} else if node.MaxOpenConnCount > 0 {
sqlDb.SetMaxOpenConns(node.MaxOpenConnCount)
}
if n := bs.maxConnLifetime.Val(); n > 0 {
sqlDb.SetConnMaxLifetime(time.Duration(n) * time.Second)
} else if node.MaxConnLifetime > 0 {
sqlDb.SetConnMaxLifetime(time.Duration(node.MaxConnLifetime) * time.Second)
}
return sqlDb
}, 0)
if v != nil && sqlDb == nil {
sqlDb = v.(*sql.DB)
}
// 是否开启调试模式
if node.Debug {
bs.db.SetDebug(node.Debug)
2019-07-15 17:40:21 +08:00
}
2019-06-19 09:06:52 +08:00
// 是否手动选择数据库
if v := bs.schema.Val(); v != "" {
if e := bs.db.setSchema(sqlDb, v); e != nil {
err = e
}
2019-06-19 09:06:52 +08:00
}
return
}
// 切换当前数据库对象操作的数据库。
2018-12-17 19:26:59 +08:00
func (bs *dbBase) SetSchema(schema string) {
2019-06-19 09:06:52 +08:00
bs.schema.Set(schema)
2018-12-17 19:26:59 +08:00
}
// 创建底层数据库master链接对象。
func (bs *dbBase) Master() (*sql.DB, error) {
return bs.getSqlDb(true)
}
2018-08-08 09:09:28 +08:00
// 创建底层数据库slave链接对象。
func (bs *dbBase) Slave() (*sql.DB, error) {
2019-06-19 09:06:52 +08:00
return bs.getSqlDb(false)
2017-11-23 10:21:28 +08:00
}