Моя версия классов, обертывающих 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;
}
Каково ваше мнение. Какие здесь ошибки проектирования или некорректные моменты? Спасибо.