Schema exchange support Go GORM 的翻译结果是:支持 Go GORM 的模式交换。

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

Schema exchange support Go GORM

问题

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

例如:我有两个模式,根据尝试使用数据的用户来进行更改。

想法是有许多其他的模式,每个用户一个。

我看到了这个问题,但是示例使用的是静态,我需要找到一种动态更改的方法。我将把用户模式放在JWT令牌中。

我的项目:Panda-API

有什么建议吗?

英文:

I'm writing a REST API in which I need to dynamically deploy connections to multiple schemas.

Example: I have two schemas in which I need to change depending on which user is trying to consume the data.

The idea is to have many other schemes, one for each user.

I saw this issue but the example used is static needed to find a way to dynamically change. I'll put the user schema in the JWT token.

My project: Panda-API

Any suggestion?

答案1

得分: 1

选项1

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

但是,为了实现这一点,你必须移除你存储DB对象的单例模式,并创建一个DB对象池,可以使用map[string]*DB来实现,而不是在services包中缓存DB对象,你可以在database包中缓存所有用户-DB对象。

你的database/database.go文件将如下所示:

// 添加sync导入以处理对缓存的并发访问
import "sync"

// ...现有的代码

// DB对象缓存
type DBs struct {
    Cache map[string]*gorm.DB
    sync.RWMutex
}

var dbs *DBs

// 初始化缓存
func init() {
    dbs = DBs{
        Cache: make(map[string]*gorm.DB),
    }
}

func GetConnection(username string) *gorm.DB {
    // 尝试从缓存中获取连接
    dbs.RLock()
    if db, ok := dbs.Cache[username]; ok {
        dbs.RUnlock()
        return db
    }

    // 在这里动态确定DB_NAME,基于用户名...
    // ...
    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)
    }

    // 打开连接的所有输出都记录日志(SQL)
    db.LogMode(GetENVLogMode())
    // 设置最大空闲连接数
    db.DB().SetMaxIdleConns(DB_MAX_CONNECTION)
    db.DB().SetMaxOpenConns(DB_MAX_CONNECTION)

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

    // 将连接保存到缓存中
    dbs.Lock()
    dbs.Cache[username] = db
    dbs.Unlock()

    return db
}

// ...以此类推

然后删除`services/services.go`文件因为它将变得无用并且将你的服务方法更改为接收用户名作为参数并且每次调用时使用`Con := database.GetConnection(username)`

希望这给你提供了一个可能的解决方案的思路当然可能还有其他选项但这是我现在能想到的

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

选项2
--------
另一种解决方案是对服务进行相同的更改使其在所有方法中接收用户作为参数但是不获取新的连接而是将用户名/数据库名称设置为自定义模型属性你可以使用该属性来实现自己的`Model.TableName()`方法该方法使用该属性返回`schema.table`格式

因此你需要更改你的模型添加一个带有setter的私有属性例如

```go
type Person struct {
   schemaName string
   
   // ...现有属性。
}

func (p *Person) SetUser(u string) string {
    // 从用户名中确定模式名称
    //...

    p.schemaName = schema
}

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

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

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

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

    db := Con

    // ...以此类推

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

<details>
<summary>英文:</summary>

OPTION 1
--------

You can change the database.GetConnection() method to receive the username and connect directly to the database without having to change all services and models. You&#39;re storing the user in the gin.Context object on `security_middleware.go`, so you can get it from there on the controllers and pass it to the services so they get the corresponding DB connection. 

But for this, you have to remove the Singleton pattern you have to store the DB object and create a pool of DB objects, maybe in a `map[string]*DB` and instead of caching the DB object in the services package, you cache all user-DB objects in the database package. 

Your `database/database.go` file will look something like: 

    // Add sync import to handle concurrent access to the cache
    import &quot;sync&quot;
    
    // ... 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, &quot;host=&quot; + DB_HOST + &quot; user=&quot; + DB_USER + &quot; dbname=&quot; + dbName + &quot; sslmode=&quot; + DB_SSL_MODE + &quot; password=&quot; + DB_PASSWORD)
    
        if err != nil {
            panic(err)
        }
    
        //Ativa log de todas as saidas da conex&#227;o (SQL)
        db.LogMode(GetENVLogMode())
        //Seta o maximo de conex&#245;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


Then remove the `services/services.go` file as it would be useless. 
And change your services methods to receive the username as a param and instead of using the `Con` variable, call `Con := database.GetConnection(username)` every time. 

I hope that gives you an idea of a possible solution. Of course there may be other options, but that&#39;s what i can think on right now. 

The problem i see with this method is that you&#39;ll have one connection open (and a gorm.DB object) for each user in the system, not sure how many users you&#39;re expecting, but it can be a problem. 


OPTION 2
--------

Another solution is to follow the same changes on the services so they receive the user as a param on all methods, but instead of getting a new connection, set the username/db name to a custom model property that you can use to implement your own `Model.TableName()` method that uses that property to return the `schema.table` format. 

So you change your models to have a private property with a setter, like: 

    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 + &quot;.persons&quot;
    }

Then, on your services you set the user every time you create a new model instance: 


    func GetPeople(pag helpers.Pagination, q url.Values, username string) models.People {
    
    	var people models.People
        (&amp;people).SetUser(username)
    
    	db := Con
    
        // ... and so on


These are 2 possible solutions i can think on now. There may be more and better, but hope that helps. 



</details>



huangapple
  • 本文由 发表于 2017年1月13日 00:44:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/41618602.html
匿名

发表评论

匿名网友

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

确定