gf/g/database/gdb/gdb.go
2018-01-03 10:38:53 +08:00

237 lines
6.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2017 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.
// 关系数据库操作
package gdb
import (
"fmt"
"errors"
"database/sql"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/gcache"
"gitee.com/johng/gf/g/util/grand"
_ "github.com/lib/pq"
_ "github.com/go-sql-driver/mysql"
)
const (
OPTION_INSERT = 0
OPTION_REPLACE = 1
OPTION_SAVE = 2
OPTION_IGNORE = 3
)
// 数据库操作接口
type Link interface {
Open (c *ConfigNode) (*sql.DB, error)
Close() error
Query(q string, args ...interface{}) (*sql.Rows, error)
Exec(q string, args ...interface{}) (sql.Result, error)
Prepare(q string) (*sql.Stmt, error)
GetAll(q string, args ...interface{}) (List, error)
GetOne(q string, args ...interface{}) (Map, error)
GetValue(q string, args ...interface{}) (interface{}, error)
PingMaster() error
PingSlave() error
SetMaxIdleConns(n int)
SetMaxOpenConns(n int)
setMaster(master *sql.DB)
setSlave(slave *sql.DB)
setQuoteChar(left string, right string)
setLink(link Link)
getQuoteCharLeft () string
getQuoteCharRight () string
handleSqlBeforeExec(q *string) *string
Begin() (*sql.Tx, error)
Commit() error
Rollback() error
insert(table string, data Map, option uint8) (sql.Result, error)
Insert(table string, data Map) (sql.Result, error)
Replace(table string, data Map) (sql.Result, error)
Save(table string, data Map) (sql.Result, error)
batchInsert(table string, list List, batch int, option uint8) error
BatchInsert(table string, list List, batch int) error
BatchReplace(table string, list List, batch int) error
BatchSave(table string, list List, batch int) error
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
Table(tables string) (*gLinkOp)
}
// 数据库链接对象
type dbLink struct {
link Link
transaction *sql.Tx
master *sql.DB
slave *sql.DB
charl string
charr string
}
// 关联数组,绑定一条数据表记录
type Map map[string]interface{}
// 关联数组列表(索引从0开始的数组),绑定多条记录
type List []Map
// 获得默认的数据库操作对象单例
func Instance () (Link, error) {
return instance(config.d)
}
// 获得指定配置项的数据库草最对象单例
func InstanceByGroup(groupName string) (Link, error) {
return instance(groupName)
}
// 根据配置项获取一个数据库操作对象单例
func instance (groupName string) (Link, error) {
instanceName := "gdb_instance_" + groupName
result := gcache.Get(instanceName)
if result == nil {
link, err := NewByGroup(groupName)
if err == nil {
gcache.Set(instanceName, link, 0)
return link, nil
} else {
return nil, err
}
} else {
return result.(Link), nil
}
}
// 使用默认选项进行连接数据库集群配置项default
func New() (Link, error) {
return NewByGroup(config.d)
}
// 根据数据库配置项创建一个数据库操作对象
func NewByGroup(groupName string) (Link, error) {
config.RLock()
defer config.RUnlock()
if len(config.c) < 1 {
return nil, errors.New("empty database configuration")
}
if list, ok := config.c[groupName]; 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 newLink(masterNode, slaveNode)
} else {
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", groupName))
}
}
// 根据单点数据库配置获得一个数据库草最对象
func NewByConfigNode(node ConfigNode) (Link, error) {
return newLink (&node, nil)
}
// 按照负载均衡算法(优先级配置)从数据库集群中选择一个配置节点出来使用
func getConfigNodeByPriority (cg *ConfigGroup) *ConfigNode {
if len(*cg) < 2 {
return &(*cg)[0]
}
var total int
for i := 0; i < len(*cg); i++ {
total += (*cg)[i].Priority * 100
}
r := grand.Rand(0, total)
min := 0
max := 0
for i := 0; i < len(*cg); i++ {
max = min + (*cg)[i].Priority * 100
//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
}
// 创建数据库链接对象
func newLink (masterNode *ConfigNode, slaveNode *ConfigNode) (Link, error) {
var link Link
switch masterNode.Type {
case "mysql":
link = Link(&mysqlLink{})
case "pgsql":
link = Link(&pgsqlLink{})
default:
return nil, errors.New(fmt.Sprintf("unsupported db type '%s'", masterNode.Type))
}
master, err := link.Open(masterNode)
if err != nil {
glog.Fatal(err)
}
slave := master
if slaveNode != nil {
slave, err = link.Open(slaveNode)
if err != nil {
glog.Fatal(err)
}
}
link.setLink(link)
link.setMaster(master)
link.setSlave(slave)
link.setQuoteChar(link.getQuoteCharLeft(), link.getQuoteCharRight())
return link, nil
}
// 设置master链接对象
func (l *dbLink) setMaster(master *sql.DB) {
l.master = master
}
// 设置slave链接对象
func (l *dbLink) setSlave(slave *sql.DB) {
l.slave = slave
}
// 设置当前数据库类型引用字符
func (l *dbLink) setQuoteChar(left string, right string) {
l.charl = left
l.charr = right
}
// 设置挡脸操作的link接口
func (l *dbLink) setLink(link Link) {
l.link = link
}