mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
改进gdb,增加数据库操作自动Close特性(使用链接池),用户无需再defer db.Close(),并增加g.DB数据库对象单例别名
This commit is contained in:
@ -94,9 +94,7 @@ func (this *IntInterfaceMap) GetOrSet(key int, value interface{}) interface{} {
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
v = f()
|
||||
this.doSetWithLockCheck(key, v)
|
||||
return v
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
@ -105,8 +103,7 @@ func (this *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interfa
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *IntInterfaceMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
this.doSetWithLockCheck(key, f)
|
||||
return v
|
||||
return this.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
|
||||
@ -96,9 +96,7 @@ func (this *InterfaceInterfaceMap) GetOrSet(key interface{}, value interface{})
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *InterfaceInterfaceMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
v = f()
|
||||
this.doSetWithLockCheck(key, v)
|
||||
return v
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
@ -107,8 +105,7 @@ func (this *InterfaceInterfaceMap) GetOrSetFunc(key interface{}, f func() interf
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *InterfaceInterfaceMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
this.doSetWithLockCheck(key, f)
|
||||
return v
|
||||
return this.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
|
||||
@ -96,9 +96,7 @@ func (this *StringInterfaceMap) GetOrSet(key string, value interface{}) interfac
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
v = f()
|
||||
this.doSetWithLockCheck(key, v)
|
||||
return v
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
@ -107,8 +105,7 @@ func (this *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) i
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *StringInterfaceMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
this.doSetWithLockCheck(key, f)
|
||||
return v
|
||||
return this.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ type Link interface {
|
||||
// 连接属性设置
|
||||
SetMaxIdleConns(n int)
|
||||
SetMaxOpenConns(n int)
|
||||
SetConnMaxLifetime(d time.Duration)
|
||||
SetConnMaxLifetime(n int)
|
||||
|
||||
// 开启事务操作
|
||||
Begin() (*Tx, error)
|
||||
@ -74,9 +74,6 @@ type Link interface {
|
||||
Table(tables string) *Model
|
||||
From(tables string) *Model
|
||||
|
||||
// 关闭数据库操作对象
|
||||
Close() error
|
||||
|
||||
// 内部方法
|
||||
insert(table string, data Map, option uint8) (sql.Result, error)
|
||||
batchInsert(table string, list List, batch int, option uint8) (sql.Result, error)
|
||||
@ -88,14 +85,16 @@ type Link interface {
|
||||
|
||||
// 数据库链接对象
|
||||
type Db struct {
|
||||
link Link // 底层数据库类型管理对象
|
||||
master *sql.DB // 实例化数据库链接(master)
|
||||
slave *sql.DB // 实例化数据库链接(slave,可能会与master相同)
|
||||
charl string // SQL安全符号(左)
|
||||
charr string // SQL安全符号(右)
|
||||
debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性
|
||||
sqls *gring.Ring // (debug=true时有效)已执行的SQL列表
|
||||
cache *gcache.Cache // 查询缓存,需要注意的是,事务查询不支持缓存
|
||||
link Link // 底层数据库类型管理对象
|
||||
group string // 配置分组名称
|
||||
charl string // SQL安全符号(左)
|
||||
charr string // SQL安全符号(右)
|
||||
debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性
|
||||
sqls *gring.Ring // (debug=true时有效)已执行的SQL列表
|
||||
cache *gcache.Cache // 查询缓存,需要注意的是,事务查询不支持缓存
|
||||
maxIdleConnCount *gtype.Int // 连接池最大限制的连接数
|
||||
maxOpenConnCount *gtype.Int // 连接池最大打开的连接数
|
||||
maxConnLifetime *gtype.Int // (单位秒)连接对象可重复使用的时间长度
|
||||
}
|
||||
|
||||
// 执行的SQL对象
|
||||
@ -138,9 +137,9 @@ var dbCaches = gmap.NewStringInterfaceMap()
|
||||
|
||||
// 使用默认/指定分组配置进行连接,数据库集群配置项:default
|
||||
func New(groupName ...string) (*Db, error) {
|
||||
name := config.d
|
||||
group := config.d
|
||||
if len(groupName) > 0 {
|
||||
name = groupName[0]
|
||||
group = groupName[0]
|
||||
}
|
||||
config.RLock()
|
||||
defer config.RUnlock()
|
||||
@ -148,32 +147,63 @@ func New(groupName ...string) (*Db, error) {
|
||||
if len(config.c) < 1 {
|
||||
return nil, errors.New("empty database configuration")
|
||||
}
|
||||
if list, ok := config.c[name]; 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 {
|
||||
// 默认配置项的角色为master
|
||||
masterList = append(masterList, list[i])
|
||||
}
|
||||
}
|
||||
if len(masterList) < 1 {
|
||||
return nil, errors.New("at least one master node configuration's need to make sense")
|
||||
}
|
||||
masterNode := getConfigNodeByPriority(masterList)
|
||||
var slaveNode *ConfigNode
|
||||
if len(slaveList) > 0 {
|
||||
slaveNode = getConfigNodeByPriority(slaveList)
|
||||
}
|
||||
return newDb(masterNode, slaveNode, name)
|
||||
if _, ok := config.c[group]; ok {
|
||||
if node, err := getConfigNodeByGroup(group, true); err == nil {
|
||||
link, err := getLinkByType(node.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db := &Db {
|
||||
link : link,
|
||||
group : group,
|
||||
charl : link.getQuoteCharLeft(),
|
||||
charr : link.getQuoteCharRight(),
|
||||
debug : gtype.NewBool(),
|
||||
maxIdleConnCount : gtype.NewInt(),
|
||||
maxOpenConnCount : gtype.NewInt(),
|
||||
maxConnLifetime : gtype.NewInt(),
|
||||
}
|
||||
db.cache = dbCaches.GetOrSetFuncLock(group, func() interface{} {
|
||||
return gcache.New()
|
||||
}).(*gcache.Cache)
|
||||
return db, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", name))
|
||||
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
|
||||
}
|
||||
}
|
||||
|
||||
// 获取指定数据库角色的一个配置项,内部根据权重计算负载均衡
|
||||
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
||||
if list, ok := config.c[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 {
|
||||
return getConfigNodeByPriority(masterList), nil
|
||||
} else {
|
||||
return getConfigNodeByPriority(slaveList), nil
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
|
||||
}
|
||||
}
|
||||
|
||||
// 按照负载均衡算法(优先级配置)从数据库集群中选择一个配置节点出来使用
|
||||
// 算法说明举例,
|
||||
// 1、假如2个节点的priority都是1,那么随机大小范围为[0, 199];
|
||||
@ -207,60 +237,63 @@ func getConfigNodeByPriority(cg ConfigGroup) *ConfigNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 创建数据库链接对象
|
||||
func newDb(masterNode *ConfigNode, slaveNode *ConfigNode, groupName string) (*Db, error) {
|
||||
var link Link
|
||||
switch masterNode.Type {
|
||||
case "mysql":
|
||||
link = linkMysql
|
||||
case "pgsql":
|
||||
link = linkPgsql
|
||||
case "sqlite":
|
||||
link = linkSqlite
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("unsupported db type '%s'", masterNode.Type))
|
||||
// 根据配置的数据库;类型获得Link接口对象
|
||||
func getLinkByType(dbType string) (Link, error) {
|
||||
switch dbType {
|
||||
case "mysql":
|
||||
return linkMysql, nil
|
||||
case "pgsql":
|
||||
return linkPgsql, nil
|
||||
case "sqlite":
|
||||
return linkSqlite, nil
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("unsupported db type '%s'", dbType))
|
||||
}
|
||||
master, err := link.Open(masterNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
slave := master
|
||||
if slaveNode != nil {
|
||||
slave, err = link.Open(slaveNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
db := &Db{
|
||||
link: link,
|
||||
master: master,
|
||||
slave: slave,
|
||||
charl: link.getQuoteCharLeft(),
|
||||
charr: link.getQuoteCharRight(),
|
||||
debug: gtype.NewBool(),
|
||||
}
|
||||
// 设置连接属性,master和slave必须是一致的,所以这里使用的是master的属性设置
|
||||
if masterNode.MaxIdleConnCount > 0 {
|
||||
db.SetMaxIdleConns(masterNode.MaxIdleConnCount)
|
||||
}
|
||||
if masterNode.MaxOpenConnCount > 0 {
|
||||
db.SetMaxOpenConns(masterNode.MaxOpenConnCount)
|
||||
}
|
||||
if masterNode.MaxConnLifetime > 0 {
|
||||
db.SetConnMaxLifetime(time.Duration(masterNode.MaxConnLifetime) * time.Second)
|
||||
}
|
||||
if v := dbCaches.Get(groupName); v == nil {
|
||||
dbCaches.LockFunc(func(m map[string]interface{}) {
|
||||
if v, ok := m[groupName]; !ok {
|
||||
db.cache = gcache.New()
|
||||
m[groupName] = db.cache
|
||||
} else {
|
||||
db.cache = v.(*gcache.Cache)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
db.cache = v.(*gcache.Cache)
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// 获得底层数据库链接对象
|
||||
func (db *Db) getSqlDb(master bool) (*sql.DB, error) {
|
||||
node, err := getConfigNodeByGroup(db.group, master)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
link, err := getLinkByType(node.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sqlDb, err := link.Open(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if node.MaxIdleConnCount > 0 {
|
||||
sqlDb.SetMaxIdleConns(node.MaxIdleConnCount)
|
||||
}
|
||||
if n := db.maxIdleConnCount.Val(); n > 0 {
|
||||
sqlDb.SetMaxIdleConns(n)
|
||||
}
|
||||
|
||||
if node.MaxOpenConnCount > 0 {
|
||||
sqlDb.SetMaxOpenConns(node.MaxOpenConnCount)
|
||||
}
|
||||
if n := db.maxOpenConnCount.Val(); n > 0 {
|
||||
sqlDb.SetMaxOpenConns(n)
|
||||
}
|
||||
|
||||
if node.MaxConnLifetime > 0 {
|
||||
sqlDb.SetConnMaxLifetime(time.Duration(node.MaxConnLifetime) * time.Second)
|
||||
}
|
||||
if n := db.maxConnLifetime.Val(); n > 0 {
|
||||
sqlDb.SetConnMaxLifetime(time.Duration(n) * time.Second)
|
||||
}
|
||||
return sqlDb, nil
|
||||
}
|
||||
|
||||
// 创建底层数据库master链接对象
|
||||
func (db *Db) Master() (*sql.DB, error) {
|
||||
return db.getSqlDb(true)
|
||||
}
|
||||
|
||||
// 创建底层数据库slave链接对象
|
||||
func (db *Db) Slave() (*sql.DB, error) {
|
||||
return db.getSqlDb(false)
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@ import (
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"gitee.com/johng/gf/g/container/gring"
|
||||
"gitee.com/johng/gf/g/os/gtime"
|
||||
"time"
|
||||
"gitee.com/johng/gf/g/os/glog"
|
||||
"gitee.com/johng/gf/g/container/gvar"
|
||||
)
|
||||
@ -81,35 +80,22 @@ func (db *Db) printSql(v *Sql) {
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭链接
|
||||
func (db *Db) Close() error {
|
||||
if db.master != nil {
|
||||
if err := db.master.Close(); err == nil {
|
||||
db.master = nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if db.slave != nil {
|
||||
if err := db.slave.Close(); err == nil {
|
||||
db.slave = nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 数据库sql查询操作,主要执行查询
|
||||
func (db *Db) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
var err error
|
||||
var rows *sql.Rows
|
||||
var err error
|
||||
var rows *sql.Rows
|
||||
var slave *sql.DB
|
||||
slave, err = db.Slave();
|
||||
if err != nil {
|
||||
return nil,err
|
||||
}
|
||||
defer slave.Close()
|
||||
p := db.link.handleSqlBeforeExec(&query)
|
||||
if db.debug.Val() {
|
||||
militime1 := gtime.Millisecond()
|
||||
rows, err = db.slave.Query(*p, args ...)
|
||||
rows, err = slave.Query(*p, args ...)
|
||||
militime2 := gtime.Millisecond()
|
||||
s := &Sql{
|
||||
s := &Sql{
|
||||
Sql : *p,
|
||||
Args : args,
|
||||
Error : err,
|
||||
@ -120,7 +106,7 @@ func (db *Db) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
db.sqls.Put(s)
|
||||
db.printSql(s)
|
||||
} else {
|
||||
rows, err = db.slave.Query(*p, args ...)
|
||||
rows, err = slave.Query(*p, args ...)
|
||||
}
|
||||
if err == nil {
|
||||
return rows, nil
|
||||
@ -134,12 +120,18 @@ func (db *Db) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
func (db *Db) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
var err error
|
||||
var result sql.Result
|
||||
var master *sql.DB
|
||||
master, err = db.Master();
|
||||
if err != nil {
|
||||
return nil,err
|
||||
}
|
||||
defer master.Close()
|
||||
p := db.link.handleSqlBeforeExec(&query)
|
||||
if db.debug.Val() {
|
||||
militime1 := gtime.Millisecond()
|
||||
result, err = db.master.Exec(*p, args ...)
|
||||
result, err = master.Exec(*p, args ...)
|
||||
militime2 := gtime.Millisecond()
|
||||
s := &Sql{
|
||||
s := &Sql{
|
||||
Sql : *p,
|
||||
Args : args,
|
||||
Error : err,
|
||||
@ -150,7 +142,7 @@ func (db *Db) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
db.sqls.Put(s)
|
||||
db.printSql(s)
|
||||
} else {
|
||||
result, err = db.master.Exec(*p, args ...)
|
||||
result, err = master.Exec(*p, args ...)
|
||||
}
|
||||
return result, db.formatError(err, p, args...)
|
||||
}
|
||||
@ -269,58 +261,49 @@ func (db *Db) Select(tables, fields string, condition interface{}, groupBy, orde
|
||||
// sql预处理,执行完成后调用返回值sql.Stmt.Exec完成sql操作
|
||||
// 记得调用sql.Stmt.Close关闭操作对象
|
||||
func (db *Db) Prepare(query string) (*sql.Stmt, error) {
|
||||
return db.master.Prepare(query)
|
||||
if master, err := db.Master(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer master.Close()
|
||||
return master.Prepare(query)
|
||||
}
|
||||
}
|
||||
|
||||
// ping一下,判断或保持数据库链接(master)
|
||||
func (db *Db) PingMaster() error {
|
||||
err := db.master.Ping()
|
||||
return err
|
||||
if master, err := db.Master(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer master.Close()
|
||||
return master.Ping()
|
||||
}
|
||||
}
|
||||
|
||||
// ping一下,判断或保持数据库链接(slave)
|
||||
func (db *Db) PingSlave() error {
|
||||
err := db.slave.Ping()
|
||||
return err
|
||||
}
|
||||
|
||||
// 设置数据库连接池中空闲链接的大小
|
||||
func (db *Db) SetMaxIdleConns(n int) {
|
||||
db.master.SetMaxIdleConns(n)
|
||||
// 比较的是指向的变量地址
|
||||
if db.master != db.slave {
|
||||
db.slave.SetMaxIdleConns(n)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置数据库连接池最大打开的链接数量
|
||||
func (db *Db) SetMaxOpenConns(n int) {
|
||||
db.master.SetMaxOpenConns(n)
|
||||
// 比较的是指向的变量地址
|
||||
if db.master != db.slave {
|
||||
db.slave.SetMaxOpenConns(n)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置数据库连接可重复利用的时间,超过该时间则被关闭废弃
|
||||
// 如果 d <= 0 表示该链接会一直重复利用
|
||||
func (db *Db) SetConnMaxLifetime(d time.Duration) {
|
||||
db.master.SetConnMaxLifetime(d)
|
||||
// 比较的是指向的变量地址
|
||||
if db.master != db.slave {
|
||||
db.slave.SetConnMaxLifetime(d)
|
||||
if slave, err := db.Slave(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer slave.Close()
|
||||
return slave.Ping()
|
||||
}
|
||||
}
|
||||
|
||||
// 事务操作,开启,会返回一个底层的事务操作对象链接如需要嵌套事务,那么可以使用该对象,否则请忽略
|
||||
// 只有在tx.Commit/tx.Rollback时,链接会自动Close
|
||||
func (db *Db) Begin() (*Tx, error) {
|
||||
if tx, err := db.master.Begin(); err == nil {
|
||||
return &Tx {
|
||||
db : db,
|
||||
tx : tx,
|
||||
}, nil
|
||||
} else {
|
||||
if master, err := db.Master(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if tx, err := master.Begin(); err == nil {
|
||||
return &Tx {
|
||||
db : db,
|
||||
tx : tx,
|
||||
master : master,
|
||||
}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -521,5 +504,4 @@ func (db *Db) formatCondition(condition interface{}) (where string) {
|
||||
where += gconv.String(condition)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
@ -119,3 +119,19 @@ func SetDefaultGroup (groupName string) {
|
||||
config.d = groupName
|
||||
config.Unlock()
|
||||
}
|
||||
|
||||
// 设置数据库连接池中空闲链接的大小
|
||||
func (db *Db) SetMaxIdleConns(n int) {
|
||||
db.maxIdleConnCount.Set(n)
|
||||
}
|
||||
|
||||
// 设置数据库连接池最大打开的链接数量
|
||||
func (db *Db) SetMaxOpenConns(n int) {
|
||||
db.maxOpenConnCount.Set(n)
|
||||
}
|
||||
|
||||
// 设置数据库连接可重复利用的时间,超过该时间则被关闭废弃
|
||||
// 如果 d <= 0 表示该链接会一直重复利用
|
||||
func (db *Db) SetConnMaxLifetime(n int) {
|
||||
db.maxConnLifetime.Set(n)
|
||||
}
|
||||
@ -20,18 +20,23 @@ import (
|
||||
|
||||
// 数据库事务对象
|
||||
type Tx struct {
|
||||
db *Db
|
||||
tx *sql.Tx
|
||||
db *Db
|
||||
tx *sql.Tx
|
||||
master *sql.DB
|
||||
}
|
||||
|
||||
// 事务操作,提交
|
||||
func (tx *Tx) Commit() error {
|
||||
return tx.tx.Commit()
|
||||
err := tx.tx.Commit()
|
||||
tx.master.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// 事务操作,回滚
|
||||
func (tx *Tx) Rollback() error {
|
||||
return tx.tx.Rollback()
|
||||
err := tx.tx.Rollback()
|
||||
tx.master.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// (事务)数据库sql查询操作,主要执行查询
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// 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://gitee.com/johng/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"gitee.com/johng/gf/g"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 这里需要修改为本地配置文件的目录地址
|
||||
g.Config().SetPath("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame")
|
||||
}
|
||||
|
||||
func Benchmark_New(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
g.Database()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_NewAndClose(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
g.Database().Close()
|
||||
}
|
||||
}
|
||||
@ -15,32 +15,54 @@ import (
|
||||
"gitee.com/johng/gf/g/os/gview"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"gitee.com/johng/gf/g/container/gmap"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"gitee.com/johng/gf/g/database/gdb"
|
||||
"gitee.com/johng/gf/g/os/gfsnotify"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
gFRAME_CORE_COMPONENT_NAME_VIEW = "gf.core.component.view"
|
||||
gFRAME_CORE_COMPONENT_NAME_CONFIG = "gf.core.component.config"
|
||||
gFRAME_CORE_COMPONENT_NAME_DATABASE = "gf.core.component.database"
|
||||
)
|
||||
|
||||
// 单例对象存储器
|
||||
var instances = gmap.NewStringInterfaceMap()
|
||||
|
||||
// 获取单例对象
|
||||
func Get(k string) interface{} {
|
||||
return instances.Get(k)
|
||||
func Get(key string) interface{} {
|
||||
return instances.Get(key)
|
||||
}
|
||||
|
||||
// 设置单例对象
|
||||
func Set(k string, v interface{}) {
|
||||
instances.Set(k, v)
|
||||
func Set(key string, value interface{}) {
|
||||
instances.Set(key, key)
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func GetOrSet(key string, value interface{}) interface{} {
|
||||
return instances.GetOrSet(key, value)
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func GetOrSetFunc(key string, f func() interface{}) interface{} {
|
||||
return instances.GetOrSetFunc(key, f)
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func GetOrSetFuncLock(key string, f func() interface{}) interface{} {
|
||||
return instances.GetOrSetFuncLock(key, f)
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func SetIfNotExist(key string, value interface{}) bool {
|
||||
return instances.SetIfNotExist(key, value)
|
||||
}
|
||||
|
||||
// 核心对象:View
|
||||
func View() *gview.View {
|
||||
result := Get(gFRAME_CORE_COMPONENT_NAME_VIEW)
|
||||
if result != nil {
|
||||
return result.(*gview.View)
|
||||
} else {
|
||||
return instances.GetOrSetFuncLock(gFRAME_CORE_COMPONENT_NAME_VIEW, func() interface{} {
|
||||
path := gcmd.Option.Get("gf.viewpath")
|
||||
if path == "" {
|
||||
path = genv.Get("gf.viewpath")
|
||||
@ -55,19 +77,14 @@ func View() *gview.View {
|
||||
}
|
||||
// 框架内置函数
|
||||
view.BindFunc("config", funcConfig)
|
||||
Set(gFRAME_CORE_COMPONENT_NAME_VIEW, view)
|
||||
return view
|
||||
}
|
||||
return nil
|
||||
}).(*gview.View)
|
||||
}
|
||||
|
||||
// 核心对象:Config
|
||||
// 配置文件目录查找依次为:启动参数cfgpath、当前程序运行目录
|
||||
func Config() *gcfg.Config {
|
||||
result := Get(gFRAME_CORE_COMPONENT_NAME_CONFIG)
|
||||
if result != nil {
|
||||
return result.(*gcfg.Config)
|
||||
} else {
|
||||
return instances.GetOrSetFuncLock(gFRAME_CORE_COMPONENT_NAME_CONFIG, func() interface{} {
|
||||
path := gcmd.Option.Get("gf.cfgpath")
|
||||
if path == "" {
|
||||
path = genv.Get("gf.cfgpath")
|
||||
@ -80,9 +97,78 @@ func Config() *gcfg.Config {
|
||||
if p := gfile.MainPkgPath(); gfile.Exists(p) {
|
||||
config.AddPath(p)
|
||||
}
|
||||
// 单例对象缓存控制
|
||||
Set(gFRAME_CORE_COMPONENT_NAME_CONFIG, config)
|
||||
return config
|
||||
}).(*gcfg.Config)
|
||||
}
|
||||
|
||||
// 数据库操作对象,使用了连接池
|
||||
func Database(name...string) *gdb.Db {
|
||||
config := Config()
|
||||
db := instances.GetOrSetFuncLock(gFRAME_CORE_COMPONENT_NAME_DATABASE, func() interface{} {
|
||||
m := config.GetMap("database")
|
||||
if m == nil {
|
||||
panic(fmt.Sprintf(`incomplete configuration for database: "database" node not found in config file "%s"`, config.GetFilePath()))
|
||||
}
|
||||
for group, v := range m {
|
||||
cg := gdb.ConfigGroup{}
|
||||
if list, ok := v.([]interface{}); ok {
|
||||
for _, nodev := range list {
|
||||
node := gdb.ConfigNode{}
|
||||
nodem := nodev.(map[string]interface{})
|
||||
if value, ok := nodem["host"]; ok {
|
||||
node.Host = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["port"]; ok {
|
||||
node.Port = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["user"]; ok {
|
||||
node.User = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["pass"]; ok {
|
||||
node.Pass = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["name"]; ok {
|
||||
node.Name = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["type"]; ok {
|
||||
node.Type = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["role"]; ok {
|
||||
node.Role = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["charset"]; ok {
|
||||
node.Charset = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["priority"]; ok {
|
||||
node.Priority = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodem["max-idle"]; ok {
|
||||
node.MaxIdleConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodem["max-open"]; ok {
|
||||
node.MaxOpenConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodem["max-lifetime"]; ok {
|
||||
node.MaxConnLifetime = gconv.Int(value)
|
||||
}
|
||||
cg = append(cg, node)
|
||||
}
|
||||
}
|
||||
gdb.AddConfigGroup(group, cg)
|
||||
}
|
||||
// 使用gfsnotify进行文件监控,当配置文件有任何变化时,清空数据库配置缓存
|
||||
gfsnotify.Add(config.GetFilePath(), func(event *gfsnotify.Event) {
|
||||
instances.Remove(gFRAME_CORE_COMPONENT_NAME_DATABASE)
|
||||
})
|
||||
if db, err := gdb.New(name...); err == nil {
|
||||
return db
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if db != nil {
|
||||
return db.(*gdb.Db)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -9,8 +9,6 @@ package g
|
||||
import (
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"gitee.com/johng/gf/g/database/gdb"
|
||||
"gitee.com/johng/gf/g/os/gcache"
|
||||
"gitee.com/johng/gf/g/os/gfsnotify"
|
||||
"gitee.com/johng/gf/g/database/gredis"
|
||||
"gitee.com/johng/gf/g/frame/gins"
|
||||
"gitee.com/johng/gf/g/net/ghttp"
|
||||
@ -19,13 +17,7 @@ import (
|
||||
"gitee.com/johng/gf/g/os/gview"
|
||||
"gitee.com/johng/gf/g/os/gcfg"
|
||||
"gitee.com/johng/gf/g/util/gregex"
|
||||
"gitee.com/johng/gf/g/os/glog"
|
||||
"fmt"
|
||||
)
|
||||
const (
|
||||
gIS_DATABASE_CONFIG_CACHED = "gf.core.component.database.cached"
|
||||
)
|
||||
|
||||
|
||||
// HTTPServer单例对象
|
||||
func Server(name...interface{}) *ghttp.Server {
|
||||
@ -55,79 +47,12 @@ func Config() *gcfg.Config {
|
||||
|
||||
// 数据库操作对象,使用了连接池
|
||||
func Database(name...string) *gdb.Db {
|
||||
config := gins.Config()
|
||||
if config == nil {
|
||||
glog.Error("Config component init failed")
|
||||
return nil
|
||||
}
|
||||
// 数据库配置是否已经设置到gdb模块中
|
||||
gcache.GetOrSetFuncLock(gIS_DATABASE_CONFIG_CACHED, func() interface{} {
|
||||
if m := config.GetMap("database"); m != nil {
|
||||
c := gdb.Config{}
|
||||
for group, v := range m {
|
||||
cg := gdb.ConfigGroup{}
|
||||
if list, ok := v.([]interface{}); ok {
|
||||
for _, nodev := range list {
|
||||
node := gdb.ConfigNode{}
|
||||
nodem := nodev.(map[string]interface{})
|
||||
if value, ok := nodem["host"]; ok {
|
||||
node.Host = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["port"]; ok {
|
||||
node.Port = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["user"]; ok {
|
||||
node.User = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["pass"]; ok {
|
||||
node.Pass = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["name"]; ok {
|
||||
node.Name = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["type"]; ok {
|
||||
node.Type = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["role"]; ok {
|
||||
node.Role = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["charset"]; ok {
|
||||
node.Charset = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodem["priority"]; ok {
|
||||
node.Priority = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodem["max-idle"]; ok {
|
||||
node.MaxIdleConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodem["max-open"]; ok {
|
||||
node.MaxOpenConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodem["max-lifetime"]; ok {
|
||||
node.MaxConnLifetime = gconv.Int(value)
|
||||
}
|
||||
cg = append(cg, node)
|
||||
}
|
||||
}
|
||||
c[group] = cg
|
||||
}
|
||||
gdb.SetConfig(c)
|
||||
// 使用gfsnotify进行文件监控,当配置文件有任何变化时,清空数据库配置缓存
|
||||
gfsnotify.Add(Config().GetFilePath(), func(event *gfsnotify.Event) {
|
||||
gcache.Remove(gIS_DATABASE_CONFIG_CACHED)
|
||||
})
|
||||
return struct{}{}
|
||||
} else {
|
||||
glog.Error(fmt.Sprintf(`incomplete configuration for database: "database" node not found in config file "%s"`, config.GetFilePath()))
|
||||
}
|
||||
return nil
|
||||
}, 0)
|
||||
if db, err := gdb.New(name...); err == nil {
|
||||
return db
|
||||
} else {
|
||||
glog.Error(err)
|
||||
return nil
|
||||
}
|
||||
return gins.Database(name...)
|
||||
}
|
||||
|
||||
// (别名)Database
|
||||
func DB(name...string) *gdb.Db {
|
||||
return gins.Database(name...)
|
||||
}
|
||||
|
||||
// Redis操作对象,使用了连接池
|
||||
@ -148,7 +73,7 @@ func Redis(name...string) *gredis.Redis {
|
||||
return gredis.New(gredis.Config{
|
||||
Host : array[1],
|
||||
Port : gconv.Int(array[2]),
|
||||
Db : gconv.Int(array[3]),
|
||||
Db : gconv.Int(array[3]),
|
||||
Pass : array[4],
|
||||
})
|
||||
}
|
||||
|
||||
@ -173,11 +173,31 @@ func Uint(i interface{}) uint {
|
||||
return 0
|
||||
}
|
||||
switch value := i.(type) {
|
||||
case int: return uint(value)
|
||||
case int8: return uint(value)
|
||||
case int16: return uint(value)
|
||||
case int32: return uint(value)
|
||||
case int64: return uint(value)
|
||||
case int:
|
||||
if value < 0 {
|
||||
value = -value
|
||||
}
|
||||
return uint(value)
|
||||
case int8:
|
||||
if value < 0 {
|
||||
value = -value
|
||||
}
|
||||
return uint(value)
|
||||
case int16:
|
||||
if value < 0 {
|
||||
value = -value
|
||||
}
|
||||
return uint(value)
|
||||
case int32:
|
||||
if value < 0 {
|
||||
value = -value
|
||||
}
|
||||
return uint(value)
|
||||
case int64:
|
||||
if value < 0 {
|
||||
value = -value
|
||||
}
|
||||
return uint(value)
|
||||
case uint: return value
|
||||
case uint8: return uint(value)
|
||||
case uint16: return uint(value)
|
||||
|
||||
@ -9,18 +9,24 @@ package grand
|
||||
|
||||
import (
|
||||
"time"
|
||||
"math/rand"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
var digits = []rune("0123456789")
|
||||
var numChan = make(chan uint, 10000)
|
||||
|
||||
// 修改全局随机数seed
|
||||
func Seed (seed...int64) {
|
||||
if len(seed) > 0 {
|
||||
rand.Seed(seed[0])
|
||||
} else {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
func init() {
|
||||
go func() {
|
||||
for {
|
||||
numChan <- gconv.Uint(time.Now().UnixNano())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// 自定义的 rand.Intn
|
||||
func intn (max int) int {
|
||||
return int(<- numChan)%max
|
||||
}
|
||||
|
||||
// 获得一个 min, max 之间的随机数(min <= x <= max)
|
||||
@ -28,21 +34,28 @@ func Rand (min, max int) int {
|
||||
if min >= max {
|
||||
return min
|
||||
}
|
||||
n := rand.Intn(max + 1)
|
||||
if n < min {
|
||||
return Rand(min, max)
|
||||
if min == 0 {
|
||||
return intn(max + 1)
|
||||
}
|
||||
return n
|
||||
if min > 0 {
|
||||
// 数值往左平移,再使用底层随机方法获得随机数,随后将结果数值往右平移
|
||||
return intn(max - (min - 0) + 1) + (min - 0)
|
||||
}
|
||||
if min < 0 {
|
||||
// 数值往右平移,再使用底层随机方法获得随机数,随后将结果数值往左平移
|
||||
return intn(max + (0 - min) + 1) - (0 - min)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// 获得指定长度的随机字符串(可能包含数字和字母)
|
||||
func RandStr(n int) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
if rand.Intn(2) == 1 {
|
||||
b[i] = digits[rand.Intn(10)]
|
||||
if intn(2) == 1 {
|
||||
b[i] = digits[intn(10)]
|
||||
} else {
|
||||
b[i] = letters[rand.Intn(52)]
|
||||
b[i] = letters[intn(52)]
|
||||
}
|
||||
}
|
||||
return string(b)
|
||||
@ -52,7 +65,7 @@ func RandStr(n int) string {
|
||||
func RandDigits(n int) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = digits[rand.Intn(10)]
|
||||
b[i] = digits[intn(10)]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
@ -61,7 +74,7 @@ func RandDigits(n int) string {
|
||||
func RandLetters(n int) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = letters[rand.Intn(52)]
|
||||
b[i] = letters[intn(52)]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ func init () {
|
||||
Host : "127.0.0.1",
|
||||
Port : "3306",
|
||||
User : "root",
|
||||
Pass : "123456",
|
||||
Pass : "8692651",
|
||||
Name : "test",
|
||||
Type : "mysql",
|
||||
Role : "master",
|
||||
@ -511,8 +511,8 @@ func main() {
|
||||
//linkopUpdate3()
|
||||
//linkopUpdate4()
|
||||
//
|
||||
//transaction1()
|
||||
//transaction2()
|
||||
transaction1()
|
||||
transaction2()
|
||||
//
|
||||
//keepPing()
|
||||
//likeQuery()
|
||||
|
||||
16
geg/database/orm/mysql/gdb_config.go
Normal file
16
geg/database/orm/mysql/gdb_config.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.Config().AddPath("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame")
|
||||
if r, err := g.DB().Table("user").Where("uid=?", 1).One(); err == nil {
|
||||
fmt.Println(r["uid"].Int())
|
||||
fmt.Println(r["name"].String())
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,7 @@ viewpath = "/home/www/templates/"
|
||||
host = "127.0.0.1"
|
||||
port = "3306"
|
||||
user = "root"
|
||||
pass = ""
|
||||
pass = "8692651"
|
||||
name = "test"
|
||||
type = "mysql"
|
||||
role = "master"
|
||||
|
||||
@ -1,70 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g"
|
||||
"gitee.com/johng/gf/g/net/ghttp"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"gitee.com/johng/gf/g/util/grand"
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fmt.Println(gfile.MainPkgPath())
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
|
||||
s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
if r.Router.Uri == "/logout" {
|
||||
r.Response.Write("HOOK_SERV")
|
||||
r.Exit()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
s.BindStatusHandler(404, func(r *ghttp.Request) {
|
||||
r.Response.Writeln("This is customized 404 page")
|
||||
})
|
||||
s.BindStatusHandler(500, func(r *ghttp.Request) {
|
||||
r.Response.Writeln("This is customized 500 page")
|
||||
})
|
||||
s.BindStatusHandler(403, func(r *ghttp.Request) {
|
||||
r.Response.Writeln("This is customized 403 page")
|
||||
})
|
||||
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("Hello World")
|
||||
})
|
||||
|
||||
p := &P{}
|
||||
s.BindHandler("/login", p.Login)
|
||||
s.BindHandler("/logout", p.Logout)
|
||||
|
||||
s.BindHandler("/api/getuser", p.GetUser)
|
||||
s.BindHandler("/api/:name", p.AnyName)
|
||||
|
||||
s.SetPort(6655)
|
||||
s.Run()
|
||||
fmt.Println(uint(-1))
|
||||
os.Exit(0)
|
||||
for i := 0; i < 10; i++ {
|
||||
fmt.Println(grand.Rand(100, 200))
|
||||
}
|
||||
}
|
||||
|
||||
type P struct {
|
||||
}
|
||||
|
||||
func (p *P) Login(c *ghttp.Request) {
|
||||
c.Cookie.SetCookie("username", "sdf", "", "/", 300)
|
||||
c.Response.Write("this is login")
|
||||
}
|
||||
|
||||
func (p *P) Logout(c *ghttp.Request) {
|
||||
c.Response.Write("this is logout")
|
||||
}
|
||||
|
||||
func (p *P) GetUser(c *ghttp.Request) {
|
||||
c.Cookie.Remove("username", "", "/")
|
||||
c.Response.Write("this is GetUser")
|
||||
}
|
||||
|
||||
func (p *P) AnyName(c *ghttp.Request) {
|
||||
c.Response.Write("this is AnyName")
|
||||
}
|
||||
Reference in New Issue
Block a user