Мой простенький SQLiteWrapper. Покритикуйте.
От: Daimond Россия  
Дата: 12.07.05 07:00
Оценка:
Моя версия классов, обертывающих API SQLite.

Главной целью была создание классов, на основе которых можно было бы свести обращения программы к базе данных и получение инф-ции в простые функции-члены и данные.

Буду рад любой критике.

Извините за отсутствие русских букв в параметрах исключений.

SQLiteWrapper.h
//---------------------------------------------------------------------------

#ifndef SQLiteWrapperH
#define SQLiteWrapperH

#include "boost\noncopyable.hpp"

#include <string>

#include <sqlite\sqlite3.h>

#include "Response.h"
#include "sqlite3_error.h"

//---------------------------------------------------------------------------
class SQLiteWrapper :
    private boost::noncopyable
{
    sqlite3* db_;

    int result_;
    char *zErrMsg_;

    static int callback(void *pArg, int argc,
        char **argv, char **azColName);

    SQLiteWrapper();
public:

    SQLiteWrapper(std::string dataBaseFileName);
    ~SQLiteWrapper() throw();

    void Exec(Response* response, std::string sql);
};
#endif



SQLiteWrapper.cpp
//---------------------------------------------------------------------------

#pragma hdrstop

#include "SQLiteWrapper.h"
//---------------------------------------------------------------------------

using std::string;

#pragma package(smart_init)
int SQLiteWrapper::callback(void *pArg, int argc, char **argv, char **azColName)
{

    Response *response = (Response*)(pArg);

    return response->FillRow(pArg, argc, argv, azColName);
}

SQLiteWrapper::SQLiteWrapper(std::string dataBaseFileName)
{
    result_ = sqlite3_open(dataBaseFileName.c_str(), &db_);
    if(result_ != SQLITE_OK)
        throw sqlite3_error("╬°шсър юЄъЁ√Єш  срч√ фрээ√ї.", result_);
}

SQLiteWrapper::~SQLiteWrapper() throw()
{
    sqlite3_close(db_);
}

void SQLiteWrapper::Exec(Response* response, std::string sql)
{
    result_ = sqlite3_exec(db_, sql.c_str(), callback, *(&response), &zErrMsg_);

    if(result_ != SQLITE_OK)
        throw sqlite3_error("╬°шсър т√яюыэхэш  чряЁюёр", result_, zErrMsg_);

    response->First();
}


Response.h
//---------------------------------------------------------------------------

#ifndef ResponseH
#define ResponseH

#include <list>
#include <map>
#include <stdexcept>

#include "Field.h"


class SQLiteWrapper;

//---------------------------------------------------------------------------
class Response
{
private:
    friend SQLiteWrapper; // for access to rows_

    typedef std::map<std::string, Field> Row;
    typedef std::list<Row> ListOfRows;
    typedef ListOfRows::iterator ListOfRowsIter;

    ListOfRows rows_;

    ListOfRowsIter currentRow_;

    bool eof_;

    bool empty_;

    virtual int FillRow(void *pArg, int argc,
        char **argv, char **azColName);

    virtual void Update()
    {

    }

    void Next();
    void Prev();

public:

    typedef void (*OnRowCompleteFunc)(Row);

    Response() :
        OnRowComplete(NULL)
    {

    }

    OnRowCompleteFunc OnRowComplete;

    const Field& operator[](const std::string& fieldName) const;

    const Field& GetField(const std::string& fieldName) const;

    Response& operator++()
    {
        Next();
        return *this;
    }
    Response& operator--()
    {
        Prev();
        return *this;
    }

    operator bool()
    {
        return !eof_;
    }

    void First();
    void Last();

    bool Eof() const
    {
        return eof_;
    }
};
#endif


Response.cpp
//---------------------------------------------------------------------------

#pragma hdrstop

#include "Response.h"

using std::string;
using std::logic_error;

//---------------------------------------------------------------------------
#pragma package(smart_init)
int Response::FillRow(void *pArg, int argc,
    char **argv, char **azColName)
{
    Row row;

    for (int i = 0; i < argc; i++)
        row[string(azColName[i])] = Field(argv[i]);

    Response *response = (Response*)(pArg);

    response->rows_.push_back(row);

    if (OnRowComplete != NULL)
        OnRowComplete(row);

    return 0;
}

const Field& Response::operator[](const std::string& fieldName) const
{
    return GetField(fieldName);
}

const Field& Response::GetField(const std::string& fieldName) const
{
    if (eof_)
        throw logic_error("Âûïîëíåí operator[] äëÿ êîíöà íàáîðà äàííûõ");
    if ((*currentRow_).count(fieldName) != 1)
        throw logic_error("Ñòîëáåö îòñóòñòâóåò â áàçå äàííûõ");
    return (*currentRow_)[fieldName];
}


void Response::First()
{
    if (!rows_.empty())
    {
        currentRow_ = rows_.begin();

        eof_ = false;
    }
    else
        eof_ = true;

    Update();
}

void Response::Last()
{
    if (!rows_.empty())
    {
        currentRow_ = rows_.end();
        --currentRow_;

        eof_ = false;
    }
    else
        eof_ = true;

    Update();
}

void Response::Next()
{
    if (eof_)
        throw logic_error("Âûïîëíåí Response::Next äëÿ êîíöà íàáîðà äàííûõ");
    ++currentRow_;
    if (currentRow_ == rows_.end()) eof_ = true;
    
    Update();
}

void Response::Prev()
{
    if (currentRow_ == rows_.begin())
        throw logic_error("Âûïîëíåí Response::Prev äëÿ íà÷àëà íàáîðà äàííûõ");
    --currentRow_;
    eof_ = false;

    Update();
}


Field.h
//---------------------------------------------------------------------------

#ifndef FieldH
#define FieldH

#include <string>

//---------------------------------------------------------------------------
class Field
{
    std::string data_;
public:
    Field()
    {
    }
    Field(std::string data) :
        data_(data)
    {
    }
    Field(const char* data) :
        data_(std::string(data))
    {
    }

    std::string AsString() const
    {
        return data_;
    }
    int AsInteger() const; // To Do
    double AsDouble() const;  // To Do
};

#endif


sqlite3_error.h
//---------------------------------------------------------------------------

#ifndef sqlite3_errorH
#define sqlite3_errorH

#include <stdexcept>
#include <string>

#include <sqlite\sqlite3.h>

//---------------------------------------------------------------------------
class sqlite3_error :
    private std::exception
{
    std::string text_;
    int result_;
    std::string msg_;
public:
    sqlite3_error(const char* text, int result, const char* msg = "") :
        text_(text),
        result_(result),
        msg_(msg)
    {
    }
    const char* what () const throw();
    std::string message() const throw();
};    
#endif


sqlite3_error.cpp
//---------------------------------------------------------------------------

#pragma hdrstop

#include "sqlite3_error.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
const char* sqlite3_error::what () const throw()
{
    switch(result_)
    {
        case SQLITE_OK        : return "Successful result";
        case SQLITE_ERROR     : return "SQL error or missing database";
        case SQLITE_INTERNAL  : return "An internal logic error in SQLite";
        case SQLITE_PERM      : return "Access permission denied";
        case SQLITE_ABORT     : return "Callback routine requested an abort";
        case SQLITE_BUSY      : return "The database file is locked";
        case SQLITE_LOCKED    : return "A table in the database is locked";
        case SQLITE_NOMEM     : return "A malloc() failed";
        case SQLITE_READONLY  : return "Attempt to write a readonly database";
        case SQLITE_INTERRUPT : return "Operation terminated by sqlite_interrupt()";
        case SQLITE_IOERR     : return "Some kind of disk I/O error occurred";
        case SQLITE_CORRUPT   : return "The database disk image is malformed";
        case SQLITE_NOTFOUND  : return "(Internal Only) Table or record not found";
        case SQLITE_FULL      : return "Insertion failed because database is full";
        case SQLITE_CANTOPEN  : return "Unable to open the database file";
        case SQLITE_PROTOCOL  : return "Database lock protocol error";
        case SQLITE_EMPTY     : return "(Internal Only) Database table is empty";
        case SQLITE_SCHEMA    : return "The database schema changed";
        case SQLITE_TOOBIG    : return "Too much data for one row of a table";
        case SQLITE_CONSTRAINT: return "Abort due to contraint violation";
        case SQLITE_MISMATCH  : return "Data type mismatch";
        case SQLITE_MISUSE    : return "Library used incorrectly";
        case SQLITE_NOLFS     : return "Uses OS features not supported on host";
        case SQLITE_AUTH      : return "Authorization denied";
        case SQLITE_ROW       : return "sqlite_step() has another row ready";
        case SQLITE_DONE      : return "sqlite_step() has finished executing";
        default               : return "Unknown SQLite error";
    }
}

std::string sqlite3_error::message() const throw()
{
    return msg_;
}


Использовать предполагается так:

Класс для инкапсуляции запросов программы к базе данных. Класс содержит поля, заполняемые функцией Update. Функции наподобии MyDataBase::GetNameAndPhone служат для задания запросов программы.

MyDataBase.h
//---------------------------------------------------------------------------

#ifndef MyDataBaseH
#define MyDataBaseH

#include <string>

#include "SQLiteWrapper.h"

//---------------------------------------------------------------------------

class MyDataBase : public Response
{

    SQLiteWrapper dataBase_;

    void Update()
    {
        if (!Eof())
        {
            Name = GetField("Name").AsString();
            Phone = GetField("Phone").AsString();
        }
        else
        {
            Name = "";
            Phone = "";
        }
    }

public:

    MyDataBase(std::string dataBaseFileName) :
        dataBase_(dataBaseFileName)
    {

    }

    void GetNameAndPhone();

    std::string Name;
    std::string Phone;
};
#endif


MyDataBase.cpp
//---------------------------------------------------------------------------

#pragma hdrstop

#include "MyDataBase.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
void MyDataBase::GetNameAndPhone()
{
    dataBase_.Exec(dynamic_cast<Response*>(this), "SELECT Name, Phone From Book");
    First();
}



Основная программа:


//---------------------------------------------------------------------------

#pragma hdrstop

#include <iostream>

#include "MyDataBase.h"

//---------------------------------------------------------------------------

using namespace std;

#pragma argsused
int main(int argc, char* argv[])
{
    MyDataBase db("c:\\Test2.st");

    db.GetNameAndPhone();

    while(db)
    {
        cout << db.Name << " " << db.Phone << endl;
        ++db;
    }

    cin.get();

    return 0;
}
//---------------------------------------------------------------------------



Можно обойтись без класса MyDataBase, напрямую:

SQLiteWrapper db("c:\\Test2.st");

Response response;

db.Exec(&responce, "SELECT Name From Book");

while(responce)
{
   cout << responce["Name"].AsString() << endl;
   ++responce;
}


Каково ваше мнение. Какие здесь ошибки проектирования или некорректные моменты? Спасибо.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.