架构交换支持Go GORM

时间:2017-01-12 16:44:41

标签: sql postgresql go orm

我正在编写一个REST API,我需要动态地将连接部署到多个模式。

示例:我有两个模式,我需要根据哪个用户尝试使用数据进行更改。

我们的想法是拥有许多其他方案,每个用户一个。

我看到了这个问题,但使用的示例是static,需要找到动态更改的方法。我将用户架构放在JWT令牌中。

我的项目:Panda-API

有什么建议吗?

1 个答案:

答案 0 :(得分:0)

选项1

您可以更改database.GetConnection()方法以接收用户名并直接连接到数据库,而无需更改所有服务和模型。您将用户存储在security_middleware.go上的gin.Context对象中,因此您可以从控制器上获取它并将其传递给服务,以便它们获得相应的数据库连接。

但是为此,你必须删除你必须存储DB对象的Singleton模式并创建一个DB对象池,可能在map[string]*DB中,而不是在服务包中缓存DB对象,缓存数据库包中的所有用户数据库对象。

您的database/database.go文件类似于:

// Add sync import to handle concurrent access to the cache
import "sync"

// ... existent code

// DB objects cache
type DBs struct {
    Cache map[string]*gorm.DB
    sync.RWMutex
}

var dbs *DBs

// Init cache
func init() {
    dbs = DBs{
        Cache: make(map[string]*gorm.DB)
    }
}

func GetConnection(username string) *gorm.DB {  
    // Try to get connection from the cache
    dbs.RLock()
    if db, ok := dbs.Cache[username]; ok {
        dbs.RUnlock()
        return db
    }

    // Figure out DB_NAME dynamically here, based on username...
    //...
    dbName := figuredOutDB_NAME    

    db, err := gorm.Open(DB_DATABASE, "host=" + DB_HOST + " user=" + DB_USER + " dbname=" + dbName + " sslmode=" + DB_SSL_MODE + " password=" + DB_PASSWORD)

    if err != nil {
        panic(err)
    }

    //Ativa log de todas as saidas da conexão (SQL)
    db.LogMode(GetENVLogMode())
    //Seta o maximo de conexões
    db.DB().SetMaxIdleConns(DB_MAX_CONNECTION)
    db.DB().SetMaxOpenConns(DB_MAX_CONNECTION)

    DropTablesIfExists(db)
    AutoMigrate(db)
    AutoPopulate(db)
    AddForeignKeys(db)

    // Save connection to cache
    dbs.Lock()
    dbs.Cache[username] = db
    dbs.Unlock()

    return db
}

// ... and so on

然后删除services/services.go文件,因为它没用。 并更改您的服务方法以接收用户名作为参数,而不是使用Con变量,每次都调用Con := database.GetConnection(username)

我希望能让您了解可能的解决方案。当然可能还有其他选择,但这就是我现在能想到的。

我在这种方法中遇到的问题是,您将为系统中的每个用户打开一个连接(和一个gorm.DB对象),不确定您有多少用户期望,但它可能是个问题。

选项2

另一种解决方案是对服务进行相同的更改,以便它们将用户作为所有方法的参数接收,但不是获取新连接,而是将用户名/ db名称设置为可用于的自定义模型属性实现您自己的Model.TableName()方法,该方法使用该属性返回schema.table格式。

因此,您将模型更改为具有setter的私有属性,例如:

type Person struct {
   schemaName string

   // ... existent properties.
}

func (p *Person) SetUser(u string) string {
    // Figure out the schema name from the username
    //...

    p.schemaName = schema
}

func (p *Person) TableName() string {
    return p.schemaName + ".persons"
}

然后,在您的服务上,每次创建新模型实例时都会设置用户:

func GetPeople(pag helpers.Pagination, q url.Values, username string) models.People {

    var people models.People
    (&people).SetUser(username)

    db := Con

    // ... and so on

这是我现在可以想到的两种可能的解决方案。可能会有更多更好,但希望有所帮助。