Дизайн класс базы данных


Я пишу на SQLite обертки для C++. У меня есть этот класс, представляющий собой базу данных:

  class Database {
    sqlite3 *db;
  public:
    Database(const std::string& file_path, bool readonly = false) throw(SQLiteException);
    ~Database();

    std::vector<std::map<std::string, Value> > Query(const std::string& query) throw(SQLiteException);
    std::vector<std::map<std::string, Value> > Query(const std::string& query, const std::vector<Value>& bindings) throw(SQLiteException);
  };

Где значение - это контейнер для инт, дабл, СТД::строка или СТД::вектор (потому что возвращаемый тип не известен во время компиляции).

Я беспокоюсь о возвращаемое значение типа базы данных:запросы. Как я могу упростить это?



4386
3
задан 17 сентября 2011 в 11:09 Источник Поделиться
Комментарии
1 ответ

Типы должны быть известны во время компиляции. К сожалению, чтобы быть гибкой, нужно искать типы arbitory во время выполнения для БД. Так что либо вы должны использовать некоторую форму шаблон мета-программирования, или вы можете использовать тип Variant. Я хотел бы пойти на что-то вроде увеличить любой предмет или увеличить вариант это позволит вам вернуть произвольных типов.

Я не думаю, что вы хотите вернуть результат из БД после создания запроса. Вы хотите вернуть объект запроса, который позволит вам связать вещи и выполнение запроса на последней стадии. Это потому что в большинство систем БД создание запросов относительно дорого.

И когда вы выполните запрос, который вы хотите использовать концепцию указатель (итератор) двигаться за результат, а не создание полного результата за один раз (как это может быть огромный).

int main();
{
Database db("Bla", "Passowrd", true);
Query getName = db.query("SELECT Name,Address,Phone "
"FROM Users Where Age=%d and Sex=%s");

// Allow user to bind values to the query and execute.
// As building the query can be expensive. So you want to re-use it.
Result age25 = getName.execute(bind() % 25 % "M"));

// Re-use the Query object for the next query (Note bind should allow multiple param)
Result age31 = getName.execute(bind() % 31 % "F"));

// Rather than returning all the results from the query in one go
// Have the result object provide an `input iterator` allowing you to retrieve the
// the rows as required. That way you do not need to copy out huge objects all in
// one go. You can scan over the input until you get the bit you want (potentially)
// only copying selected parts out of the result.

if (!age25) // convert object to boolean to test for OK
{ // Like a std::stream object. This way if the query has
// an error you can quickly an easily test for it.

std::cout << age25.error() << "\n";
}
else
{
for(RowIter row = RowIter(age25); row != RowIter(); ++row)
{ // ^^^^^^^^^ like std::istream_iteator
// an empty object is end()

std::cout << db_cast<std::string>(row->get("Name")) << " "
<< db_cast<std::string>(row->get("Address")) << " "
<< db_cast<int>(row->get("Phone")) << "\n";
// ^^^^^^^^^^^^
// Convert the object into the correct type
// using a cast like syntax.

}
}
}

Также не ограничивайте его в SQLite.
Сделать это так легко расширяемой с другими типами БД.

Я бы пошел с префиксная нотация на имя БД:

DataBase    db1("sqlite://FileName1", "OptionalPassword", flags);
DataBase db2("mysql://db@host.bob.com", "Password", flags);
Database db3("oracle://plop.foo.bar");

Внутренне можно использовать интеллекту шаблон с абстрактными различных типов БД. Сейчас тебе просто реализовать SQLite версии сделать другие генерировать исключение.

4
ответ дан 17 сентября 2011 в 02:09 Источник Поделиться