diff --git a/go.mod b/go.mod index d3bfca1..da5f3a4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19.2 require ( qoobing.com/gomod/gorm v0.0.2 qoobing.com/gomod/log v1.4.0 - qoobing.com/gomod/redis v1.3.8 + qoobing.com/gomod/redis v1.3.9 ) require ( diff --git a/go.sum b/go.sum index 52db605..667e372 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ qoobing.com/gomod/log v1.4.0 h1:VSdV8Fm2roCkSwOTunmNrH2kl14KOCYavsh4tJ/SGj0= qoobing.com/gomod/log v1.4.0/go.mod h1:rNXuq0d/EWog4+8hIEVGvkusLD/pzafYBQo6w+Evv6A= qoobing.com/gomod/redis v1.3.8 h1:9REpvyKXsOWBl3KG1oCGpJcITf7Mz1SjqCeIM65k9cQ= qoobing.com/gomod/redis v1.3.8/go.mod h1:5j9kopj3CY1qf70OQ8IHQjDKHUl72rj0LhQgTBpl2BU= +qoobing.com/gomod/redis v1.3.9 h1:wtojzRP7T+p+MAzkAUrU8GLNDUMU84ZJ3U/cWPrDby8= +qoobing.com/gomod/redis v1.3.9/go.mod h1:5j9kopj3CY1qf70OQ8IHQjDKHUl72rj0LhQgTBpl2BU= qoobing.com/gomod/str v1.0.1/go.mod h1:gbhN2dba/P5gFRGVJvEI57KEJLlMHHAd6Kuuxn4GlMY= qoobing.com/gomod/str v1.0.5 h1:AXEB8k/yhepLK5jVez+WL4sWVuCFb8pWAgmo3nvt96A= qoobing.com/gomod/str v1.0.5/go.mod h1:gbhN2dba/P5gFRGVJvEI57KEJLlMHHAd6Kuuxn4GlMY= diff --git a/model.go b/model.go index 963eb18..55f9d4f 100644 --- a/model.go +++ b/model.go @@ -2,6 +2,7 @@ package model import ( "fmt" + "reflect" "sync" "qoobing.com/gomod/gorm" @@ -10,11 +11,16 @@ import ( ) var ( - instances = map[string]instance{} - errNoModelFound = errorsWrap("named model(%s) not found") - errModeAlreadyInit = errorsWrap("named model(%s) already initilized") - errOpenDBFailed = errorsWrap("open database failed: %s") - errOpenRedisFailed = errorsWrap("open redis failed: %s") + modelsDb = map[string]*modelDb{} + modelsRedis = map[string]*modelRedis{} + defaultDbName = "" + defaultRedisName = "" + + errNoModelFound = errorsWrap("named model(%s) not found") + errModeAlreadyInit = errorsWrap("named model(%s) already initilized") + errOpenDBFailed = errorsWrap("open database failed: %s") + errOpenRedisFailed = errorsWrap("open redis failed: %s") + errUnknownModelType = errorsWrap("unknown model type: %s") ) func errorsWrap(base string) func(...any) error { @@ -23,154 +29,115 @@ func errorsWrap(base string) func(...any) error { } } -const ( - tx_status_none = 0 - tx_status_doing = 1 - tx_status_commit = 2 - tx_status_rollback = 3 +type ( + DbConfig = gorm.Config + RedisConfig = redis.Config + Configurable interface { + *DbConfig | *RedisConfig + } ) -type Model struct { - mu sync.Mutex - dbCfg *gorm.Config - redisCfg *redis.Config - - db *gorm.DB - dbTxStatus int - redis redis.Conn - redisPool *redis.Pool -} - -func (m *Model) Begin() { - if m.DB() == nil { - panic("unreachable code, db is uninitialized") - } else if m.dbTxStatus != tx_status_none { - panic("unreachable code, begin transaction towice???") - } else { - m.dbTxStatus = tx_status_doing - m.db = m.db.Begin() - } -} - -func (m *Model) Commit() error { - if m.DB() == nil { - panic("unreachable code, db is uninitialized") - } else if m.dbTxStatus != tx_status_doing { - return nil - } else if err := m.db.Commit().Error; err != nil { - m.dbTxStatus = tx_status_rollback - return err - } else { - m.dbTxStatus = tx_status_commit - return nil - } -} - -func (m *Model) Close() { - if m.redis != nil { - m.redis.Close() - } - if m.db == nil { - m.dbTxStatus = tx_status_none - } else if m.dbTxStatus == tx_status_doing { - m.db.Rollback() - m.dbTxStatus = tx_status_rollback - } -} - -func (m *Model) DB() *gorm.DB { - return m.db -} - -func (m *Model) Redis() redis.Conn { - return m.redis -} - -func (m *Model) RedisPool() *redis.Pool { - return m.redisPool -} - -type instance struct { - db *gorm.DB - dbCfg *gorm.Config - redis *redis.Pool - redisCfg *redis.Config -} - -// OpenDefault open 'default' model -func OpenDefault() (*Model, error) { - return Open("default") -} - -// OpenDefault open named model, if err is nil, caller MUST call m.Close() in defer. -func Open(name string) (m *Model, err error) { - // check initialize - cfg, ok := instances[name] - if !ok { - return nil, errNoModelFound(name) +type ( + modelDb struct { + mu sync.Mutex + db *gorm.DB + dbCfg *gorm.Config } - // open redis - var rds redis.Conn - if cfg.redisCfg == nil { - // don't need to open redis - } else if cfg.redis != nil { - rds = cfg.redis.Get() - } else if cfg.redis = redis.NewPool(*cfg.redisCfg); cfg.redis == nil { - return nil, errOpenRedisFailed("new redis pool return nil") - } else { - rds = cfg.redis.Get() + modelRedis struct { + mu sync.Mutex + redis redis.Conn + redisCfg *redis.Config + redisPool *redis.Pool } - defer func() { - if m == nil && rds != nil { - rds.Close() - } - }() - - // open db - var db *gorm.DB - if cfg.dbCfg == nil { - // don't need to open database - } else if cfg.db != nil { - db = gorm.NewSession(cfg.db) - } else if cfg.db, err = gorm.NewDB(*cfg.dbCfg); err != nil { - return nil, errOpenDBFailed(err) - } else { - db = gorm.NewSession(cfg.db) - } - - // success - m = &Model{ - dbCfg: cfg.dbCfg, - redisCfg: cfg.redisCfg, - db: db, - redis: rds, - redisPool: cfg.redis, - } - return m, nil -} - -// Init init default database config & redis config -func InitDefault(_defaultDb *gorm.Config, _defaultRds *redis.Config) error { - return Init("default", _defaultDb, _defaultRds) -} +) // Init init named database config & redis config -func Init(name string, dbCfg *gorm.Config, rdsCfg *redis.Config) error { - _, ok := instances[name] - if ok { - return errModeAlreadyInit(name) +func Init[Config Configurable](name string, cfg Config) error { + var ( + dbCfg *gorm.Config + redisCfg *redis.Config + inCfgType = reflect.TypeOf(cfg) + ) + + if inCfgType == reflect.TypeOf(dbCfg) { + dbCfg = reflect.ValueOf(cfg).Interface().(*gorm.Config) + _, ok := modelsDb[name] + if ok { + return errModeAlreadyInit("db:" + name) + } + db, err := gorm.NewDB(*dbCfg) + if err != nil { + return errOpenDBFailed(err) + } + modelsDb[name] = &modelDb{ + db: db, + dbCfg: dbCfg, + } + if defaultDbName == "" || (defaultDbName != "" && name == "default") { + defaultDbName = name + } + log.Infof("success init model(%s) database:%s", name, dbCfg.GetSecDsn()) + return nil } - instances[name] = instance{ - dbCfg: dbCfg, - redisCfg: rdsCfg, + + if inCfgType == reflect.TypeOf(redisCfg) { + redisCfg = reflect.ValueOf(cfg).Interface().(*redis.Config) + _, ok := modelsRedis[name] + if ok { + return errModeAlreadyInit("redis:" + name) + } + redisPool := redis.NewPool(*redisCfg) + if redisPool == nil { + return errOpenRedisFailed("new redis pool return nil") + } + modelsRedis[name] = &modelRedis{ + redisPool: redisPool, + redisCfg: redisCfg, + } + if defaultRedisName == "" || (defaultRedisName != "" && name == "default") { + defaultRedisName = name + } + log.Infof("success init model(%s) database:%s", name, redisCfg.GetSecDsn()) + return nil } - if dbCfg != nil && dbCfg.Debug { - log.Infof("init model(%s) database:%s", name, gorm.GetSecDsn(dbCfg)) - } - if rdsCfg != nil && rdsCfg.Debug { - log.Infof("init model(%s) redis:%s", name, redis.) - } - log.Infof("init model(%s) done", name) - return nil + + log.Infof("failed init model(%s), unknown model type: %s", name, inCfgType.Name()) + return errUnknownModelType(inCfgType.Name()) +} + +func Db() *gorm.DB { + return DbOf(defaultDbName) +} + +func DbOf(name string) *gorm.DB { + m, ok := modelsDb[name] + if !ok { + panic(errNoModelFound(name)) + } + return gorm.NewSession(m.db) +} + +func Redis() redis.Conn { + return RedisOf(defaultRedisName) +} + +func RedisOf(name string) redis.Conn { + m, ok := modelsRedis[name] + if !ok { + panic(errNoModelFound(name)) + } + return m.redisPool.Get() +} + +func RedisPool() *redis.Pool { + return RedisPoolOf(defaultRedisName) +} + +func RedisPoolOf(name string) *redis.Pool { + m, ok := modelsRedis[name] + if !ok { + panic(errNoModelFound(name)) + } + return m.redisPool }