Categories: DatabasewxWidgets

Организуем доступ к базам данных SQLite при разработке кросс-платформенных приложений на C++/wxWidgets – Часть I – Шаблон проекта и GUI

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

Итак, эта статья о разработке приложений, использующих для своей работы базы данных SQLite.
Для работы нам понадобится:


Для начала, создадим новый проект (Win32 Project) в Visual Studio 2005 и назовем его SQLiteTest

В мастере создания проекта выберем тип проекта Windows Application и в дополнительных свойствах укажем Empty project

Затем распакуем библиотеку DatabaseLayer в папку, в которой находится файл SQLiteTest.sln

В Solution Explorer жмём правой кнопкой на названии Solution’а, выбираем пункт Add -> Existing Project

И в окне выбора файла проекта, выбираем проект databaselayer/build/databaselayer_databaselayer_sqlite.dsp (или .vcproj).

Теперь нам необходимо указать зависимости проектов. Наше приложение должно быть собрано после сборки библиотеки Databaselayer, поэтому в Solution Explorer жмем правой кнопкой на названии проекта SQLiteTest и выбираем пункт меню Project Dependencies…

В списке проектов ставим маркер на названии проекта databaselayer_sqlite и жмем OK

Далее, выбираем пункт меню Build -> Configuration Manager и указываем конфигурации сборки каждого проекта для каждой конфигурации сборки Solution’а. Я использую статическую раздельную Unicode-сборку wxWidgets, поэтому для Debug-конфигурации сборки Solution’а я выбираю конфигурацию Static Unicode Debug Multilib проекта databaselayer_sqlite и конфигурацию Debug проекта SQLiteTest. Такие же действия необходимо проделать и для Release-конфигурации сборки Solution’а, все вхождения слова Debug в названиях конфигураций на Release

После этого, распаковываем архив sqlite-source-x_y_z.zip в папку databaselayer/sqlite/include и архив sqlitedll-x_y_z.zip в папку databaselayer/sqlite/lib
В каталоге databaselayer/sqlite/lib должны появиться файлы sqlite3.dll и sqlite3.def
Всё это очень хорошо, но для использования динамической библиотеки в нашем проекте, неплохо было бы иметь .lib файл, которого у нас сейчас нет. Для того, чтобы он у нас был, создаем два batch-скрипта:

setupvars.bat

"C:/Program Files/Microsoft Visual Studio 8/VC/bin/vcvars32.bat"

export.bat

lib.exe /def:sqlite3.def /machine:x86 /out:sqlite3.lib

Запускаем командную строку, переходим в папку databaselayer/sqlite/lib и по очереди запускаем эти два batch-файла

setupvars
export

После выполнения этих нехитрых действий, у нас должны появиться два файла: sqlite3.exp и sqlite3.lib
Копируем файл sqlite3.dll в папку SQLiteTest/bin

Отлично, теперь создаем в нашем проекте SQLiteTest четыре новых файла:

  • SQLiteTestApp.h
  • SQLiteTestApp.cpp
  • SQLiteTestMainFrame.h
  • SQLiteTestMainFrame.cpp

Теперь открываем свойства проекта, переходим в раздел C/C++ и в настройках Additional Include Directories добавляем две новые записи:

  • $(ProjectDir)../databaselayer/sqlite/include
  • $(ProjectDir)../databaselayer/ include

Переходим в раздел Linker и в настройках Additional Library Directories добавляем две новые записи:

  • $(ProjectDir)../databaselayer/sqlite/lib
  • $(ProjectDir)../databaselayer/ lib


В разделе Linker -> Input в настройках Additional Dependencies добавляем записи:

  • advapi32.lib
  • comctl32.lib
  • uuid.lib
  • rpcrt4.lib
  • wxbase28ud.lib
  • wxmsw28ud_core.lib
  • wxmsw28ud_adv.lib
  • wxpngd.lib
  • wxcode_msw28ud_databaselayer_sqlite.lib
  • sqlite3.lib

Не забываем, что суффикс d в названиях библиотек wxWidgets указывает на то, что используется отладочная версия библиотеки. Release-версии библиотек не имеют в названии этого суффикса.

Далее, в разделе Linker -> General для всех конфигураций указываем параметр Output File равным ../bin/$(ProjectName).exe и в разделе Debugging параметр Working Directory равным ../bin. Это позволит генерировать исполняемый файл в отдельную папку.

Итак, предварительная настройка завершена, можно приступать к написанию кода.

SQLiteTestMainFrame.h

#ifndef _SQLITE_TEST_MAINFRAME_H
#define _SQLITE_TEST_MAINFRAME_H

#include <wx/wx.h>

class SQLiteTestMainFrame : public wxFrame
{
 void CreateControls();
public:
 SQLiteTestMainFrame();
 bool Create(wxWindow * parent, wxWindowID id, const wxString & title);

 DECLARE_EVENT_TABLE()
 void OnExit(wxCommandEvent & event);
};

#endif

SQLiteTestMainFrame.cpp

#include "SQLiteTestMainFrame.h"
#include "SQLiteTestApp.h"

BEGIN_EVENT_TABLE(SQLiteTestMainFrame, wxFrame)
EVT_MENU(wxID_EXIT, SQLiteTestMainFrame::OnExit)
END_EVENT_TABLE()

SQLiteTestMainFrame::SQLiteTestMainFrame()
{
 Create(NULL, wxID_ANY, _("SQLite Addressbook"));
}

bool SQLiteTestMainFrame::Create(wxWindow * parent, wxWindowID id, const wxString & title)
{
 bool res = wxFrame::Create(parent, id, title, wxDefaultPosition, wxSize(700, 500));
 if(res)
 {
  CreateControls();
 }
 return res;
}

void SQLiteTestMainFrame::CreateControls()
{
 wxMenuBar * menuBar = new wxMenuBar;
 SetMenuBar(menuBar);

 wxMenu * fileMenu = new wxMenu;
 fileMenu->Append(wxID_EXIT, _("ExittAlt+F4"));

 menuBar->Append(fileMenu, _("File"));

 CreateStatusBar(2);
 Centre();
}

void SQLiteTestMainFrame::OnExit(wxCommandEvent & event)
{
 wxUnusedVar(event);
 Close();
}

SQLiteTestApp.h

#ifndef _SQLITE_TEST_APP_H
#define _SQLITE_TEST_APP_H

#include <wx/wx.h>
#include <databaselayer.h>
#include <sqliteDatabaseLayer.h>

class SQLiteTestApp : public wxApp
{
 DatabaseLayer * m_Database;
public:
 virtual bool OnInit();
 virtual int OnExit();

 bool ConnectToDatabase();
 DatabaseLayer * GetDatabase();
};

DECLARE_APP(SQLiteTestApp)

#endif

SQLiteTestApp.cpp

#include <wx/image.h>
#include <databaseLayerException.h>
#include "SQLiteTestApp.h"
#include "SQLiteTestMainFrame.h"

IMPLEMENT_APP(SQLiteTestApp)

bool SQLiteTestApp::OnInit()
{
 if(!ConnectToDatabase())
 {
  wxFAIL_MSG(_("Error connecting to database!"));
  return false;
 }
 wxImage::AddHandler(new wxPNGHandler);
 wxImage::AddHandler(new wxJPEGHandler);

 SQLiteTestMainFrame * frame = new SQLiteTestMainFrame;
 SetTopWindow(frame);
 frame->Show();

 return true;
}

int SQLiteTestApp::OnExit()
{
 if(m_Database)
 {
  if(m_Database->IsOpen())
  {
   m_Database->Close();
  }
  wxDELETE(m_Database);
 }
 return wxApp::OnExit();
}

bool SQLiteTestApp::ConnectToDatabase()
{
 m_Database = new SqliteDatabaseLayer();
 wxString db_filename(wxT("addressbook.db"));
 PreparedStatement * pStatement(NULL);
 bool bCreate = !wxFileExists(db_filename);
 if(bCreate)
 {
  wxMessageBox(_("Database does not exist... recreating."));
 }
 try
 {
  m_Database->Open(db_filename);
  // Try to recreate tables
  try
  {
   m_Database->RunQuery(wxT("CREATE TABLE groups(id integer primary key,
    name varchar(128) not null,
    description varchar(512));"));
  }
  catch(DatabaseLayerException & e) {wxUnusedVar(e);}
  try
  {
   m_Database->RunQuery(wxT("CREATE TABLE persons(
    id integer primary key,
    groupid integer not null,
    first_name varchar(64) not null,
    last_name varchar(64) not null,
    gender boolean not null,
    address varchar(128) not null,
    city varchar(64) not null,
    country varchar(32) not null,
    phone varchar(32),
    email varchar(128),
    website varchar(260));"));
  }
  catch(DatabaseLayerException & e) {wxUnusedVar(e);}
  if(bCreate)
  {
   m_Database->RunQuery(
    wxT("INSERT INTO groups(name, description) VALUES ('Friends', 'My friends')"));
   pStatement = m_Database->PrepareStatement(
    wxT("INSERT INTO persons(groupid, first_name,last_name,gender,address,city,country,phone) VALUES
     (?,?,?,?,?,?,?,?)"));
   if (pStatement)
   {
    pStatement->SetParamInt(1, 1);
    pStatement->SetParamString(2, _("John"));
    pStatement->SetParamString(3, _("Doe"));
    pStatement->SetParamBool(4, 1);
    pStatement->SetParamString(5, _("SomeStreet st., 123/45"));
    pStatement->SetParamString(6, _("Some City"));
    pStatement->SetParamString(7, _("Some Country"));
    pStatement->SetParamString(8, _("+000-00-000-00-00"));
    pStatement->RunQuery();
    m_Database->CloseStatement(pStatement);
    pStatement = NULL;
   }
  }
 }
 catch(DatabaseLayerException & e)
 {
  if(pStatement)
  {
   m_Database->CloseStatement(pStatement);
   pStatement = NULL;
  }
  wxFAIL_MSG(e.GetErrorMessage());
  return false;
 }
 return true;
}

DatabaseLayer * SQLiteTestApp::GetDatabase()
{
 return m_Database;
}

Итак, что же мы сделали: мы создали класс приложения SQLiteTestApp, содержащий указатель на объект класса DatabaseLayer, который и будет обеспечивать связь с нашей базой данных. При запуске приложения проверяется наличие файла addressbook.db и, в случае если файл не найден, он создается. Вместе с базой данных создаются две таблицы: groups и persons и заполняются тестовыми данными.
Главная форма приложения содержит строку меню и строку статуса и, пока, ничего не делает.

После успешной сборки приложения и его запуска, в папке bin должен появиться файл базы данных addressbook.db

Отлично, теперь можно приступать к работе с базой данных.

Читать продолжение статьи…

T-Rex

View Comments

Share
Published by
T-Rex

Recent Posts

Разработка кроссплатформенных модульных приложений на C++ с библиотекой wxWidgets

Введение Уже долгое время не пишу статьи о разработке, хотя сам процесс написания мне очень…

11 years ago

wxWidgets App With Plugins (Windows/Linux/Mac) – Sample Source Code

I can see that there is still a lot of topics at wxWidgets forums related…

11 years ago

wxToolBox is Now Open-Source!

I've just published the source code of wxToolBox component and a couple of sample apps at…

11 years ago

Microsoft Kinect Helper Library and Sample for wxWidgets

Microsoft released their Kinect SDK several days ago. So, for those wxWidgets developers who are…

13 years ago

wxJSON 1.1.0 Released

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to…

15 years ago

wxRuby. Оно даже работает!

Вдохновленнный читаемой нынче книгой My Job Went to India: 52 Ways to Save Your Job…

15 years ago