首页 > 解决方案 > 编写一个可以使用 2 个数据库的应用程序?

问题描述

我正在尝试在 Go 中编写一个能够基于配置连接到 oracle 和 MySQL 的应用程序。我现在遇到的问题是当我使用准备好的语句时。例如,考虑下面的查询

Select * from data_table where id = 1

MySQL和oracle中对应的prepared statement如下

MySQL -> Select * from data_table where id = ?
ORACLE -> Select * from data_table where id = :val1

如果是这样,我维护了 2 组查询并根据配置选择查询。有一个更好的方法吗?

我想避免保留两组查询的麻烦

标签: mysqloraclego

解决方案


很大程度上是通过使用接口。

假设您创建了一个 Web 应用程序并希望显示用户。

首先,您将定义一个接口,例如

type Creator interface{
  Create(u User)(User,error)
}

type Reader interface{
  Read(k PrimaryKey)(User, error)
  ListAll()([]User,error)
  ListPaginated(page, offset int)([]User,error)
}

type Updater interface{
  Update(u User)(User, error)
  UpdateByKey(k PrimaryKey, u User)(User, error)
  UpdateMany(...User)error
}

type Deleter interface{
  Delete(u User)error
  DeleteMany(u ...User)error
  DeleteByKey(keys ...PrimaryKey)error
}

type CRUD interface {
  Creator
  Reader
  Updater
  Deleter
}

接下来,为您想要支持的每种数据库类型实现 CRUD 接口。

现在,您可以创建一个处理程序:


// ListHandler uses an interface instead of a concrete type to
// retrieve the data from the databases.
// Not only does this approach make it possible to provide different
// implementations, but it makes unit testing way easier.
//
// "Thou Shalt Write Tests"
func ListHandler(rdr Reader) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        // Pagination ommited for brevity

        // Note that the handler is agnostic of the underlying implementation.
        u, err := rdr.ListAll()
        if err != nil {
            log.Printf("ListHandler: error retrieving user list: %s", err)
            // Do not do this in production! It might give an attacker information
            // Use a static error message instead!
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        if err := json.NewEncoder(w).Encode(u); err != nil {
            log.Printf("ListHandler: error encoding user list to JSON: %s", err)
            // See above
            http.Error(w, err.Error(), http.StatusInternalServerError)
        }
    })
}

并设置如下:


func main() {
    // Get your config
    // Then simply use an implementation of CRUD

    var dbConn CRUD
    switch cfg.DbType {
    case "myql":
        // returns your implementation of CRUD using MySQL
        dbConn = createMySQLConnector(cfg)
    case "oracle":
        // returns your implementation of CRUD using Oracle
        dbConn = createOracleConnector(cfg)
    }

    http.Handle("/users/", ListHandler(dbConn))

    log.Fatal(http.ListenAndServe("0.0.0.0:8080", nil))
}

hth


推荐阅读