我们知道数据库的配置中有支持对默认数据库的配置,因此DB对象及Model对象在初始化的时候已经绑定到了特定的数据库上。运行时切换数据库有几种方案(假如我们的数据库有user用户数据库和order订单数据库):

  1. 通过不同的配置分组来实现。这需要在配置文件中配置不同的分组配置,随后在程序中可以通过g.DB("分组名称")来获取特定数据库的单例对象。
  2. 通过运行时DB.SetSchema方法切换单例对象的数据库,需要注意的是由于修改的是单例对象的数据库配置,因此影响是全局的:

    g.DB().SetSchema("user-schema")
    g.DB().SetSchema("order-schema")
  3. 通过链式操作Schema方法创建Schema数据库对象,并通过该数据库对象创建模型对象并执行后续链式操作:

    g.DB().Schema("user-schema").Model("user").All()
    g.DB().Schema("order-schema").Model("order").All()
  4. 也可以通过链式操作Model.Schema方法设置当前链式操作对应的数据库,没有设置的情况下使用的是其DB或者TX默认连接的数据库:

    g.Model("user").Schema("user-schema").All()
    g.Model("order").Schema("order-schema").All()

    注意两种使用方式的差别,前一种方式来自于Schema对象创建Model对象后执行操作;后一种方式是通过修改当前Model对象操作的数据库名称达到切换数据库的目的。

  5. 此外,假如当前数据库操作配置的用户有权限,那么可以直接通过表名中带数据库名称实现跨域操作,甚至跨域关联查询:

    // SELECT * FROM `order`.`order` o LEFT JOIN `user`.`user` u ON (o.uid=u.id) WHERE u.id=1 LIMIT 1
    g.Model("order.order o").LeftJoin("user.user u", "o.uid=u.id").Where("u.id", 1).One()
  • No labels

7 Comments

  1. SetSchema 是实际的数据库名
  2. Ray

    Schema 填的是数据库名 ,同盟数据库怎么办呢

  3. 同问:这里的数据库切换,对于mysql来说,设置Schema是切换database,但对于postgresql来说,看上去是切换Schema,但是其实也是切换database,那么对于postgresql,schema是归属于database的,那么这里如果想切换schema而不是切换数据库应该如何实现呢?   比如我有个数据库test,下面有很多个schema,我需要动态切换schema

      1. 谢谢回复,我了解到这个namespace的配置参数,但是尝试过后发现,目前存在以下问题:

        1. 仅支持配置方式设置对应的namespace来指明shema,无法在运行时切换schema,我期望的是例如g.DB().Schema("test").Namespace("xxx")这种形式可以直接切换。
        2. 该namespace代表了schema, 而原有的schema是指代database name也就是所说的catalog,既然如此,那么为何不将原有的schema名称改成database呢?用database本义来指代catalog,schema本义来指代schema不好吗?

        连接中的方式满足不了我的需求,我阅读了源码进行了修改。

        目前涉及的改造有:

        1. 将原本的schema命名全部替换修改成database,例如原本的g.DB().Schema("testdb") 修改为g.DB().Database("testdb")这种使用方式,不会造成歧义,同时支持链式动态切换schema,在Core和Model struct定义中将原本schema字段修改为database字段以存储数据库名称,额外添加一个schema字段来存储schema也就是namespace名称;同时修改了创建数据库的方法如openSqlDb, Open等以支持在oracle、pgsql、db2、mssql中通过修改dsn 设置currentSchema或者search_path参数配置方式来修改会话级别的默认查找的schema。
        2. pgsql driver目前还不支持 InsertOrUpdate 的Save操作方式,但实际上pgsql早已经支持insert into on conflict (...) do update set 语法,因此在pgsql的驱动中进行改造以支持save方式保存数据;
        3. pgsql dirver中看到有个TODO写了有重复id的问题未解决,这个实际上是因为查询表字段时未正确区分schema导致查出来多个schema中同名table查出来会有同名字段问题,其实不只是id,如果多个schema中的table有一模一样的定义,那么查出来的字段都会有重复的,因此加入schema的动态区分(默认只查public的schema即可)可以正确获取TableFields。下面是代码参考:
        structureSql := fmt.Sprintf(`
        SELECT a.attname AS field, t.typname AS type,a.attnotnull as null,
            (case
        		when d.contype = 'p' then 'pri'
        		when d.contype = 'f' then 'for'
        		else '' end)  as key
              ,ic.column_default as default_value,b.description as comment
              ,coalesce(character_maximum_length, numeric_precision, -1) as length
              ,numeric_scale as scale
        FROM pg_attribute a
                 left join pg_class c on a.attrelid = c.oid
                 inner join pg_namespace n ON c.relnamespace = n.oid
                 left join pg_constraint d on d.conrelid = c.oid and a.attnum = d.conkey[1]
                 left join pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid
                 left join pg_type t ON a.atttypid = t.oid
                 left join information_schema.columns ic on ic.column_name = a.attname and ic.table_name = c.relname and ic.table_schema = n.nspname
        WHERE n.nspname = '%s' and c.relname = '%s' and a.attisdropped is false and a.attnum > 0
        ORDER BY a.attnum`,
        		usedSchema,
        		gstr.Trim(table, quoteChar),
        )
          1. 等我测试好了再提交pr吧,还在改造测试中。