如何创建单例数据库实例

huangapple go评论65阅读模式
英文:

How to create singleton DB instance

问题

我参考了一些代码示例,了解如何创建Go的单例模式,但我希望在这些示例中有一些方法,并在它们的单例引用上调用这些方法。我的代码如下:

package dbprovider

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"rest/article"
	"log"
)

type DBOperations interface {
	AddArticle(article *article.Article)
}

type DBManager struct {
	db            *gorm.DB
	isInitialized bool
}

var dbManagerInstance = new()

func GetDBManager() DBManager {
	return dbManagerInstance
}

func new() DBManager {
	localDbRef, err := gorm.Open("sqlite3", "../articles.db")
	if err != nil {
		panic("Error initializing db")
	} else {
		log.Print("DB Initialized successfully")
	}
	return DBManager{db: localDbRef, isInitialized: true}
}

func (dbManager DBManager) AddArticle(article article.Article) (err error) {
	if dbManager.isInitialized {
		tx := dbManager.db.Begin()
		//dbManager.db.NewRecord(article)
		//dbManager.db.Commit()
		tx.NewRecord(article)
		tx.Commit()
		errs := dbManager.db.GetErrors()
		if len(errs) > 0 {
			err = errs[0]
		} else {
			log.Print("No error in this transactions")
		}

	}
	return
}

根据新的答案,我更新了这个问题,包括答案。但我有一些疑问。如何捕获并返回gorm.Create(..)的异常?

英文:

I referred a few code samples of how to create go singleton but I wish to have methods in those and call them on their singleton reference. My Code is as follows

package dbprovider

import (
	"github.com/jinzhu/gorm"
	_"github.com/jinzhu/gorm/dialects/sqlite"
	"rest/article"
	"log"
)

type DBOperations interface {
	AddArticle(article *article.Article)
}

type DBManager struct {
	db            *gorm.DB
	isInitialized bool
}

var dbManagerInstance = new()

func GetDBManager() DBManager {
	return dbManagerInstance
}

func new() DBManager {
	localDbRef, err := gorm.Open("sqlite3", "../articles.db")
	if (err != nil) {
		panic("Error initializing db")
	} else {
		log.Print("DB Initialized successfully")
	}
	return DBManager{db:localDbRef, isInitialized:true}
}

func (dbManager DBManager)  AddArticle(article article.Article) (err error) {
	if (dbManager.isInitialized) {
		tx := dbManager.db.Begin()
		//dbManager.db.NewRecord(article)
		//dbManager.db.Commit()
		tx.NewRecord(article)
		tx.Commit()
		errs := dbManager.db.GetErrors()
		if (len(errs) > 0) {
			err = errs[0]
		} else {
			log.Print("No error in this transactions")
		}

	}
	return
}

With a new answer I have updated this question including answer. But I have a few queries. How to cathc and return exception from gorm.Create(..)

答案1

得分: 20

一种方法是创建一个带有方法的导出接口,并使实现类型未导出。创建一个接口类型的全局变量,并使用包的init()函数进行初始化。由于包的init()函数只会运行一次,所以不需要任何同步操作。

包的init()函数会在你引用包中的任何内容之前,由运行时自动执行一次。详细信息请参见规范:包初始化

例如:

package dbprovider

type Manager interface {
    AddArticle(article *article.Article) error
    // 添加其他方法
}

type manager struct {
    db *gorm.DB
}

var Mgr Manager

func init() {
    db, err := gorm.Open("sqlite3", "../articles.db")
    if err != nil {
        log.Fatal("Failed to init db:", err)
    }
    Mgr = &manager{db: db}
}

func (mgr *manager) AddArticle(article *article.Article) (err error) {
    mgr.db.Create(article)
    if errs := mgr.db.GetErrors(); len(errs) > 0 {
        err = errs[0]
    }
    return
}

使用它:

import "dbprovider"

if err := dbprovider.Mgr.AddArticle(someArticle); err != nil {
    // 处理错误
}

你也可以不使用init()函数,例如:

var Mgr = newManager()

func newManager() Manager {
    db, err := gorm.Open("sqlite3", "../articles.db")
    if err != nil {
        log.Fatal("Failed to init db:", err)
    }
    return &manager{db: db}
}

通过这种方式,你可以决定将newManager()导出,让包的用户决定使用共享的Mgr实例,或者他们可以创建另一个Manager,例如用于测试目的。

注意: Mgr是一个导出的全局变量,其他包可以给它赋予新值(例如dbprovider.Mgr = nil)。如果你想避免这种情况,你需要将其设为未导出,并为其提供一个"getter"函数,例如:

var mgr = newManager()

func Mgr() Manager { return mgr }

使用它:

err := dbprovider.Mgr().AddArticle(someArticle)
英文:

One way is to create an exported interface with the methods, and make the implementing type unexported. Create a global variable of the interface type, and initialize it with a package init() function. You don't need any synchronization as the package init() function will run only once, safely.

Package init() functions are executed once, automatically, by the runtime, before you could refer to anything from the package. For details, see Spec: Package initialization.

For example:

package dbprovider

type Manager interface {
	AddArticle(article *article.Article) error
	// Add other methods
}

type manager struct {
	db *gorm.DB
}

var Mgr Manager

func init() {
	db, err := gorm.Open("sqlite3", "../articles.db")
	if err != nil {
		log.Fatal("Failed to init db:", err)
	}
	Mgr = &manager{db: db}
}

func (mgr *manager) AddArticle(article *article.Article) (err error) {
	mgr.db.Create(article)
	if errs := mgr.db.GetErrors(); len(errs) > 0 {
		err = errs[0]
	}
	return
}

Using it:

import "dbprovider"

if err := dbprovider.Mgr.AddArticle(someArticle); err != nil {
    // Handle error
}

You could also do it without an init() function, e.g.:

var Mgr = newManager()

func newManager() Manager {
	db, err := gorm.Open("sqlite3", "../articles.db")
	if err != nil {
		log.Fatal("Failed to init db:", err)
	}
	return &manager{db: db}
}

With this you may decide to make newManager() exported and users of your package could decide to use the shared Mgr instance, or they could create another Manager, e.g. for testing purposes.

Notes: Mgr is an exported global variable, and it is possible to assign a new value to it by other packages (e.g. dbprovider.Mgr = nil). If you want to avoid this, you have to make it unexported, and provide a "getter" function for it, e.g.:

var mgr = newManager()

func Mgr() Manager { return mgr }

And using it:

err := dbprovider.Mgr().AddArticle(someArticle)

huangapple
  • 本文由 发表于 2016年12月21日 16:01:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/41257847.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定