Skip to content Skip to footer

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

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

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

Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
Для начала, создадим новый проект (Win32 Project) в Visual Studio 2005 и назовем его SQLiteTest
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
В мастере создания проекта выберем тип проекта Windows Application и в дополнительных свойствах укажем Empty project
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
Затем распакуем библиотеку DatabaseLayer в папку, в которой находится файл SQLiteTest.sln
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
В Solution Explorer жмём правой кнопкой на названии Solution’а, выбираем пункт Add -> Existing Project
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
И в окне выбора файла проекта, выбираем проект databaselayer/build/databaselayer_databaselayer_sqlite.dsp (или .vcproj).
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
Теперь нам необходимо указать зависимости проектов. Наше приложение должно быть собрано после сборки библиотеки Databaselayer, поэтому в Solution Explorer жмем правой кнопкой на названии проекта SQLiteTest и выбираем пункт меню Project Dependencies…
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
В списке проектов ставим маркер на названии проекта databaselayer_sqlite и жмем OK
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
Далее, выбираем пункт меню Build -> Configuration Manager и указываем конфигурации сборки каждого проекта для каждой конфигурации сборки Solution’а. Я использую статическую раздельную Unicode-сборку wxWidgets, поэтому для Debug-конфигурации сборки Solution’а я выбираю конфигурацию Static Unicode Debug Multilib проекта databaselayer_sqlite и конфигурацию Debug проекта SQLiteTest. Такие же действия необходимо проделать и для Release-конфигурации сборки Solution’а, все вхождения слова Debug в названиях конфигураций на Release
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
После этого, распаковываем архив 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

Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1

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

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

Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
В разделе 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-версии библиотек не имеют в названии этого суффикса.
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
Далее, в разделе Linker -> General для всех конфигураций указываем параметр Output File равным ../bin/$(ProjectName).exe и в разделе Debugging параметр Working Directory равным ../bin. Это позволит генерировать исполняемый файл в отдельную папку.
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
Итак, предварительная настройка завершена, можно приступать к написанию кода.

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 и заполняются тестовыми данными.
Главная форма приложения содержит строку меню и строку статуса и, пока, ничего не делает.
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
После успешной сборки приложения и его запуска, в папке bin должен появиться файл базы данных addressbook.db
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
Отлично, теперь можно приступать к работе с базой данных.

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

Leave a comment

0.0/5