数据库连接模式

Database connection pattern

我用mysql-native。此驱动程序是支持 vibed 的连接池。在 dlang 新闻组 mysql-native 开发者 Nick Sabalausky 写道:

"If you're using a connection pool, you shouldn't need to worry about closing the connection. The whole point is that the connections stay open until you need to use one again. When your program ends, then connections will close by themselves."

"You create the pool once (wherever/whenever you want to). Then, every time you want to use the database you obtain a connection by calling MySqlPool.lockConnection."

"Calling 'close' will always close the connection. If you got you connection from the pool, then it will automatically return to the pool when you're no longer using it. No need to do anything special for that."

关于pool应该怎么做的问题?我已经阅读了有关单例模式的内容,但我无法理解是不是这种情况。

我写了下一个代码:

database class:
import std.stdio;
import std.string;
import mysql;
import vibe.d;

import config;
import user;

class Database
{
    Config config;
    MySqlPool mydb;
    Connection connection;

    this(Config config)
    {
        this.config = config;
        mydb = new MySqlPool(config.dbhost, config.dbuser, config.dbpassword, config.dbname, config.dbport);    
    }

    void connect()
    {
        if(connection is null)
        {
            connection = mydb.lockConnection();
        }
        scope(exit) connection.close();
    }

}

用户class/struct:

module user;

import mysql;
import vibe.d;

struct User
{
    int id;
    string login;
    string password;
    string usergroup;
}
    void getUserByName(string login)
    {
        User user;
        Prepared prepared = prepare(connection, `SELECT id, login, password, usergroup from users WHERE login=?`); // need to get connection accessible here to make request to DB
        prepared.setArgs(login);
        ResultRange result = prepared.query();
        if (result.empty) 
            logWarn(`user: "%s" do not exists`, login);
        else
        {
                Row row = result.front;
                user.id = row[0].coerce!(int);
                user.login = row[1].coerce!string;
                user.password = row[2].coerce!string;
                user.usergroup = row[3].coerce!string;

        logInfo(`user: "%s" is exists`, login);
        }

    }

我无法理解访问 connection 实例的正确方法的问题。在 users 结构中创建每个新的数据库连接 class 似乎是非常愚蠢的想法。但是如何以更好的方式做到这一点?使 Connection connection 全球化?好不好?或者有更正确的方法?

一种方法是将连接传递给需要它的函数。因此,您将重构 getUserByName() 以将连接作为参数。

另一种选择是使用 DAO pattern 。您的 DAO 的构造函数 class 会将连接作为主要参数之一,所有方法都将使用它来进行数据库操作。

scope(exit) connection.close();

删除该行。它正在关闭您刚刚在 connect 函数 returns 之前从池中接收到的连接。您所做的只是打开一个连接,然后立即再次关闭它。

更改 getUserByName 以将连接作为参数(通常作为第一个参数)。通常,任何需要调用 getUserByName 的代码都应该打开一个连接,或者通过 lockConnection 从池中获取连接,然后将该连接传递给 getUserByName 和任何其他 DB-related 它需要使用的功能。然后,在您的代码完成调用 getUserByName (以及它需要调用的任何其他数据库函数)之后,您要么不再担心连接并让您的 vibed 光纤完成(如果您使用的是 vibed 和从池中获得连接)或者你 close 连接(如果你没有从 vibed 池中获得连接)。