天天看點

GO從入門到進階教程系列 - 研發高性能ORM架構mysql管理多資料源篇

  ↵

       上一篇教程我們了解到了基礎的GO文法,今天我們來學習如何使用GO開發一個通用的mysql管理器,下面就直接進入步驟環節,代碼需要承接上一篇教程的,如有疑問請檢視上一篇教程

技術版權歸屬

廣州市金獅網絡科技有限公司 (https://kingc.cn/)

,如需商用請聯系公司

1. 編寫一個多資料源執行個體的管理器對象,以及改造下之前的DBConfig對象

// 連接配接管理器
type RDBManager struct {
	OpenTx   bool    // 是否開啟事務
	DsName string  // 資料源名稱
	Db     *sql.DB // 非事務執行個體
	Tx     *sql.Tx // 事務執行個體
	Errors []error // 操作過程中記錄的錯誤
}           
// 資料庫配置
type DBConfig struct {
	DsName   string // 資料源名稱
	Host     string // 位址IP
	Port     int    // 資料庫端口
	Database string // 資料庫名稱
	Username string // 賬号
	Password string // 密碼
}           

2. 編寫初始化多個資料源配置的方法,并改造下我們之前的NewMysql方法

// 初始化資料庫執行個體
func NewMysql(conf DBConfig) (*sql.DB, error) {
	// 定義占位符字元串,使用配置值替換%s和%d
	link := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8", conf.Username, conf.Password, conf.Host, conf.Port, conf.Database)
	// 打開mysql獲得執行個體對象
	db, err := sql.Open("mysql", link)
	// 打開mysql失敗,傳回nil對象,以及傳回err對象
	if err != nil {
		return nil, errors.New("mysql初始化失敗: " + err.Error())
	}
	// 打開mysql成功傳回db對象,err=nil
	return db, nil
}           
var (
	MASTER = "MASTER"	// 預設主資料源
	RDBs   = map[string]*RDBManager{} // 初始化時加載資料源到集合
)

// 初始化多個資料庫配置檔案
func BuildByConfig(input ...DBConfig) {
	if len(input) == 0 {
		panic("資料源配置不能為空")
	}
	for _, v := range input {
		db, err := NewMysql(v)
		panic(err)
		if len(v.DsName) == 0 {
			v.DsName = MASTER
		}
		rdb := &RDBManager{
			Db:     db,
			DsName: v.DsName,
		}
		RDBs[v.DsName] = rdb
	}
}           

3. 編寫擷取資料源的方法,包含是否開啟事務,資料源名稱參數

// 擷取資料源,并控制是否開啟事務
func GetDB(openTx bool, ds ...string) (*RDBManager, error) {
	dsName := MASTER
	if len(ds) > 0 && len(ds[0]) > 0 {
		dsName = ds[0]
	}
	rt := RDBs[dsName]
	rdb := &RDBManager{
		OpenTx: openTx,
		Db:     rt.Db,
		DsName: rt.DsName,
		Errors: []error{},
	}
	// 如設定事務,則初始化事務執行個體
	if rdb.OpenTx {
		tx, err := rdb.Db.Begin()
		if err != nil {
			return nil, errors.New("開啟事務失敗:" + err.Error())
		}
		rdb.Tx = tx
	}
	return rdb, nil
}           

4. 編寫釋放資料庫資源,并送出事務的方法

// 釋放資源并送出事務
func (self *RDBManager) Close() {
	if self.OpenTx { // 開啟事務操作邏輯
		if len(self.Errors) > 0 { // 如産生異常則復原事務
			self.Tx.Rollback()
		} else {
			self.Tx.Commit() // 如無異常則送出事務
		}
	}
}           

5. 使用擷取的資料源進行儲存資料操作,我們改造下之前的CRUD方法

// 通過管理器開啟事務儲存資料
func (self *RDBManager) CRUD1() error {
	// 編寫需要執行的sql
	createSql := "insert test_user(username, password, age, sex) values(?,?,?,?)"
	// 預編譯sql,事務模式
	stmt, err := self.Tx.Prepare(createSql)
	if err != nil {
		return errors.New("預編譯失敗: " + err.Error())
	}
	// 送出編譯sql對應參數
	ret, err := stmt.Exec("zhangsan", "123456", 18, 1)
	if err != nil {
		return errors.New("送出資料失敗: " + err.Error())
	}
	// 儲存成功後擷取自增ID
	fmt.Println(ret.LastInsertId())
	return nil
}           

6. 編寫單元測試用例

// 單元測試
func TestCRUD1(t *testing.T) {
	// 資料源配置
	conf := DBConfig{
		DsName:   "test",
		Host:     "127.0.0.1",
		Port:     3306,
		Database: "test",
		Username: "root",
		Password: "123456",
	}
	// 初始化資料源
	BuildByConfig(conf)
	// 擷取test資料源
	rdb, err := GetDB(true, "test")
	if err != nil {
		panic(err)
	}
	// 釋放資源
	defer rdb.Close()
	// 執行儲存資料方法
	if err := rdb.CRUD1(); err != nil {
		panic(err)
	}
}           

上面我們已經明白了自定義資料源管理器如何實作,通過示例示範操作,下一篇文章我會講解如何封裝對象轉sql入庫,敬請期待!