Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / IBM DB2, WebSphere, IMS, U2, etc Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 Embedded SQL & MFC HOWTO  [new]
gardenman
Member

Откуда: С-Петербург
Сообщений: 2347
РАЗРАБОТКА МНОГОПОТОЧНЫХ КЛИЕНТСКИХ ПРИЛОЖЕНИЙ
ДЛЯ IBM DB2 V8.1 C ИСПОЛЬЗОВАНИЕМ MS Visual C++ V6.0

Очень часто программисты которые впервые сталкиваются с DB2
стараются пременить опыт, который они имели при работе с другими
базами (Oracle,Sybase,MS SQL). Я же полагаю что переносить такой опыт
нежелательно, ибо что для одного сервера баз данных работает хорошо,
то для другого может быть совсем не так.

Здесь, в этой статье я покажу, как легко и просто можно создавать
высокоэффективные программы на Visual С++ для DB2.

Скажу сразу, что приложение реализовано с помощью Embedded SQL.
Почему не ODBC,CLI? По многим причинам:

1) Производительность. Что делают программисты в других базах данных
для повышения производительности? Они делают хранимые процедуры.
Почему приработе хранимых процедур скорость выше? Потому, что они
уже откомпилированы и план запросы уже готов. Осталось только
передать нужные параметры.
А что такое Embedded SQL? В сущности это то же самое. Разработка
программы состоит из написания исходиника *.sqx, обработки его
препроцессором (компилятором SQL) и получения 2-х файлов -
*.bnd и *.cxx. Файлы с расширением *.bnd содержат SQL в
откомпилированном, понятном DB2 виде. А файл *.cxx - содержит
всего лишь вызов этого SQL. Причем файл *.bnd должен быть
связан с базой данных припомощи команды bind. В процессе связывания
строится план запроса.
Как нужно работать с базой данных, чтобы не было никаких задержек?
Понятно, что запросы должны быть как можно короче и выполняться
как можно быстрее. Как вы думаете, сколько хранимых процедур
нужно реализовать, чтобы получить нормальный контекстный поиск?
Для создания нужной интерактивности и производительности
потребуется не один десяток процедур. Причем это не процедуры,
которые реализуют какую-то сложную бизнес-логику. Они реализуют
всего-лишь пользовательский интерфейс. Отсюда вытекает следующий
тезис:
2) Простота и скорость разработки. Отсутствие ошибок.
Гораздо проще иметь все под рукой. В одном файле и SQL,
который будет выполняться, и собственно код приложения.
Более того, в процессе компиляции будут проверены типы данных,
корректность SQL, и если вы написали что-то не так - программа
просто не откомпилится. Более того - нет необходимости
согласовывать код SQL на сервере и код приложения.
Кроме того у embedded sql имеется поддержка версионности.
Поэтому всегда будут работать как старые, так и новые приложения.

3) Удобство. В чем оно заключается? Во-первых больше возможностей
по отладке приложений. После каждого оператора SQL вы можете
вывести сообщение, и решить как поступать - выполнить COMMIT
или ROLLBACK или продолжить дальше выполнение программы.
Попробуйте сделать такое в хранимой процедуре? Кроме того
вам достуно выполнение любых операторов SQL.

4) Переносимость. Это самый больной вопрос. Все программисты
кричат о переносимости. И, как правило говорят что все что можно
запихнуть на сервер базы данных - надо запихнуть. В результате
на сервере в конце концов появляются хранимые процедуры длинной
в несколько листов и которые выполняются не 1-2 секунды,
а по нескольку минут, или даже нескольку дисятков минут.
Причем в этот момент невозможно точно отследить что делает
такая процедура. Может она зависла?
Я полагаю что все обращения к базе данных должны отрабатываться
мгновенно, за исключением разве что отчетов в которых количество
строк перекачиваемых на рабочуюю станцию достаточно велико
(несколько тысяч). В этом случае простительна некоторая задержка.
Перенос основной массы кода не сервер базы данных врядли
значительно сократит трудозатраты переноса с платформы на платформу,
но вот увеличит неоправданную нагрузку на сервер базы данных - это
точно. В любом случае перенос с С++ на Java или с Windows на Linux
потребует некоторых затрат.

4) Безопасность. В случае использования embedded sql радикально
улучшается положение с защищенностью.
a) Защищенность от ошибки программиста
б) У позьзователей работающих с приложениями реализованными на
embedded sql отсутствует реальный доступ к объектам в базе данных.
У них имеется только право выполнять ваше приложение.
в) Разработка приложений и их инсталяция может быть разделена
между несколькими людьми. Одни имеют право компиляции, другие -
связывания, третьи - выполнения.
г) В приложении содержится только код вызываемого SQL, таким образом
полностью отсутствует возможность увидеть реальную структуру данных
или узнать ее на основе информации идущей по сети.

5) Полная поддержка C++ и как следствие ООП.
Поддержка всех фичей C++ (указатели, ссылки, шаблоны, классы)
делает С++ самым выгодным инструментом для разработки приложений.

А теперь о некоторых неудобствах:

Насколько я понял, embedded sql пока что не позволяет обрабатывать
recordsets которые возвращаются из сохраненных процедур. Но без
этого можно запросто обойтись. Например записать recordset во
временную таблицу.

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

////////////////////
//test.sqx

#define LEN 12
EXEC SQL BEGIN DECLARE SECTION;
char name[LEN];
EXEC SQL END DECLARE SECTION;

препроцессор DB2 не понимает выражения #define. Поэтому первым этапом
нужно выполнить оброботку препроцессором C++, затем препроцессором DB2,
а затем только откомпилировать результат. Все осложняется тем, что
компилятор от MS Visual С++ v.6.0 не понимает расширения .sqx.
в результате команда:

prep test.sqx bindfile isolation ur preprocessor "cl /P"

не может быть выполненна корректно,в той части, где при помощи
cl /P должет был бы быть получен файл test.i , в котором все макросы
#define и дерективы #include уже выполнены. Поэтому мой вам совет:
по возможности не испотзуйте макросы в секциях EXEC SQL.
Можно конечно пойти таким путем:

cl /Р test.cpp #препроцессор C++
copy test.i test.sqx #
db2 prep test.sqx bindfile isolation ur #препроцессор DB2
cl test.cxx db2api.lib #собственно компиляция

но в даже в этом случае дерективы #line создадут такую кашу в
исходнике, что потом в случае ошибки будет очень трудно что-либо
понять.

следующим неудобством является то, что заставить IDE от MS Visual Studio
относиться к файлам с расширением .sqx как к файлам .cpp довольно таки
трудно. Более того, каждый файл .sqx должен быть обработан препроцессором,
т.е. должно быть выполнено custom build. К счатьстью эту проблему можно
обойти. Я, например не испрользую в своих проектах .sqx файлы, а все
файлы исходников компилю следующим нехитрым .bat файлом:


rem =============================================
rem Файл для компиляции исходников с embedded sql
rem prep.bat
rem =============================================
@if exist %1.sqx @del %1.sqx > nul
@if exist %1.obj @del %1.obj > nul
@if exist %1.log @del %1.log > nul
@if exist %1.cxx @del %1.cxx > nul
@copy %1.cpp %1.sqx > nul
@echo connect to SAMPLE user 'DB2ADMIN' using 'PASSSWWWDD' > %1.db2
@echo prep %1.sqx bindfile using %1.bnd isolation ur >> %1.db2
@echo bind %1.bnd blocking all queryopt 0 isolation ur grant public >> %1.db2
@d:\sqllib\bin\db2clpex db2 -z %1.log -vf %1.db2
@if not exist %1.bnd ( type %1.log; exit 1 )
@type %1.log
sqx2cpp %1 > nul
cl /nologo /MDd /W3 /Gm /GR /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c %1.cxx


Рассмотрим подробнее что тут написано. первые четыре строки после
комментрариев удаляют мусор который мог бы остаться после предыдущих
компиляций. Затем создается файл с расширением sqx, и файл с расширением
.db2, который содержит скрипт обработки исходника препроцессором db2
и связывания sql с базой данных SAMPLE. Причем пакет будет создан от лица
пользователя DB2ADMIN использующего пароль PASSSWWWDD.
Комнда @d:\sqllib\bin\db2clpex db2 -z %1.log -vf %1.db2 запускает
скрипт на выполнение, а результат выполнения скрипта будет содержаться
в файле .log. Далее , если в процессе выполнения скрипта был получен
файл с расширением .bnd, то процесс связывания прошел успешо,
в противном случае возвращаем ошибку, предварительно распечатов .log
Еще одна маленькая хитрость заключена в командочке sqx2cpp.
Дело в том, что после компиляции .sqx в дерективах #line файла .cxx
будет содержаться ссылка на файл .sqx. Но первоисточником у нас-то
является .cpp. Поэтому файл sqx2cpp просто напросто патчит исходник
заменяя .sqx на .cpp. В результате, если в процессе компиляции случится
ошибка, то двойным кликом по ошибке в окне вывода вы будете перенесены
именно в то место исходного файла которое нужно.
Это текст sqx2cpp, вам нужно его просто откомприлить:


/////////////////////////////////////
// sqx2cpp.cpp
// to compile: cl sqx2cpp.cpp
/////////////////////////////////////

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <iostream>

using namespace std;

class UIException {
public:
UIException(char *msg) {
cout << msg << endl;
}
};

class UIMemFile {
long len;
HANDLE mh;
char* pData;
public:
UIMemFile(char* fname) {
cout << "File:" << fname << endl;
HANDLE hf;
hf=CreateFile(
fname,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL
);
if (INVALID_HANDLE_VALUE==hf)
throw UIException("INVALID_HANDLE_VALUE");

len=GetFileSize(hf,NULL);

cout << "File size:" << len <<endl;

mh=CreateFileMapping(hf,NULL,PAGE_READWRITE,0,0,NULL);

if (NULL==mh)
throw UIException("CreateFileMapping");

CloseHandle(hf);
pData=(char*)MapViewOfFile(mh,FILE_MAP_ALL_ACCESS,0,0,0);
if (NULL==pData)
throw UIException("MapViewOfFile");
}

~UIMemFile() {
CloseHandle(mh);
cout << "Closed." << endl;
}
long getlen() { return len; }
char& operator[] (long offset) {
if (offset>=len) throw UIException("Out of range");
return pData[offset];
}
char* operator () (long offset) {
if (offset>=len) throw UIException("Out of range");
return &pData[offset];
}
};

bool strcmpz(char* a,char *p) {
for (long i=0;p[i]!=0;i++) {
if (p[i]!=a[i]) return false;
}
return true;
}

char *patch;
char *fname;
char *pattern;

void main(int argc,char *argv[]) {
if(argc!=2) {
cout << "Usage: sqx2cpp {filename}" << endl;
exit(0);
}

patch=new char[strlen(argv[1])+5];
fname=new char[strlen(argv[1])+5];
pattern=new char[strlen(argv[1])+5];

sprintf(patch,"%s.cpp",argv[1]);
sprintf(pattern,"%s.sqx",argv[1]);
sprintf(fname,"%s.cxx",argv[1]);

UIMemFile eecxxdll(fname);
long i;
long patternlen=strlen(pattern);

for(i=0; i+patternlen<eecxxdll.getlen(); i++) {
if (strcmpz(eecxxdll(i),pattern)) {
cout << "Patched at offset:" << i << endl;
for(long j=0;patch[j]!=0;j++) {
eecxxdll[i+j]=patch[j];
}
}
}

delete patch;
delete pattern;
delete fname;
}


Ну вот, а теперь давайте разработкой простейшей многопоточной
программки, которая только и будет делать, что вставлять записи в
таблицу test сразу в несколько потоков. Причем по ходу дела
потоки можно будет создавать, приостанавливать и запускать снова.

Надеюсь что у вас установлена DB2 версии 8.1.

Создайте при помощи AppWizard новое приложение, я назвал его XX5.
При создании укажите что представление будет основано на классе
CFormView, и будет линковаться с использованием динамических
библиотек (Shared DLL). После создания приложения зайдите
Project->Project Settings->Category->Precompiled Headers и откажитесь от
использования предварительной компиляции заголовков. Оно и понятно,
заголовки должны быть сначала обработаны препроцессором db2.
Убедитесь что у вас правильно прописаны пути к каталогам
с файлами INCLUDE, LIB. На всякий случай проверте что в
Tools->Options->Directories->Include Files имеются
каталоги D:\SQLLIB\INCLUDE и D:\SQLLIB\TEMPLATES\INCLUDE
(у меня DB2 проинсталирована на D:\SQLLIB, у вас может быть по-другому)
а также Tools->Options->Directories-Library Files D:\SQLLIB\LIB

на закладке Project->Project Settings->Link в поле Object/Library
modules впишите имя библиотеки: db2api.lib
Теперь нужно сделать чтобы все файлы входящие в наш проект
обрабатывались препроцессором DB2. Для этого нужно зайти в установки
проекта (меню Project->Settings (Alt-F7)) для каждого файла *.cpp
на закладке General устнановить галочку в чекбоксе Always use custom
build step, после чего появится закладка Custom Build, и там в поле
Commands нужно ввести команду: prep <имя файла без расширения>
а в поле Outputs необходимо ввести путь и имя объектного файла.
В нашем случае это каталог Debug\<имяфайла>.obj. Теперь можно попытаться
откомпилить файл.


Займемся конкретным делом, а именно добавим для нашего приложения
возможность регистрации в базе данных. Создадим ресурс диалога
IDD_LOGIN, у которого будут три окна Edit для ввода имени базы данных,
имени пользователя и пароля (IDC_DATABASE,IDC_LOGIN,IDC_PASSWORD).
В класс приложения CXX5App добавим переменные для регистрации,
и собственно процедуру регистрации - connect()


class CXX5App : public CWinApp
{
public:
bool connect();
char dbpassword[12];
char dbuser[12];
char dbname[12];
CXX5App();
.
.
.


bool CXX5App::connect()
{
EXEC SQL BEGIN DECLARE SECTION;
char (*pDb)[12];
char (*pUser)[12];
char (*pPwd)[12];
EXEC SQL END DECLARE SECTION;
pDb=(char(*)[12])this->dbname;
pUser=(char(*)[12])this->dbuser;
pPwd=(char(*)[12])this->dbpassword;
EXEC SQL CONNECT TO :pDb USER :pUser USING :pPwd;
if (SQLCODE) return false;
return true;
}
На обработчик кнопки [ОК] навесим такую функцию:

void CLogin::OnOK()
{
// TODO: Add extra validation here
CXX5App* pApp=(CXX5App*) ::AfxGetApp();

GetDlgItem(IDC_DATABASE)->GetWindowText(pApp->dbname,sizeof(pApp->dbname));
GetDlgItem(IDC_LOGIN)->GetWindowText(pApp->dbuser,sizeof(pApp->dbuser));
GetDlgItem(IDC_PASSWORD)->GetWindowText(pApp->dbpassword,sizeof(pApp->dbpassword));
if (pApp->connect()) {
CDialog::OnOK();
}
}
Код приведенный выше можно упростить, например таким образом:

class CXX5App : public CWinApp
{
public:
bool connect();
EXEC SQL BEGIN DECLARE SECTION;
char dbpassword[12];
char dbuser[12];
char dbname[12];
EXEC SQL END DECLARE SECTION;
CXX5App();
.
.
.

И сообтестственно файл реализации


EXEC SQL INCLUDE SQLCA;
bool CXX5App::connect()
{
EXEC SQL CONNECT TO :dbname USER :dbuser USING :dbpassword;
if (SQLCODE) return false;
return true;
}

Но в связи с тем, что препроцессором должны быть обработаны и *.h файлы
нужно правильно включить их в файл реализации. Я делаю это таким образом:


EXEC SQL INCLUDE 'stdafx.h';
#include "stdafx.h"

EXEC SQL INCLUDE 'TT.h';
#include "TT.h"

EXEC SQL INCLUDE 'XX5.h';
#include "XX5.h"

EXEC SQL INCLUDE 'XX5Doc.h';
#include "XX5Doc.h"

EXEC SQL INCLUDE 'XX5View.h';
#include "XX5View.h"

Почему я так делаю? Строки вида EXEC SQL INCLUDE 'stdafx.h'; - собственно
выражение, которое будет обработано препроцессором DB2. А вторая
строчка #include "stdafx.h" нужна исключительно для IDE, чтобы
визуальная среда "видела" содержание и структуру заголовочного файла,
т.к. выражения вида EXEC SQL INCLUDE не распознаются IDE. А так как
внутренности заголовочных файлов защищены директивами #ifdef #define #endif
компиляция всегда бедет проходить без ошибок.
Аналогичным образом, пусть мы получили структуру таблицы из нашей базы
данных при помощи команды db2dclgn.exe (кстати - очень удобная штучка,
рекомендую к использованию) резельтатом выполнения которой является
заголовочный файл, содержащий структуру требуемой таблицы в формате C++


//file:account.h
struct
{
sqlint32 stuff;
sqlint32 account;
char closed[11];
char type[2];
double overdraft;
double active;
double passive;
double debet;
double credit;
sqlint32 dcnt;
sqlint32 ccnt;
} account;

В другом заголовочном файле мы можем сделать следующее:

class CMyClass {
public:
EXEC SQL BEGIN DECLARE SECTION;
EXEC SQL INCLUDE 'account.h'; //Для препроцессора DB2
#include "account.h" //Для среды IDE
EXEC SQL END DECLARE SECTION;
.
.
.

Да, и еще одна деталь. Как правило во всех файлах использующих
embedded sql необходимо использование структуры sqlca, поэтому
я можифицирую файл stdafx.h добавив в него всего две строчки:


#include <Afxmt.h> //Реализация многопоточности
#include <sqlenv.h> //декларация sqlca и пр.


Что будет делать наше многопоточное приложение?
Пусть добавляет в таблицу test записи. структура таблицы test
будет очень простой, причем для генирации значений исполизуем
SEQUENCE. Вот скрипт, который создаст необходимые объекты
в базе данных:


---------------------------------------------------------------

--ttt.sql

--Cкрип выполняется командой:

--d:\SQLLIB\BIN\db2clpex.exe db2 -z ttt.log -v -td@ -f ttt.sql

---------------------------------------------------------------

CONNECT TO SAMPLE USER db2admin USING 'ibmdb2'@
CREATE TABLE TEST (
ID INTEGER NOT NULL,
NAME CHAR(35),
CONSTRAINT AtID_PK PRIMARY KEY (ID)
)@
CREATE SEQUENCE S_TEST
AS INTEGER
START WITH 0
INCREMENT BY 1
NOMINVALUE
NOMAXVALUE
NO CYCLE
CACHE 1000
@

Отредактикуем представление IDD_XX5_FORM, добавим поле Edit (IDC_CNT),
и четыре кнопки - Start Thread,Suspend,Resume,Terminate (IDC_START,
IDC_SUSPEND_THREAD,IDC_RESUME,IDC_TERMINATE) Причем кнопки
Suspend,Resume,Terminate по умалчанию сделаем disabled.
Каждое запущенное окно будет соответствовать одному потоку.
В процессе выполнения поток будет обновлять текст элемета
управления IDC_CNT, показывающего количство записей, добавленнх
текущим потоком. Собственно поток реализуется на базе класса
CWinThread:
class CTT: public CWinThread {...}, и полный его текст приведен
в конце статьи

А пока назначим действия на кнопки:


void CXX5View::OnStart()
{
// TODO: Add your control notification handler code here
pTT=new CTT(this);
pTT->CreateThread();
GetDlgItem(IDC_START)->EnableWindow(FALSE);
GetDlgItem(IDC_SUSPEND)->EnableWindow(TRUE);
GetDlgItem(IDC_RESUME)->EnableWindow(FALSE);
GetDlgItem(IDC_TERMINATE)->EnableWindow(TRUE);
}
...
void CXX5View::OnSuspend()
{
// TODO: Add your control notification handler code here
if (pTT!=NULL) {
(pTT->pSection)->Lock();
pTT->SuspendThread();
GetDlgItem(IDC_SUSPEND)->EnableWindow(FALSE);
GetDlgItem(IDC_RESUME)->EnableWindow(TRUE);
GetDlgItem(IDC_TERMINATE)->EnableWindow(TRUE);
(pTT->pSection)->Unlock();
}
}
...
void CXX5View::OnResume()
{
// TODO: Add your control notification handler code here
pTT->ResumeThread();
GetDlgItem(IDC_SUSPEND)->EnableWindow(TRUE);
GetDlgItem(IDC_RESUME)->EnableWindow(FALSE);
GetDlgItem(IDC_TERMINATE)->EnableWindow(TRUE);
}

BOOL CXX5View::DestroyWindow()
{
MessageBox("Terminate thread on Destroy");
// TODO: Add your specialized code here and/or call the base class
if (pTT!=NULL) {
while (!(pTT->pSection)->Lock()) {}
pTT->SuspendThread();
TerminateThread(pTT->m_hThread,0);
(pTT->pSection)->Unlock();
delete pTT;
pTT=NULL;
}
return CFormView::DestroyWindow();
}
...
void CXX5View::OnTerminate()
{
// TODO: Add your control notification handler code here
if (pTT==NULL) return;
while (!(pTT->pSection)->Lock()){};
::TerminateThread(pTT->m_hThread,0);
GetDlgItem(IDC_START)->EnableWindow(TRUE);
GetDlgItem(IDC_SUSPEND)->EnableWindow(FALSE);
GetDlgItem(IDC_RESUME)->EnableWindow(FALSE);
GetDlgItem(IDC_TERMINATE)->EnableWindow(FALSE);
(pTT->pSection)->Unlock();
delete pTT;
pTT=NULL;
}
...



Класс, реализующий поток:


#if !defined(AFX_TT_H__BC791288_2AD1_48C5_AFE3_39CDB95AB411__INCLUDED_)
#define AFX_TT_H__BC791288_2AD1_48C5_AFE3_39CDB95AB411__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// TT.h : header file
//



/////////////////////////////////////////////////////////////////////////////
// CTT thread

class CTT : public CWinThread
{
DECLARE_DYNCREATE(CTT)
protected:
CTT(); // protected constructor used by dynamic creation
// Attributes
public:
CTT(CFormView *pV){
pView=pV;
pSection=new CCriticalSection;
}
CFormView *pView;
CCriticalSection *pSection;
void **ppCtx;
struct sqlca sqlca;
// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTT)
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
virtual int Run();
//}}AFX_VIRTUAL

// Implementation
protected:
public:
virtual ~CTT();

// Generated message map functions
//{{AFX_MSG(CTT)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TT_H__BC791288_2AD1_48C5_AFE3_39CDB95AB411__INCLUDED_)


// TT.cpp : implementation file
//

#include "stdafx.h"
#include "TT.h"
#include "XX5.h"

#include "XX5Doc.h"
#include "XX5View.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CTT

IMPLEMENT_DYNCREATE(CTT, CWinThread)

CTT::CTT()
{
ppCtx=(void**)new char[2048];
}

CTT::~CTT()
{
delete pSection;
}

BOOL CTT::InitInstance()
{
// TODO: perform and per-thread initialization here
return TRUE;
}

int CTT::ExitInstance()
{
// TODO: perform any per-thread cleanup here
return CWinThread::ExitInstance();
}

BEGIN_MESSAGE_MAP(CTT, CWinThread)
//{{AFX_MSG_MAP(CTT)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTT message handlers

int CTT::Run()
{
// TODO: Add your specialized code here and/or call the base class
//-----------------------------------------------------------

//Установим минимальный приоритет потока, в противном случае
//от сожрет все ресурсы системы так, что мы даже на экране
//ничего не увидим
//-----------------------------------------------------------

this->SetThreadPriority(THREAD_PRIORITY_IDLE);
//-----------------------------------------------------------

//Здесь будет выделена память под контекст
//потока команд DB2. Почему-то что и сколько выделять
//не описано в документации. Я выделил наобум. Прокатило
//Запускал одновременно до 50 потоков. Все работает.
//-----------------------------------------------------------

ppCtx=(void **) new char[1024]; //Буфер под контекст
char buf[15]; //Бувер для форматирования
long i=0; //Кол-во добавленных записей

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

//Получаем контекст, сразу же присоединив его к текущему потоку
//--------------------------------------------------------------

i=sqleBeginCtx(ppCtx,SQL_CTX_BEGIN_ALL,NULL,&sqlca);
sprintf(buf,"%d",i);
(((CXX5View*)pView)->GetDlgItem(IDC_CNT))->SetWindowText(buf);

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

//Собственно выполнение потока
//----------------------------

while (true) {
i++;
sprintf(buf,"%d",i);
//-------------------------------------------------

//Вывод информации о количестве добавленных записей
//-------------------------------------------------

(((CXX5View*)pView)->GetDlgItem(IDC_CNT))->SetWindowText(buf);

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

//Добавление записи в таблицу test
//--------------------------------

EXEC SQL INSERT INTO TEST (id,Name)
VALUES (NEXTVAL FOR S_TEST,CURRENT TIMESTAMP);

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

//В случае ошибки прерываем работу цикла
//---------------------------------------

if (SQLCODE) {
EXEC SQL ROLLBACK WORK;
break;
} else {
EXEC SQL COMMIT WORK;
}
//------------------------------------------

//Каждая нить будет генерировать 1000 значений
//Если у вас есть желание, то можете задавать кол-во
//в поле IDC_CNT
//------------------------------------------

if (i>1000) break;
}
//---------------------------------------------

//После того, как нить отработала отсоединяем
//контекст от базы данных и убиваем его
//---------------------------------------------

EXEC SQL CONNECT RESET;
i=sqleEndCtx(ppCtx,SQL_CTX_END_ALL,NULL,&sqlca);
sprintf(buf,"%d",i);
(((CXX5View*)pView)->GetDlgItem(IDC_CNT))->SetWindowText(buf);
while(!pSection->Lock()){};
(((CXX5View*)pView)->pTT)=NULL;
pSection->Unlock();
::ExitThread(0);
return CWinThread::Run();
}


Вообще я не являюсь большим специалистом по написанию многопоточных
соединений, и показанный здесь пример уж очень простой. К сожалению
в тех задачах, которые решаю я, многопоточность не нужна.
Однако главная идея, как это все использовать понятна. Более того,
реализуется эта техника очень просто. Если кто захочет получить
приведенный здесь в качестве примера проект в исходниках, обращайтесь
ко мне по адресу g a r d e n m a n @ y a n d e x . r u
5 май 04, 09:41    [662709]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
Lepsik
Member

Откуда: glubinka
Сообщений: 4256
по просьбе Gardenman присоединил файлик
8 апр 05, 17:53    [1453555]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
Lepsik
Member

Откуда: glubinka
Сообщений: 4256
еще одна попытка, после превью файл не присоединяется

К сообщению приложен файл (tech2db2.zip - 13Kb) cкачать
8 апр 05, 17:53    [1453559]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
Ggg_old
Guest
Вот буквально пару дней назад хотел поднять топик про EmbedSQL, про то , кто и в каких случаях его использует, и в каких случаях нужно применять его, а не ООП библиотеки.
Прочитав этот топик, становится абсолютно непонятно, зачем так страдать,
когда можно написать:
TDatabase...
TQuery...
TResultSet...

Тем не менее, я думаю, что отдельный топик по EmbedSQL стоит завести. Вот только в какой ветке ворума? Может в "Сравнение СУБД"?.
11 апр 05, 10:16    [1456040]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
gardenman
Member

Откуда: С-Петербург
Сообщений: 2347
Чтож, в сравнениях можно поднять. Можно глянуть у кого код проще получается. Хотя это уже поднималось.
11 апр 05, 10:55    [1456204]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
Ggg_old
Guest
2gardenman:
Договорились. Кто будет создавать? Я вопросом EmbedSQL не владею на практике. Вам, я думаю сормулировать тему получится лучше. Ну и ссылка на этот топик, что бы была.
11 апр 05, 11:16    [1456297]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
ggv
Member

Откуда:
Сообщений: 1810
gardenman
можно добавить только ссылку на db2mag, где поясняеться как и почему появились пакеты, и почему еще используються.
Ну и ежели MQ добавить для стягивания "длинных" репортов посредством, например, publish/subscribe, да еще и QP навесить, то получим архитектуру приближенную к идеалу :)

PS
Понятия "контекста" в ESQL появилось недавно, а до этого приходилось писать multithreaded на CLI, но позже я все мигрировал на ESQL.
11 апр 05, 19:20    [1458663]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
gardenman
Member

Откуда: С-Петербург
Сообщений: 2347
2 ggv
Тут прикол еще вот в чем. Понятие контекста по ходу дела менялось. Даже в 8 версии. Когда я писал этот пример - при порождении контекста создавалась новая сессия. И следовательно, транзакция в этом новом контексте контексте была независимой. (Делая list applications я видел несколько сооединений) А теперь понятие контекста (как я полагаю) существенно изменилось. Я экспериментировал с этим недавно. У меня получилось так, что транзакция на все контексты - одна. Т.е. в транзакции можно распараллелить несколько операций:
Типа
Порождаем нить 1, породжаем контекс для нити 1, выполняем SQL
Порождаем нить 2, породжаем контекс для нити 2, выполняем SQL
...
Порождаем нить N, породжаем контекс для нити N, выполняем SQL
Ждем завершения всех нитей - проверяем что все выполнилось ОК
делаем один COMMIT или ROLLBACK сразу для всех нитей.
Т.е. получили многопоточную транзакцию.

С использованием CLI у нас каждый поток - у нас несколько независимых соединений, а следовательно и несколько независимых транзакций. Хотя порождать в этом случае отдельные потоки не нужно. А просто смешивая CLI и ESQL можно просто переключаться между соединениями и таким образом "эмулировать" автономные транзакции.
11 апр 05, 20:49    [1458785]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
Lepsik
Member

Откуда: glubinka
Сообщений: 4256
Для MSSQL предупреждение :

While the ESQL/C API is still supported in Microsoft SQL Server 2000, no future versions of SQL Server will include the files needed to do programming work on applications that use this API. Connections from existing applications written using ESQL/C will still be supported in the next version of SQL Server, but this support will also be dropped in a future release. When writing new applications, avoid using ESQL/C. When modifying existing applications, you are strongly encouraged to remove dependencies on ESQL/C. Instead of ESQL/C, you can use Microsoft ActiveX® Data Objects (ADO), OLE DB, or ODBC to access data in SQL Server.

А как у DB2 c этим будет обстоять ?
11 апр 05, 21:23    [1458821]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
gardenman
Member

Откуда: С-Петербург
Сообщений: 2347
2 Lepsik
Мда... серьезные у MS намерения...
Я думаю что IBM вряд-ли когда-либо поступит так же. Больно уж серьезно это все встроено в ядро.
12 апр 05, 10:03    [1459646]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
ggv
Member

Откуда:
Сообщений: 1810
gardenman - вот этого я не видел, щас буду пробовать.
Когда я с многопоточным приложением возился, контекст был полноценным независимым коннектом.
Тогда 8.2 еще небыло.

Я сильно разочаруюсь в IBM, если убъют ESQL.....
12 апр 05, 10:57    [1459910]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
gardenman
Member

Откуда: С-Петербург
Сообщений: 2347
2 ggv Щас я тебе примерчики выложу, на которых я экспериментировал..
12 апр 05, 11:01    [1459932]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
gardenman
Member

Откуда: С-Петербург
Сообщений: 2347
Вот, смотри приложенный архив, может я в чем-то ошибся, но не думаю...

К сообщению приложен файл (src.zip - 3Kb) cкачать
12 апр 05, 11:10    [1459963]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
ggv
Member

Откуда:
Сообщений: 1810
gardenman - не обещаю что прям щас, но постараюсь в течении дня-двух разобраться.
12 апр 05, 11:29    [1460069]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
ggv
Member

Откуда:
Сообщений: 1810
о как, это в добавок ко всему еще и С++ :)
Я уж как-нибудь по старинке, на С-ях :)
12 апр 05, 11:33    [1460088]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
gardenman
Member

Откуда: С-Петербург
Сообщений: 2347
Мне интересно как будет многопоточное приложение работать с временной таблицей. Т.е. возможно ли для каждой нити создать свою GTT - оказалось - нет. Типа получилось что коннект один. Ну и - вобщем получилась параллельная (многопоточная) транзакция
12 апр 05, 11:38    [1460128]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
ggv
Member

Откуда:
Сообщений: 1810
gardenman -
ну на сегодня я многого не достиг (не очень много времени, какие-то непонятки с DB2 DWE for linux, также с WebSphere App Serv & DB2 Alphablox и всё под linux)
но вот сейчас глянул доку
раздел
http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/c0006107.htm
особенно подраздел recomendations
http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/c0006108.htm
ну и также
http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/admin/r0001917.htm

мне показалось, что в концепции контекста ничего не изменилось....
Ежели я только внимательно читал.
сегодня постараюсь скомпилить и проверить
20 апр 05, 08:35    [1481234]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
gardenman
Member

Откуда: С-Петербург
Сообщений: 2347
почитал, однако как объяснить то, что я получаю? Может я где-то ошибся в коде? Конечно код, который я привел - сделан слегка тяп-ляп, но я не думаю что я там слишком сисльно напортачил.
20 апр 05, 10:44    [1481599]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
ggv
Member

Откуда:
Сообщений: 1810
http://publib.boulder.ibm.com/infocenter/db2help/index.jsp?topic=/com.ibm.db2.udb.doc/admin/r0005841.htm

gardenman, продолжать, или это оно и есть?

У меня, правда, уже материал на статью получился, что-то типа "HOWTO DB2 Multithread Programmin with ESQL and Context."
Можно и закончить ее, у можно и забить.
22 апр 05, 14:47    [1489976]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
ggv
Member

Откуда:
Сообщений: 1810
gardenman -- oops, у тебя это используеться.
22 апр 05, 14:51    [1489992]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
gardenman
Member

Откуда: С-Петербург
Сообщений: 2347
Написать статью - это хорошо... правда не знаю будет ли она востребована...
А то вот я смотрю кроме меня нихто DB2 не любит
22 апр 05, 18:19    [1491082]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
ggv
Member

Откуда:
Сообщений: 1810
gardenman - а я??????
на выходных вот закончу с context (если ничем не загрузят дома), а затем можно в виде howto оформить.
Правда, восстребованость будет минимальная - J2EE наш рулевой на обозримую перспективу.
Так что надо почитать будет про SQLJ :)
Может, сравнение какое оформиться.
23 апр 05, 11:39    [1492137]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
ggv
Member

Откуда:
Сообщений: 1810
gardenman - ну всё работает как и документировано.
Если я правильно понимаю, чего я натворил, и суть вопроса.
25 апр 05, 12:06    [1494101]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
gardenman
Member

Откуда: С-Петербург
Сообщений: 2347
Стоп!

Все же скажи такую конкретную вещь (предлагаю 2 варианта)

1) Каждый контекст работает в своей транзакции (и, как следствие, откат в любой нити - независим от других нитей, т.е. все контексты - независимы)
2) Все контексты работают в одной транзакции, и => откат в одной нити откатывает все изменения в других нитях.

Если тебе не трудно, выложи исходники, я глянуть хочу...
25 апр 05, 12:24    [1494204]     Ответить | Цитировать Сообщить модератору
 Re: Embedded SQL & MFC HOWTO  [new]
ggv
Member

Откуда:
Сообщений: 1810
ну поскольку я так и не выяснил, на каких таких правах я могу публиковать код, и поскольку мне тут же посоветовали провести "событие" для бизнес партнеров, проще сказали будет послать код персонально на мыло. Что я и сделаю.
Щас вот только еще одну фигню проверю (самому интересно) и пошлю.
25 апр 05, 12:45    [1494301]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / IBM DB2, WebSphere, IMS, U2, etc Ответить