Основы проектирования приложений баз данных

         

Библиотека MFC


Среда Visual Studio.NET предоставляет различные подходы для реализации работы с базами данных:

Применение библиотеки MFC (Microsoft Foundation Class Library).Применение ATL (Active Template Library).Использование библиотек Framework.

Библиотека MFC реализует поддержку доступа к базам данных, основанную на двух механизмах:

через ODBC-драйверы;с применением провайдеров данных OLE DB.

Приложения, использующие доступ к базам данных с применением ODBC-драйверов, в обязательном порядке используют класс CDatabase для создания объекта "база данных" и класс CRecordset для создания объекта "результирующий набор".

Библиотека MFC предоставляет для работы с базами данных через ODBC-драйверы следующие классы:

CDatabase – класс, инкапсулирующий соединение с источником данных;CRecordset – класс, инкапсулирующий работу с результирующим набором, включая переход между записями, добавление, удаление и изменение записей, задание фильтров и выполнение параметризированных запросов;CRecordView – класс, предоставляющий средства визуального отображения данных из созданного результирующего набора;CDBException – класс, предназначаемый для обработки ошибок, которые возникают при работе с источником данных;CFieldExchange – класс, который поддерживает RFX обмен данными между полями – членами класса результирующего набора и столбцами таблицы источника данных.

Библиотека MFC предоставляет для работы с базами данных с применением провайдеров OLE DB класс COleDBRecordView, используемый для отображения данных из результирующего набора (представляемого классом CRowset) в элементах управления шаблона диалога. Этот класс объявляется в заголовочном файле afxoledb.h.

Для реализации клиентов и серверов OLE DB можно использовать ATL, предоставляющую OLE DB шаблоны как С++ шаблоны. Более подробно шаблоны ATL будут рассмотрены в следующей лекции.

Библиотека MFC предоставляет следующие два класса, упрощающие обмен данными:

CLongBinary – класс, предоставляющий средства работы с BLOB-объектами (Binary Large OBject), такими как bitmap-изображения, хранимыми в базе данных;CDBVariant – класс, реализующий тип Variant, который позволяет сохранять информацию в базе данных, используя текущее значение, хранимое в переменной типа union.

Для доступа к конкретной базе данных требуется, чтобы был установлен соответствующий драйвер ODBC или OLE DB провайдер.

Приложения, работающие с базами данных, могут быть реализованы как консольные приложения, приложения с архитектурой документ-отображение (SDI- и MDI-приложения), приложения-диалоги, серверные приложения.



Класс CDATABASE


Класс CDatabase предоставляет средства подключения к источнику данных. В приложении может использоваться несколько активных объектов CDatabase.

Для использования класса CDatabase следует подключить заголовочный файл afxdb.h.

Член класса CDatabase::m_hdbc является указателем на подсоединенный ODBC источник данных. Он может использоваться как дескриптор соединения при непосредственном вызове функций ODBC.

Например:

// Использование m_hdbc для непосредственного вызова // ODBC API. // m_db – объект типа CDatabase, // m_hdbc – член класса CDatabase nRetcode = ::SQLGetInfo( m_db.m_hdbc, SQL_ODBC_SQL_CONFORMANCE, &nValue, sizeof( nValue ), &cbValue );

Класс CDatabase имеет один конструктор без параметров и набор методов, включая следующие:

OpenEx (LPCTSTR lpszConnectString, DWORD dwOptions = 0);throw (CDBException, CMemoryException ); - метод, выполняющий открытие базы данных. Первый параметр определяет ODBC строку подключения, описывающую источник данных, и некоторую дополнительную информацию, такую как идентификатор пользователя и пароль.

Например: "DSN=SQLServer_Source;UID=Us1;PWD=pwd1".

Второй параметр по умолчанию равен нулю, что предполагает открытие базы данных с разделяемым доступом и с правами записи, без загрузки ODBC Cursor Library DLL и без принудительного отображения диалога ODBC, определяющего информацию для подключения к базе данных. Этот параметр может быть задан как битовая маска, определяемая комбинацией следующих значений:

CDatabase::openReadOnly – открытие источника данных только для чтения;CDatabase::useCursorLib – указывает загрузку библиотеки ODBC Cursor Library DLL, маскирующей некоторую функциональность ODBC-драйвера;CDatabase::noOdbcDialog – предотвращает появление диалога для подсоединения источника данных;

CDatabase::forceOdbcDialog – обеспечивает отображение диалога с целью определения информации для ODBC-соединения.

Например:

// Объявление объекта типа CDatabase CDatabase m_db1; // Объект CDatabase: открытие источника данных // "только на чтение" m_db1.OpenEx( _T( "DSN=MYDATASOURCE;UID=U1" ), CDatabase::openReadOnly | CDatabase::noOdbcDialog ) );Close – метод, закрывающий соединение с базой данных.
После закрытия соединения объект типа CDatabase можно использовать для открытия другого источника данных;IsOpen – метод, определяющий, установлено ли соединение с базой данных для объекта CDatabase;GetConnect – метод, который возвращает строку подключения, использованную для открытия источника данных;GetDatabaseName – метод, возвращающий имя текущей подсоединенной базы данных (это не источник данных DSN, указываемый для методов OpenEx и Open, и зависит от ODBC);CanTransact – метод, позволяющий определить, можно ли использовать транзакции для базы данных;SetLoginTimeout – метод, устанавливающий интервал времени, по истечении которого попытка подсоединения к источнику данных будет отменена;BeginTrans – метод, открывающий транзакцию. Не все ODBC-драйверы поддерживают применение транзакций;CommitTrans – метод, выполняющий завершение транзакции;Rollback – метод, выполняющий откат транзакции;

ExecuteSQL – метод, выполняющий SQL-оператор без возвращения результирующего набора.



Например:

CString strCmd = "UPDATE TBL1 SET F1 = 124"; TRY { m_db1.ExecuteSQL( strCmd );} CATCH(CDBException, e) { // Код ошибки в e->m_nRetCode } END_CATCH


Класс CRECORDSET


Класс CRecordset реализует операции над результирующим набором, получаемым из источника данных при выполнении SQL-оператора SELECT.

Результирующий набор может использоваться в двух режимах:

динамический набор записей (dynasets), состояние которого синхронизируется с изменениями, сделанными другими пользователями;статический набор записей (snapshots), не отражающий изменения, производимые другими пользователями.

Класс CRecordset позволяет:

просматривать записи результирующего набора;изменять записи;выполнять блокировку записей;сортировать записи;определять фильтр для выбора записей.

Для использования набора записей из базы данных следует:

Создать объект класса CRecordset, передав конструктору в качестве параметра указатель на объект CDatabase или NULL;Вызвать метод Open и определить режим использования набора данных: динамический или статический.

Метод Open создает (открывает) результирующий набор.

Член класса CRecordset::m_hstmt содержит указатель на структуру данных типа HSTMT (дескриптор оператора).

Класс CRecordset содержит переменные и методы для работы с результирующим набором, включая следующие:

CRecordset::m_nFields – переменная, содержащая количество полей в результирующем наборе.CRecordset::m_nParams – переменная, содержащая количество параметров, используемых в запросе.CRecordset::m_pDatabase – переменная, содержащая указатель на объект CDatabase посредством которого для данного результирующего набора было установлено соединение с базой данных.CRecordset::m_strFilter – переменная, содержащая условие, указываемое во фразе WHERE SQL-оператора (условие должно быть определено до создания результирующего набора вызовом метода Open).

Например:

CMySet rsMySet( NULL ); // Класс CMySet наследуется от CRecordset // Определение фильтра (условия во фразе WHERE) rsMySet.m_strFilter = "field2 > 123"; // Выполнение запроса – открытие результирующего набора rsMySet.Open( CRecordset::snapshot, "MyTbl1" );

CRecordset::m_strSort – переменная, содержащая значение, указываемое во фразе ORDER BY SQL-оператора;CRecordset ( CDatabase* pDatabase = NULL); – конструктор;Open – метод, создающий результирующий набор;CanAppend – метод, определяющий, разрешено ли в данный открытый набор записей добавлять новые записи;GetSQL – метод, возвращающий SQL-оператор, использованный для получения открытого в данный момент результирующего набора;IsEOF – метод, определяющий, достигнут ли конец результирующего набора;IsBOF – метод, определяющий, достигнуто ли начало набора записей.


Например:

rsSet.Open( ); // Результирующий набор открыт: // текущая запись - первая if( rsSet.IsBOF( ) ) return; // Результирующий набор пуст while ( !rsSet.IsEOF( ) ) // Просмотр всех // записей от начала до конца rsSet.MoveNext( ); rsSet.MoveLast( ); // Переход к последней записи while( !rsSet.IsBOF( ) ) // Просмотр всех // записей от конца до начала rsSet.MovePrev( ); rsSet.MoveFirst( ); // Переход к первой записи

AddNew – метод, используемый для добавления новой записи (только для режима выборки по одной записи);CancelUpdate – метод, используемый для отмены изменений, произведенных операциями Edit или AddNew с момента последнего вызова метода Update;Delete – метод, используемый для удаления текущей записи;Edit – метод, вызываемый для включения режима редактирования текущей записи. В режиме редактирования можно выполнять методы AddNew и Delete. Для внесения изменений, сделанных этими методами, в базу данных следует вызвать метод Update.

Например:

rsSet.Edit( ); // Начало операций изменения записи rsSet.m_dwF1 = 123; rsSet.m_strF1 = "abc"; if( !rsCustSet.Update( ) ) // Внесение изменений

Update – метод, возвращающий ненулевое значение в том случае, если запись источника данных была успешно обновлена;MoveFirst – метод, перемещающий указатель текущей записи на первую запись результирующего набора;MoveLast – метод, перемещающий указатель текущей записи на последнюю запись результирующего набора;MoveNext – метод, перемещающий указатель текущей записи на следующую запись результирующего набора;MovePrev – метод, перемещающий указатель текущей записи на предыдущую запись результирующего набора;DoFieldExchange – метод, вызываемый для автоматического обмена данными между членами класса – полями результирующего набора (текущей записи) и соответствующими полями текущей записи в источнике данных. Метод DoFieldExchange доступен только для объектов класса, производного от класса CRecordset. Если набор записей создан непосредственно как объект класса CRecordset, то следует использовать метод GetFieldValue.Обмен данными с источником данных, называемый RFX-обменом (record field exchange), работает в двух направлениях: из полей объекта "результирующий набор" в поля источника данных и обратно. Для того чтобы использовать этот метод, достаточно в производном классе результирующего набора определить имена и тип полей данных – членов класса. ClassWizard самостоятельно вставит код в переопределяемый метод DoFieldExchange.

Например:

void CSet::DoFieldExchange(CFieldExchange* pFX) { pFX->SetFieldType(CFieldExchange::outputColumn); RFX_Int(pFX, "F1", m_wF1); // Вызов RFX-метода RFX_Text(pFX, "F2", m_strF2); }


Класс CRECORDVIEW


Объект "отображение" типа CRecordView предоставляет средства для просмотра полей базы данных в элементах управления.

Отображение создается на основе шаблона документа, используя элементы управления, добавленные в ресурс шаблона диалога.

Объект CRecordView использует DDX-обмен данными и RFX-обмен данными для реализации обмена между тремя наборами данных: элементами управления, полями результирующего набора и записями источника данных. На следующей схеме проиллюстрировано взаимодействие этих трех наборов данных.

Элементы управления (определяются как ресурсы)
DDX-методы
Поля результирующего набора (определяются как переменные члены класса, наследуемого от CRecordset)
RFX-методы
Поля источника данных (указываются оператором SELECT)

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

При использовании для создания шаблона приложения MFC Application Wizard автоматически будут созданы ресурс меню и панели инструментов, содержащей кнопки прокрутки набора записей. При программировании вручную, используя ClassWizard, эти ресурсы надо создавать самостоятельно в редакторе меню и в редакторе битовых изображений.

Класс CRecordView имеет два конструктора и набор методов, включая следующие:

CRecordView – конструктор, параметр которого указывает используемый ресурс шаблона диалога;OnGetRecordset – метод, возвращающий указатель на "объект результирующий набор", ассоциированный с отображением, при его успешном создании, и NULL — в противном случае;OnMove – метод, используемый для перехода к другой записи результирующего набора. Направление перехода задается параметром, который может принимать следующие значения: ID_RECORD_FIRST; ID_RECORD_LAST; ID_RECORD_NEXT; ID_RECORD_PREV. По умолчанию, OnMove обновляет текущую запись источника данных, если пользователь внес изменения в отображение записи (элементы управления);IsOnFirstRecord – метод, возвращающий ненулевое значение, если текущей записью является первая запись результирующего набора.

Для использования базы данных следует:

Создать объект класса, наследуемого от CDatabase.Установить соединение, открыв базу данных (вызвав метод CDatabase::OpenEx или CDatabase::Open).Создать объект класса, наследуемого от Crecordset, для операций над подсоединенным источником данных, передав конструктору указатель на CDatabase. Как любой класс, реализующий визуализацию данных, этот класс связывается с шаблоном диалога (шаблон диалога – это ресурс, описывающий диалоговое окно и все располагаемые в нем элементы управления). Используя DDX-механизм (Dialog Data eXchange) обмена данными между результирующим набором и элементами управления, класс CRecordView реализует отображение и изменение данных.Для завершения работы закрыть базу данных, вызвав метод CDatabase::Close. Это также закроет все наборы записей.



Создание приложений баз данных


Средства MFC Application Wizard позволяют практически без всякого программирования разрабатывать приложения, выполняющие подключение к таблице базы данных и отображение содержимого ее полей. При этом для реализации доступа к базе данных используется или механизм ODBC, или OLE DB.

Для того чтобы использовать ODBC-драйверы, первоначально следует создать источник данных DSN (Data Source Name). Это можно сделать как в момент формирования проекта с помощью MFC Application Wizard, так и используя ODBC32 панели управления Windows.

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

Для того, чтобы создать приложение, реализующее доступ к базе данных, выполните следующие действия:

Создайте новый проект. Выберите в качестве шаблона создаваемого проекта MFC Application.Автоматически формируемые приложения, реализующие работу с базой данных, должны поддерживать архитектуру "документ-отображение". (Выберите, например, тип приложения Single Document).Перейдите на страницу Database Support (рис. 10.1) и определите поддержку работы с базой данных. Опция Database view without file support обеспечивает формирование кода, выполняющего подключение к источнику данных, но без поддержки механизма сериалицации. Опция Bind all columns доступна только для ODBC и обеспечивает автоматическое связывание полей результирующего набора с переменными членами класса, наследуемого от CRecordset.


Рис. 10.1.  Страница Database Support мастера MFC Application Wizard

Выберите механизм доступа (Client type): опция ODBC обеспечивает включение в проект заголовочного файла AFXDB.H и линкуемых библиотек;опция OLE DB обеспечивает включение в проект заголовочных файлов ATLBASE.H, AFXOLEDB.H и ATLPLUS.H.


Если для Database Support была установлена опция Database view without file support или Database view with file support, то для механизма ODBC создаваемый класс отображения будет наследоваться от CRecordView и будет ассоциирован с классом результирующего набора, наследуемого от CRecordset. Для механизма OLE DB класс отображения наследуется от COleDBRecordView и ассоциируется с классом, наследуемым от CTable или CCommand.Далее, если используется механизм ODBC, то следует выбрать источник данных, с которым устанавливается соединение. По щелчку мышью на командной кнопке Data Source отображается диалог для выбора источника данных (рис. 10.2).


Рис. 10.2.  Диалог для выбора источника данных (для ODBC)

При использовании механизма OLE DB также следует определить источник данных, используя или имя источника данных, или строку подключения (рис. 10.3)


Рис. 10.3.  Диалог для выбора источника данных (для OLE DB)

После определения используемого источника данных и идентификации пользователя отображается диалог (рис. 10.4) со всеми таблицами, представлениями и хранимыми процедурами (только для OLE DB) источника данных, которые доступны данному пользователю. В этом диалоге следует выбрать таблицу базы данных, используемую для формирования результирующего набора.


Рис. 10.4.  Диалог Select Database Object

На странице Generated Classes отображается список классов, автоматически создаваемый MFC Application Wizard. Класс отображения будет наследован от класса СRecordView, а класс результирующего набора – от класса CRecordset.

Созданное AppWizard приложение будет содержать панель инструментов с кнопками для перехода между записями таблицы базы данных, но не будет содержать элементов управления для отображения полей таблицы базы данных.

Мастер MFC Application Wizard создает в классе, наследуемом от Crecordset, переменные члены класса для каждого поля таблицы подключаемой базы данных.

Для того чтобы отобразить в форме поля базы данных, следует:



Расположить в редакторе ресурсов для каждого поля таблицы соответствующий ему элемент управления.

Вставить в метод DoDataExchange класса отображения вызовы методов DDX_FieldText (или DDX_FieldCheck, DDX_FieldRadio, DDX_FieldSlider и т.п.), выполняющие связь между идентификатором ресурса и переменной – членом класса результирующего набора.

Например:

void CP2View::DoDataExchange(CDataExchange* pDX) { CRecordView::DoDataExchange(pDX); DDX_FieldText(pDX, IDC_EDIT1, // Идентификатор ресурса m_pSet->m_f1, // Переменная — член // класса, наследуемого // от CRecordset m_pSet); // Член класса, наследуемого // от CRecordView: CP2Set* m_pSet; // Член класса, наследуемого от // CDocument: CP2Set m_P2Set; // Метод OnInitialUpdate класса // наследуемого от CRecordView : // CDocument: CP2Set m_P2Set; // m_pSet = &GetDocument()->m_P2Set; DDX_FieldText(pDX, IDC_EDIT2, m_pSet->m_f2, m_pSet); }

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


Добавление строк


Метод CRowset::Insert создает новую строку, используя данные из аксессора, и вставляет ее после текущей строки.

Метод Insert имеет следующее формальное описание:

HRESULT Insert(int nAccessor = 0, bool bGetRow = false)

Параметр bGetRow определяет, какая строка станет текущей: значение false (по умолчанию) указывает, что текущей будет новая вставленная строка; значение true указывает, что текущая строка не изменится.

В следующем примере иллюстрируется добавление новой строки после 10-й строки результирующего набора:

CTable<CAccessor<CTBL1Accessor> > tbl1; CSession session;

// Открываем результирующий набор tbl1.Open(session, "Product", &ps, 1); // ps - набор // свойств tbl1.MoveToBookmark(&bookmark, 0); // Переход // на 10-ю строку // Устанавливаем значение столбцов tbl1.m_F1 = 2020; _tcscpy( tbl1.m_F2, _T( "ABCD" ) ); tbl1.m_F3 = 137137; tbl1.m_F4 = 456; _tcscpy( tbl1.m_F5, _T( "123456789" ) ); tbl1.m_F6 = 3; tbl1.m_F7 = false;

// До добавления строки следует инициализировать поля // состояния и длины // Состояние столбцов m_dwF1Status = DBSTATUS_S_ISNULL; m_dwF2Status = DBSTATUS_S_ISNULL; m_dwF3Status = DBSTATUS_S_ISNULL; m_dwF4Status = DBSTATUS_S_ISNULL; m_dwF5Status = DBSTATUS_S_ISNULL; m_dwF6Status = DBSTATUS_S_ISNULL; m_dwF7Status = DBSTATUS_S_ISNULL; // Длина строк m_dwF2Length = 4; // "ABCD" - это 6 символов m_dwF5Length = 10; // "123456789" - это 9 символов // Добавление строки HRESULT hr = tbl1.Insert( );

Поля статуса и длины для каждого поля записываются в таблице столбцов, указываемой после BEGIN_COLUMN_MAP (при использовании мастера ATL OLE DB Consumer эта таблица формируется автоматически).

Например:

[db_source("insert connection string")] [db_command(" SELECT \ Au_ID, \ Author, \ ` Year Born`, \ FROM TblAuthors")]

class CAuthors { public: DWORD m_dwAuIDStatus; // Поля статуса DWORD m_dwAuthorStatus; DWORD m_dwYearBornStatus;

DWORD m_dwAuIDLength; // Поля длины DWORD m_dwAuthorLength; DWORD m_dwYearBornLength;


BEGIN_COLUMN_MAP(CTblAuthorsAccessor) // Таблица // столбцов COLUMN_ENTRY_LENGTH_STATUS(1, m_AuID, dwAuIDLength, dwAuIDStatus) COLUMN_ENTRY_LENGTH_STATUS(2, m_Author, dwAuthorLength, dwAuthorStatus) COLUMN_ENTRY_LENGTH_STATUS(3, m_YearBorn, dwYearBornLength, dwYearBornStatus) END_COLUMN_MAP()

[ db_column(1, status=m_dwAuIDStatus, length=m_dwAuIDLength) ] LONG m_AuID; [ db_column(2, status=m_dwAuthorStatus, length=m_dwAuthorLength) ] TCHAR m_Author[51]; [ db_column(3, status=m_dwYearBornStatus, length=m_dwYearBornLength) ] SHORT m_YearBorn;

void GetRowsetProperties(CDBPropSet* pPropSet) { pPropSet->AddProperty(DBPROP_IRowsetChange, true); pPropSet->AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE); } };

Для добавления входа в таблицу столбцов могут использоваться следующие макросы:

COLUMN_ENTRY_LENGTH - для получения поля длины; COLUMN_ENTRY_STATUS - для получения поля статуса;COLUMN_ENTRY_LENGTH_STATUS - для получения полей длины и статуса.


Классы, используемые для доступа к БД


ATL предоставляет OLE DB шаблоны как С++ шаблоны для реализации клиентов и серверов OLE DB.

Для реализации клиента OLE DB провайдера можно использовать следующие классы:

CDataConnection - класс, управляющий соединением с источником данных и инкапсулирующий поведение объектов OLE DB "источник данных" (DataSource) и "сеанс" (Session).

CDataSource - класс, соответствующий объекту OLE DB источник данных, предоставляющий соединение с источником данных через OLE DB провайдера. Для одного соединения можно создать несколько объектов сеансов (CSession).

CEnumerator - класс, соответствующий объекту OLE DB нумератор, предоставляющему средства для получения наборов данных, содержащих информацию об источниках данных.

CEnumeratorAccessor - класс, используемый классом CEnumerator для доступа к данным из набора данных нумератора.

CSession - класс, реализующий сеанс доступа к базе данных.

CAccessor - класс аксессора, используемый для записей статически связанных с источником данных. Этот класс используется в том случае, если известна структура источника данных.

CAccessorBase - базовый класс всех классов аксессоров.

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

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

CDynamicStringAccessor - класс аксессора, позволяющий реализовать доступ к источнику данных, если структура базы данных неизвестна.

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

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


CManualAccessor - класс аксессора, предоставляющий методы для связывания как столбцов, так и параметров во время выполнения.

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

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

CAccessorRowset - инкапсулирует работу с результирующим набором и соответствующим ему аксессором.

CArrayRowset - позволяет реализовывать доступ к результирующему набору как к массиву.

CBulkRowset - позволяет выполнять за один вызов функции выборку нескольких строк результирующего набора.

CNoRowset - может использоваться в том случае, если результирующий набор не возвращается (для CCommand или CTable).

CRestrictions - используется для задания ограничений для результирующих наборов схемы.

CRowset - применяется для извлечения данных и управления результирующим набором.

CStreamRowset - возвращает объект типа ISequentialStream, используемый далее для выборки данных в XML-формате (вызовом метода Read).

CCommand - применяется для задания и выполнения команд OLE DB; может использоваться для создания результирующих наборов; позволяет задавать параметры.

CMultipleResults - применяется для команд, создающих несколько результирующих наборов.

CNoMultipleResults - применяется по умолчанию для команд, создающих только один результирующий набор.

CTable - используется для доступа к результирующему набору без указания параметров.


Команды и таблицы


Классы команды и таблицы позволяют реализовывать доступ к результирующим наборам. Эти классы наследуются от класса CAccessorRowset, как это показано на следующей схеме:


В данной схеме TAccessor может быть любым допустимым типом аксессора, а TRowset - любым допустимым типом результирующего набора; TMultiple определяет тип результирующего набора (один или несколько).

В среде Visual Studio.NET мастер ATL OLE DB Consumer позволяет определить, какой класс будет использован для создания результирующего набора. Класс CTable следует применять в том случае, если результирующий набор только один и не требуется использование параметров.

Применение класса CCommand открывает более широкие возможности:

создание одного или нескольких результирующих наборов;использование параметрических запросов.

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

Класс CCommand имеет три параметра шаблона: тип аксессора, тип результирующего набора и тип результата. Тип результата может указываться значением типа CNoMultipleResults (по умолчанию) или CMultipleResults.



Обновление записей


Метод CRowset::SetData устанавливает значение для одного или нескольких столбцов текущей строки.

Например:

// Класс таблицы основывается на классе записи CTable<CAccessor<CTBL1Accessor> > tbl1; CSession session;

// Открываем результирующий набор и переходим // к 10-й строке tbl1.Open(session, "TBL1", &ps, 1); // ps - набор // свойств tbl1.MoveToBookmark(&bookmark, 0); // Предположим, //что закладка установлена на 10-й строке // Изменим значения полей F1 и F2 текущей строки // в таблице TBL1 (устанавливаем значения связанных // полей согласно аксессору) _tcscpy(tbl1.m_F1, _T( "ABCD" ) ); tbl1.m_F2 = 123; // Внесем изменения в таблицу HRESULT hr = tbl1.SetData( );



Отображение результирующего набора в элементах управления MFC


Для того чтобы отобразить OLE DB результирующий набор в MFC-приложении, следует использовать MFC-класс визуализации данных COleDBRecordView. Это позволит отобразить значения столбцов результирующего набора в элементах управления MFC.

Объект типа COleDBRecordView должен быть явно связан с результирующим набором, создаваемым OLE DB провайдером из класса CRowset.

Например:

COleDBRecordView myRecordView; ... // CTBL1Accessor - класс записи CRowset<CAccessor<CTBL1Accessor>> myRowSet = myRecordView.OnGetRowset();

Для отображения полей результирующего набора в элементах управления и для внесения изменений, сделанных в элементах управления, в полях результирующего набора используются DDX-методы. Переход между записями осуществляется вызовом функций MoveFirst, MoveNext, MovePrev и MoveLast.



Подключение к БД


Для подключения к базе данных следует создать объект типа CDataSource, затем объект типа CSession. Далее, уже используя объект "сеанс", можно реализовывать работу с таблицами базы данных.

В следующем примере иллюстрируется подключение к базе данных с использованием OLE DB провайдера, создание сеанса и получение доступа к таблице TBL1:

#include <atldbcli.h>

CDataSource con; CSession session;

CTable<CAccessor<CTBL1> > Tbl1; // Аксессор использует // класс записи CTBL1

// Устанавливаем соединение с базой данных con.Open(CLSID_MSDASQL,"MyDB1","user1","psw1"); // Создаем сеанс session.Open(connection); // Открываем таблицу c именем TBL1 (создаем результирующий набор). // Структура таблицы должна соответствовать классу записи CTBL1 Tbl1.Open(session, "TBL1"); // Извлекаем данные из результирующего набора while (Tbl1.MoveNext() == S_OK) { cout << Tbl1.m_szF1; // Записываем в стандартный // поток вывода значение // первого поля (класс CTBL1) cout << Tbl1.m_szF2; } // Класс записи CTBL1 class CTBL1 { public: // Поля записи CHAR m_szF1[10]; CHAR m_szF2[12]; short m_nF3; // Таблица связывания столбцов // Каждый вход таблицы сопоставляет номер столбца // с полем в классе записи. BEGIN_COLUMN_MAP(CTBL1) COLUMN_ENTRY(1, m_szF1) COLUMN_ENTRY(2, m_szF2) COLUMN_ENTRY(3, m_nF3) END_COLUMN_MAP()



Редактирование результирующего набора


Технология OLE DB позволяет выполнять редактирование результирующего набора, добавляя, удаляя или изменяя записи.

Для выполнения этих действий в классе CRowset реализован интерфейс IRowsetChange, предоставляющий следующие методы:

SetData - для изменения записи; Insert - для добавления записи; Delete - для удаления записи.

Для редактирования записей должно быть соответствующим образом установлено значение свойства DBPROP_UPDATABILITY результирующего набора. Это можно выполнить как в мастере ATL OLE DB Consumer, так и впоследствии вызовом метода AddProperty.

Тип поддерживаемого редактирования указывается следующими константами:

DBPROPVAL_UP_CHANGE - разрешение изменения записи;DBPROPVAL_UP_INSERT - разрешение вставки новой записи;DBPROPVAL_UP_DELETE - разрешение удаления записи.

Например:

CDBPropSet ps(DBPROPSET_ROWSET); ps.AddProperty(DBPROP_IRowsetChange, true) ps.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE)

На следующей схеме приведена иерархия классов результирующих наборов:




Выборка данных


Для того чтобы приступить к выборке данных, предварительно должны быть открыты источник данных и сеанс. В зависимости от используемого типа аксессора, может потребоваться связывание данных.

На следующей схеме приведена иерархия классов аксессоров.


Класс записи должен содержать переменные члены класса, соответствующие (по типам) полям создаваемого результирующего набора.

Для извлечения данных достаточно выполнить следующие действия:

Откройте результирующий набор, выполнив функцию Open. При использовании аксессора типа CManualAccessor выполните связывание столбцов.

Например:

// Получение информации о столбцах ULONG ulColumns = 0; DBCOLUMNINFO* pColumnInfo = NULL; LPOLESTR pStrings = NULL; // rc - результирующий набор if (rs.GetColumnInfo(&ulColumns, &pColumnInfo, &pStrings) != S_OK) ThrowMyOLEDBException(rs.m_pRowset, IID_IColumnsInfo); // Ошибка выполнения struct MYBIND* pBind = new MYBIND[ulColumns]; // Создание аксессора rs.CreateAccessor(ulColumns, &pBind[0], sizeof(MYBIND)*ulColumns); // Цикл по числу столбцов for (ULONG l=0; l<ulColumns; l++) rs.AddBindEntry(l+1, DBTYPE_STR, sizeof(TCHAR)*40, &pBind[l].szValue, NULL, &pBind[l].dwStatus); rs.Bind(); // Выполнение связывания // Цикл по курсору (результирующему набору) while (rs.MoveNext() == S_OK) { // Извлечение данных // Если не используется автоаксессор, то // вызывается метод rs.GetData() }

Извлечение данных выполняется согласно используемому типу аксессора:

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

Например:

while (rs.MoveNext() == S_OK) { cout<<rs.m_nF3; }

При использовании классов типа CDynamicAccessor или CDynamicParameterAccessor данные извлекаются вызовом функций GetValue и GetColumn. При этом тип данных может быть определен вызовом функции GetType.

Например:

while (rs.MoveNext() == S_OK) { // Определение числа столбцов ULONG ulColumns = rs.GetColumnCount(); for (ULONG i=0; i<ulColumns; i++) { rs.GetValue(i); // Извлечение данных } }

При использовании класса типа CManualAccessor следует определить переменные для извлечения значений столбцов, связать эти переменные со столбцами, и извлечение данных будет выполняться в эти переменные.

Например:

while (rs.MoveNext() == S_OK) { // использование переменных, определенных // при вызове AddBindEntry. cout<<szF1; }



Класс TADOCONNECTION


Класс TADOConnection обеспечивает соединение с данными, доступ к которым реализуется через ADO-объекты.

ADO-объекты позволяют работать с различными хранилищами данных, которые могут и не быть SQL-операторами. Объекты типа TADOConnection используют для доступа к данным OLE DB провайдеры.

Компоненты TADOCommand и TADODataSet связываются с источником данных посредством объекта TADOConnection, указывая ссылку на него как значение свойства Connection.

Для идентификации соединения необходимо определить значение свойства ConnectionString (строка соединения) компонента TADOConnection, которое может основываться на указании:

datalink-файла;строки соединения.

Если в качестве значения свойства ConnectionString указано имя UDL-файла, то настройку соединения можно выполнять автономно от приложения (например, указывая имя базы данных Microsoft SQL Server на текущем ПК).



Класс TDATABASE


Класс TDatabase реализует работу с объектом "база данных" и предоставляет средства контроля над соединением с базой данных.

Компонент типа TDatabase позволяет управлять транзакциями, использовать настраиваемое подключение к серверу, определять в приложении свои псевдонимы для базы данных.

Для работы с компонентом TDatabase необходимо установить значение свойств AliasName и DatabaseName.

Если значением свойства AliasName указан DSN существующего источника данных, то разработчик может сам определить любой внутренний (для приложения) псевдоним базы данных и задать его в свойстве DatabaseName. В этом случае для любого набора данных в списке значений свойства DatabaseName будет отображаться наряду со всеми доступными DSN источниками данных и внутренний псевдоним, заданный свойством DatabaseName компонента TDatabase.

В том случае, если DSN не определен, то свойство DatabaseName должно содержать полное имя файла базы данных, а свойство DriverName - указывать используемый ODBC-драйвер.

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

Exclusive - свойство, позволяющее приложению получить исключительный доступ к базе данных (если это поддерживается SQL-сервером);InTransaction - свойство, указывающее, был ли выполнен для базы данных вызов метода StartTransaction.ReadOnly - метод, указывающий, установлен ли для соединения с базой данных доступ "только чтение".

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

Свойство TransIsolation может быть указано одним из следующих значений:

tiDirtyRead - транзакция может читать данные, которые были изменены другой транзакцией, но для которых не был выполнен вызов Commit (фиксация изменений);

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

tiRepeatableRead - истинность данных гарантируется на все время чтения, и транзакция не видит никаких изменений, сделанных другой транзакцией. Прочитанная запись остается постоянной, пока в ней не будут сделаны изменения внутри самой транзакции;

StartTransaction - метод, открывающий новую транзакцию;Commit - метод, выполняющий фиксацию текущей транзакции;Rollback - метод, выполняющий откат текущей транзакции;Execute - метод, который выполняет указанный параметром SQL-оператор, не возвращающий результирующего набора.



Класс TDATASET


Класс TDataSet является базовым для всех классов наборов данных, наследующих общие свойства и методы этого класса, включая следующие:

Active - свойство, определяющее, открыт ли набор данных;CurrentRecord - свойство, определяющее номер текущей записи набора данных;DataSource - свойство, указывающее родительскую таблицу (для таблиц, связанных отношением родительская - дочерняя);Bof - свойство, определяющее, находится ли курсор на первой записи набора данных;Eof - свойство, определяющее, достигнут ли конец набора данных;Bookmark - свойство, указывающее текущую закладку в наборе данных. Закладка отмечает позицию в наборе данных. Используя методы TDataSet.GetBookmark и TDataSet.GotoBookmark, приложение может запоминать и быстро переходить на нужную позицию в наборе данных;FieldCount - свойство, указывающее количество полей в наборе данных;

Fields - свойство, представляющее собой массив полей набора данных и используемое для доступа к этим полям.

Например:

Table1.Edit; // Включение режима редактирования Table1.Fields.Fields[6].AsString := Edit1.Text; Table1.Post; // Присвоение изменений

Свойство Fields позволяет:

получить имя поля в текущей структуре записи.

Например:

var S: String; begin S := Fields[0].FieldName; // Имя первого поля S := Fields[1].FieldName; // Имя второго поля ... end;записать в переменную значение поля.

Например:

var s: String; i: Integer; d: TDateTime; s := Fields[0].AsString; i := Fields[0].AsInteger; d := Fields[0].AsDate;

Вместо Fields[n] можно использовать функцию FieldsByName('имя_поля').

Например:

s := Fields[0].AsString; s := FieldsByName('F1').AsString;

FieldByName - метод, определяющий поле набора данных по его имени;

Например:

Table1.FieldByName('F1').AsInteger:=StrToInt(Edit1.Text);

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

Определение фильтра должно удовлетворять следующим правилам:

фильтр состоит из условий для полей набора данных, объединенных логическими операциями AND и OR.
Например: F2 > 10 AND F2 <50; если имя поля содержит пробелы, то оно должно быть заключено в квадратные скобки или двойные кавычки. Например: [Field Name1] > 50;

Filtered - свойство, указывающее, используется ли фильтр, заданный свойством Filter;Found - свойство, определяющее, успешно ли выполнен поиск методами FindFirst, FindLast, FindNext или FindPrior;Modified - свойство, определяющее, была ли изменена активная запись;RecordCount - свойство, содержащее общее число записей в наборе данных;

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

dsInactive - набор данных закрыт;dsBrowse - данные доступны только для просмотра;dsEdi - можно изменять активную запись;dsInsert - активной записью является новая запись, пока не сохраненная;dsSetKey - просмотр ограниченного множества записей (SetRange) или поиск записи;dsCalcFields - выполняется обработчик события OnCalcFields;dsFilter - выполняется обработчик события OnFilterRecord;dsOpening - набор данных находится в процессе открытия.Append - метод, добавляющий в конец набора данных новую запись;Delete - метод, удаляющий текущую запись из базы данных. Если в момент удаления записи набор данных находится в неактивном состоянии, то инициируется исключение;Edit - метод, переводящий текущую запись в режим редактирования;Cancel - метод, отменяющий изменения, сделанные в текущей записи;Post - метод, выполняющий внесение изменений в базу данных;Refresh - метод, выполняющий обновление результирующего набора путем повторного извлечения данных из БД;Insert - метод, вставляющий в набор данных новую запись;InsertRecord - метод, вставляющий в набор данных новую запись со значениями, которые указаны параметрами метода;Close - метод, закрывающий набор данных;Open - метод, открывающий набор данных;First - метод, устанавливающий курсор на первую запись набора данных и делающий эту запись активной;Last - метод, устанавливающий курсор на последнюю запись набора данных и делающий эту запись активной;Next - метод, перемещающий курсор на следующую запись набора данных и делающий эту запись активной;Prior - метод, перемещающий курсор на предыдущую запись набора данных и делающий эту запись активной.


Класс TDATASOURCE


Класс TDataSource реализует связь между компонентами - наборами данных и элементами управления, используемыми для отображения данных.

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

Класс TDataSource содержит набор свойств и методов, используемых для доступа к набору данных, включая следующие:

AutoEdit - свойство, определяющее, будет ли автоматически вызываться метод Edit набора данных при получении фокуса элементом управления, ассоциированным с источником данных;

DataSet - свойство, указывающее используемый набор данных.

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

Например: DataSource.DataSet := Table1;.

Enabled - свойство, определяющее, будет ли элемент управления отображать ассоциированные с ним данные, или будет отображаться пустым;

State - свойство, позволяющее определить состояние используемого набора данных.

Например:

if DataSource1.Dataset <> nil then //Кнопка доступна только в том случае, если набор //данных находится в состоянии редактирования //или вставки новой записи BtnPost1.Enabled := DataSource1.State in [dsEdit, dsInsert];



Класс TQUERY


Компонент типа TQuery позволяет выполнять любой SQL-оператор, допустимый по синтаксису ODBC-драйвером. Если в качестве выполнимого оператора используется SQL-оператор SELECT, то компонент возвращает набор данных (результирующий набор).

В отличие от класса Ttable, класс TQuery позволяет создавать наборы данных из нескольких таблиц, а также ограничивать получаемый набор данных определенными условиями. Это отменяет необходимость извлечения всех записей таблицы в набор данных, что, в свою очередь, экономит память, сокращает сетевой трафик для удаленных баз данных и уменьшает время доступа.

Для определения набора данных TQuery следует установить значение свойства SQL и, возможно, свойства DatabaseName (свойство DatabaseName определяет имя источника данных, но для некоторых баз данных можно задать полное имя таблицы, включающее месторасположение файла, в тексте SQL-оператора, - в этом случае свойство DatabaseName не используется). Наиболее правильным подходом все же следует считать тот, при котором имя DSN источника данных указывается свойством DatabaseName, а в SQL-операторе определяется только имя таблицы без определения ее местоположения.

По умолчанию, набор данных, формируемый компонентом типа TQuery, не является редактируемым.

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

связать компонент TQuery с компонентом типа TUpdateSQL (например: Query1. Query1.UpdateObject:= UpdateSQL1;) и определить для последнего значение свойства ModifySQL (например: update TBL1 set F1 = :F1, F2 = :F2 where F3 = :OLD_F3);установить значение свойства RequestLive равным True (поддержка этой возможности зависит от используемой базы данных).

Класс TQuery содержит свойства и методы, используемые для работы с набором данных, включая следующие:

DataSource - свойство, позволяющее указать родительский набор данных (для отношения "родительский-дочерний").

Например, если свойство SQL содержит значение 'SELECT * FROM Tbl1 t WHERE (t.FNo = :FNo)', то значение переменной связи :FNo будет определяться из источника данных, указанного свойством DataSource.




Params - свойство, содержащее список параметров для SQL-оператора.

Например:

Query3.SQL.Clear; // Очищаем значение свойств // Динамически формируем код SQL-оператора INSERT Query3.SQL.Add('INSERT INTO ADDRESS (F1, F2)'); // Имена параметров указываются после символа : Query3.SQL.Add('VALUES (:F1, :F2)'); // Устанавливаем значение параметров Query3.Params[0].AsString := 'Abc'; Query3.Params[1].AsInteger := 123; Query3.ExecSQL; // Выполнение SQL-оператораRequestLive - свойство, определяющее, будет ли создаваемый набор данных редактируемым (возможность получения модифицируемого результирующего набора зависит от используемого SQL-сервера);SQL - свойство, содержащее текст SQL-оператора (для автоматического формирования SQL-оператора можно вызвать из контекстного меню компонента TQuery диалог SQL Builder);DatabaseName - свойство, определяющее имя подключаемого источника данных (имя DSN источника данных или имя, введенное классом типа TDatabase);

ExecSQL - метод, выполняющий SQL-оператор, указанный свойством SQL (для SQL-оператора, создающего набор данных, вместо ExecSQL используется метод Open).

ExecSQL можно вызывать для таких SQL-операторов как INSERT, UPDATE, DELETE, CREATE TABLE и т. п.

Если перед вызовом ExecSQL не был вызван метод Prepare, то SQL-оператор будет одновременно и откомпилирован, и выполнен.



Prepare - метод, выполняющий компиляцию SQL-оператора.

Вызов этого метода перед ExecSQL увеличивает скорость выполнения запроса при многократном повторении вызовов ExecSQL для одного и того же оператора (например, параметризированного запроса). Это позволяет откомпилировать SQL-оператор только один раз, а затем многократно его выполнять.




Класс TSQLTABLE


Класс TSQLTable представляет таблицу базы данных, доступную для клиента как направленный набор данных. Такой набор содержит все записи для полей, определенных в классе TSQLTable. Объект типа TSQLTable должен быть связан с объектом типа TSQLConnection, определяющим соединение с источником данных. Для отображения такого набора данных нельзя использовать таблицу, так как у клиента отсутствует кэш памяти для набора данных. Значение полей таблицы можно отображать компонентами типа TDBText или TDBEdit. Для перемещений по набору записей доступны только методы First и Next.

После размещения в модуле данных или на форме компонента надо выполнить следующие действия:

Установить значение свойства SQLConnection компонента TSQLTable, выбрав добавленный ранее компонент типа TSQLConnection из предлагаемого списка.Определить имя таблицы базы данных, используемой для построения набора данных, определив свойство TableName компонента TSQLTable.



Класс TTABLE


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

Компоненты типа TTable могут использовать все свойства и методы, наследуемые от класса TDataSet, а также свойства и методы класса TTable для набора данных, включая следующие:

DatabaseName - свойство, определяющее имя источника данных DSN;CanModify - свойство, определяющее, может ли приложение выполнять вставку, редактирование и удаление записей в таблице;DefaultIndex - свойство, определяющее, должны ли данные в таблице быть упорядочены при ее открытии. Если значение свойства равно True (по умолчанию), то выполняется упорядочивание по первичному ключу или уникальному индексу;IndexName - свойство, позволяющее определить вторичный индекс, используемый для сортировки открываемого набора данных;Exclusive - свойство, позволяющее установить исключительный режим доступа к таблице (значение свойства должно быть определено до открытия таблицы);MasterSource - свойство, определяющее имя компонента "источник данных" родительской таблицы для установления отношения между таблицами "родительская-дочерняя";MasterFields - свойство, определяющее одно или несколько полей из родительской таблицы, служащих для связи с соответствующими полями данной дочерней таблицы (Это задает отношение между родительской и дочерней таблицами. Поля в списке разделяются точкой с запятой);ReadOnly - свойство, позволяющее установить для таблицы режим доступа "только для чтения";TableName - свойство, указывающее используемую таблицу базы данных;RecNo - свойство, указывающее номер текущей записи набора данных;FindKey - метод, выполняющий поиск значения или значений, перечисленных в списке, для ключевого поля;

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

Например:

{Изменение значения в компоненте Edit1 автоматически перемещает позицию курсора в наборе данных Table1} procedure TForm1.FormActivate(Sender: TObject); begin Table1.DatabaseName := 'DBDemos'; Table1.TableName := 'Customer.db'; Table1.Active := True; Table1.IndexName := 'ByCompany'; {Ключевое поле} end; {Обработчик события OnChange (изменение значения) для компонента Edit1} procedure TForm1.Edit1Change(Sender: TObject); begin Table1.FindNearest([Edit1.Text]); {Выполнение поиска} end;

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

Например:

with Table1 do Locate('F1;F2', VarArrayOf(['ABC', 123]), [loPartialKey]);



Класс TUPDATESQL


Объект типа TUpdateSQL позволяет для наборов данных, созданных с доступом "только для чтения", поддерживать возможность их обновления посредством выполнения SQL-оператора.

Класс TUpdateSQL реализует следующие свойства и методы:

DeleteSQL - свойство, определяющее SQL-оператор DELETE.InsertSQL - свойство, определяющее SQL-оператор INSERT.ModifySQL - свойство, определяющее SQL-оператор UPDATE.ExecSQL - метод, выполняющий один из заданных SQL-операторов (в зависимости от значения параметра, указываемого следующими константами: ukDelete, ukInsert,ukModify).



Классы компонентов управления данными


Компоненты управления данными расположены на странице Data Controls палитры компонентов. Многие из этих компонентов аналогичны элементам управления страницы Standard, с тем лишь отличием, что связаны через источник данных (компонент типа TDataSource) с определенным полем (или полями) из набора данных (компонентов типа TTable или TQuery).

Библиотека VCL предоставляет следующие классы компонентов управления данными:

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

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

DBNavigator1.BtnClick(nbPost); // Присвоение сделанных изменений.

Компонент TDBNavigator может отображать кнопки, указываемые следующими константами:

nbFirst - переход к первой записи;

nbPrior - переход к предыдущей записи;

nbNext - переход к следующей записи;

nbLast - переход к последней записи;

nbInsert - вставка перед текущей записью новой записи и переход на нее;

nbDelete - удаление текущей записи;

nbEdit - переход в режим редактирования текущей записи;

nbPost - внесение изменений текущей записи в базу данных;

nbCancel - отмена изменений, сделанных в текущей записи;

nbRefresh - повторное считывание значений полей из источника данных.

TDBText - класс, позволяющий в качестве надписи отображать значение поля текущей записи набора данных.TDBEdit - класс, реализующий работу с однострочным полем редактирования.TDBMemo - класс, реализующий многострочное поле редактирования, в котором можно отображать и изменять значение поля набора данных.TDBImage - класс, реализующий объект "рисунок", в котором можно отображать и изменять значение поля набора данных формата BLOB.TDBRadioGroup - класс, реализующий группу радиокнопок, которые связаны с полем базы данных.
Применение такого объекта предоставляет пользователю удобную возможность устанавливать значение поля базы данных, выбирая его из предлагаемых опций.TDBCheckBox - класс, реализующий компонент "флажок", который связан с полем базы данных.TDBListBox - класс, реализующий компонент "список", который используется для отображения значений поля набора данных. Значения, отображаемые в списке, содержатся в свойстве Items.TDBComboBox - класс, реализующий компонент "раскрывающийся список", который используется для отображения значений поля набора данных. Значения, отображаемые в списке, содержатся в свойстве Items.TDBLookupListBox - класс, позволяющий выполнять просмотр списка, заполненного значениями полей из другого набора данных. Просматриваемый набор данных указывается свойством ListSource, просматриваемое поле (или поля) - свойством ListField. Свойство KeyField указывает поле просматриваемого набора данных, соответствующее полю текущего набора данных, указываемого свойствами DataField и DataSource. Данный класс позволяет выбирать значение поля текущего набора данных из другого просматриваемого набора данных;TDBCtrlGrid - класс, реализующий особый вид таблицы, в которой каждая запись отображается на отдельной панели (количество панелей в компоненте указывается значением свойства RowCount).


Механизмы доступа к БД


VCL-библиотека классов среды проектирования Delphi предоставляет ряд классов, позволяющих быстро и эффективно разрабатывать различные приложения баз данных.

Эти классы представлены следующими группами:

компоненты для доступа к данным, реализующие: доступ через машину баз данных BDE (Borland Database Engine), предоставляющую доступ через ODBC-драйверы или через внутренние драйверы машины баз данных BDE (компоненты страницы BDE-палитры инструментов);доступ через ADO-объекты (ActiveX Data Objects), в основе которого лежит применение технологии OLE DB (компоненты страницы ADO);доступ к локальному или удаленному SQL-серверу InterBase (компоненты страницы InterBase);доступ посредством легковесных драйверов dbExpress;доступ к БД при многозвенной архитектуре (компоненты страницы DataSnap);

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

Основными механизмами доступа к данным, поддерживаемым в Delphi, являются:

ODBC - доступ через ODBC-драйверы БД или BDE-драйверы;OLE DB - доступ с использованием провайдеров данных (OLE DB - это метод доступа к любым данным через стандартный COM-интерфейс);средства dbExpress, использующие легковесные драйверы БД; средства доступа к распределенным наборам данных в многозвенной архитектуре.

Самый простой механизм управления данными, использующий ODBC-драйверы, может быть реализован по следующей схеме:

В модуль данных (или в форму) добавляется компонент набора данных (объект класса TDataSet) и устанавливается связь с источником данных, определяемая свойством DatabaseName. Связь может быть указана одним из трех способов: по имени базы данных, каталогу или псевдониму (способ указания связи может быть ограничен типом источника данных). Список всех псевдонимов доступен на этапе проектирования.В модуль данных (или в форму) добавляется компонент источника данных (TDataSourse), являющийся центральным связующим звеном между набором данных и элементами управления, отображающими эти данные.
Свойство DataSet компонента типа TDataSourse указывает набор данных, формируемый компонентами таких классов как TTable или TQuery. Если компоненты набора данных и источника данных расположены в модуле данных, то их следует добавить в проект (команда меню File | Use unit).В форму добавляются элементы управления для работы с данными, такие как TDBGrid, TDBEdit, TDBCheckbox. Они связываются с компонентом источником данных, который указывается свойством DataSource. Имя поля набора данных определяется свойством DataField.

Графически схему работы с базами данных для двухзвенных архитектур в среде Delphi можно представить следующим образом:

Машина баз данных BDEПровайдеры OLE DBdbExpressInterBaseTDatabase (необязательный)TADOConnectionTSQLConnectionTIBDatabaseНевизуальные компоненты набора даныхTTable, TQuery, TDataSet, TField TADODataSet, TADOTable, TADOQueryTSQLDataSet, TSQLQuery, TSQLTable, TSQLStoreProc, TSQLClientDataSetTIBDataSet, TIBTable, TIBQury(
свойство DataSet) Компонент "источник данных" - центральный связующий компонент TDataSource (
свойство DataSource) Визуальные компоненты для работы с данными TDBGrid, TDBEdit, TDBText, TDBMemo, TDBCheckbox, TDBNavigator и др.
Для сохранения данных из БД в XML-формате или двоичном формате, и обратно, для формирования набора данных из XML или двоичного файла применяется провайдер данных.

Графически схема сохранения данных из БД в XML-формате приведена на следующей схеме:

Набор данных (TTable, TQuery или др.)Клиентский набор данных TClientDataSetПровайдер данных TDataSetProviderФайл в одном из следующих форматов: двоичный формат (dfBinary); XML-формат (dfXML); XML-формат, использующий UTF8 (dfXMLUTF8)
(свойство DataSet)
(свойство ProviderName)
(метод SaveToFile)
(метод LoadFromFile)

Наборы данных


Предком всех классов наборов данных является класс TDataSet. Он определяет основу структуры всех наборов данных - массив компонентов типа TField (каждый элемент массива соответствует столбцу таблицы).

Набор данных - это упорядоченная последовательность строк, извлеченных из источника данных. Каждая строка набора данных состоит из полей, указываемых в свойствах класса.

В зависимости от механизма доступа, используемого приложением, базовыми классами набора данных могут быть:

TTable, TQuery, TStoredProc - для однозвенных или двухзвенных приложений, использующих машину баз данных BDE. Класс TQuery дополнительно позволяет выполнять параметрические запросы;TClientDataSet - для реализации клиентского набора данных и для многозвенной архитектуры, использующей распределенный доступ;TADODataSet - для приложений, использующих ADO-объекты;TSQLDataSet - для доступа к базе данных посредством dbExpress. Этот класс реализует направленный набор данных, функционирующий по принципу курсора. Для такого набора данных не создается кэш памяти на клиенте, и среди методов доступа возможны только методы Next и First. Редактирование записей в направленном наборе данных возможно только явным выполнением SQL-оператора UPDATE или при установке соединения с клиентским набором данных через провайдера;TSQLTable и TSQLQuery - для доступа к базе данных посредством dbExpress.

На следующей схеме приведена иерархия классов наборов данных библиотеки VCL:


Для определения набора данных необходимо задать следующие свойства:

для класса TTable - значения свойств DatabaseName и TableName;для класса TQuery - значение свойства SQL и, возможно, свойства DatabaseName.

Для того чтобы читать данные из таблиц или записывать их в таблицы, набор данных предварительно должен быть открыт.

Открыть набор данных можно одним из следующих способов:

установить значение свойства Active набора данных равным True во время выполнения приложения (например, Table1.Active:= True;) или в режиме проектирования в инспекторе объектов;вызвать метод Open (например, Table1.Open;).


Аналогично, закрыть набор данных можно вызовом метода Close или установив значение свойства Active равным False. Для компонента типа TQuery метод Open может быть выполнен только для закрытого набора данных: попытка открыть уже открытый набор данных инициирует ошибку.

Открытие набора данных влечет за собой:

инициацию событий BeforeOpen и AfterOpen;установку состояния набора данных в dsBrowse;открытие курсора для набора данных.

Если в момент открытия набора данных произошла ошибка, то состояние набора данных устанавливается в dsInactive, а курсор закрывается.

При работе с компонентами наборов данных можно обойтись без явного использования компонентов, реализующих соединение с базой данных. Однако некоторые возможности, такие как управление транзакциями или кэшированные обновления, невозможны без компонентов типа TDatabase или TADOConnection. Компонент "база данных" TDatabase применяется для соединения с источником данных через драйверы BDE или внешние ODBC-драйверы. Компонент TADOConnection используется для создания объекта "соединение" при доступе через OLE DB, который инкапсулируется посредством ADO-объектов VCL-библиотеки.

По умолчанию при переходе от одной записи набора данных к другой происходит запись всех сделанных изменений в базу данных. Для того чтобы можно было отменять сделанные изменения или выполнять обновление нескольких записей, применяют кэшированные обновления. Они позволяют значительно снизить сетевой трафик за счет того, что все сделанные изменения хранятся во внутреннем кэше и при переходе от одной записи к другой информация в базу данных не передается. Чтобы включить режим кэшированного обновления, следует установить значение свойства CachedUpdates равным True для компонента набора данных. Для присвоения кэшированного обновления вызывается метод ApplyUpdates, а для отмены - CancelUpdates.


Применение многозвенных архитектур


Применение многозвенной архитектуры позволяет вынести бизнес-логику работы с данными в приложение-сервер.

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

Применение трехзвенной архитектуры приведено на следующей схеме.

приложение-клиент-

приложение-сервер

Приложение Remote Data Module

Компонент TDCOMConnection
(свойство ServerName или ServerGUID)
(свойство RemoteServer)
Компонент TClientDataSet
(свойство ProviderName)
Удаленный модуль данных - компонент TDataSetProvider
(свойство Dataset)
(свойство DataSet)
Компонент TDataSourceНабор данных - компонент TTable или Tquery-

Такая архитектура позволяет реализовывать доступ к серверу БД из приложения-сервера, не имея на клиентских машинах никаких драйверов доступа к базе данных.

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

Приложение-сервер получает набор данных стандартным способом - через один из компонентов набора данных, таких как TTable или Tquery, и пересылает его с помощью компонента TDataSetProvider компоненту TClientDataSet в приложение-клиент. Приложение-сервер реализуется как удаленный модуль данных, представляющий из себя СОМ-объект. Доступ к такому компоненту может быть выполнен посредством DCOM с любого удаленного компьютера.



События, инициируемые для наборов данных


При работе с наборами данных могут инициироваться следующие события:

AfterCancel и BeforeCancel - происходит после/до отмены в приложении всех изменений, сделанных для текущей записи.AfterClose и BeforeClose - происходит после/до закрытия набора данных и перевода базы данных в состояние dsInactive.AfterDelete и BeforeDelete - инициируется после/до удаления приложением текущей записи, перевода набора данных в состояние dsBrowse и перемещения позиции курсора на предыдущую запись.AfterEdit и BeforeEdi - происходит после/до начала редактирования приложением текущей записи.AfterInsert и BeforeInsert - происходит после/до того как приложение вставит новую запись.AfterOpen и BeforeOpen - происходит после/до того как приложение откроет набор данных, но до того, как какие-либо доступные данные будут отображены.AfterPost и BeforePost - происходит до завершения переноса значений активной записи в базу данных или внутренний кэш.AfterRefresh и BeforeRefresh - происходит после/до обновления набора данных.AfterScroll и BeforeScroll - происходит после/до перемещения позиции курсора на другую запись.OnCalcFields - происходит при открытии набора данных, перевода его в состояние dsEdit, перемещении фокуса ввода от одного компонента к другому или от одного столбца к другому, при изменениях записи или при извлечении записи из базы данных, но только в том случае, если значение свойства AutoCalcFields равно True;OnDeleteError - инициируется, если при попытке удаления строки произошла ошибка - было брошено исключение.OnEditError - инициируется, если при попытке изменения или вставки записи произошла ошибка - было брошено исключение.OnPostError - инициируется, если при попытке передать изменение или вставку новой записи происходит ошибка - бросается исключение.OnFilterRecord - происходит при изменении активной записи и только в том случае, если свойство State набора данных установлено равным dsFilter, а свойство Filtered равно True. Чтобы запись была включена в набор данных, для нее следует установить параметр Accept равным True.OnNewRecord - происходит при вставке или добавлении новой записи.



Интерфейс CONNECTION


Интерфейс java.sql.Connection определяет основные возможности для соединения с базой данных и предоставляет следующие константы и методы:

static final int TRANSACTION_NONE - константа, определяющая отсутствие поддержки транзакций;static final int TRANSACTION_READ_UNCOMMITTED - константа, определяющая режим "чтение без фиксации". Этот уровень изоляции позволяет читать из транзакции строки, измененные в другой незавершенной транзакции;static final int TRANSACTION_READ_COMMITTED - константа, определяющая режим "чтение с фиксацией". Данный режим предотвращает возможность "грязного" чтения (dirty read), но допускает неоднократное выполнение одного и того же запроса с различными результатами (неповторяющееся чтение) и фантомное чтение;static int TRANSACTION_REPEATABLE_READ - константа, определяющая режим повторяющегося чтения, который предотвращает возможность "грязного" чтения (dirty read) и неповторяющегося чтения, но допускает фантомное чтение;static int TRANSACTION_SERIALIZABLE - константа, определяющая режим "последовательное выполнение", который предотвращает возможность "грязного" чтения, неповторяющегося чтения и фантомного чтения;void close () - метод, закрывающий соединение с базой данных и освобождающий JDBC ресурсы;void commit () - метод, фиксирующий сделанные изменения в базе данных;Statement createStatement () - метод, создающий объект Statement, используемый для передачи SQL-оператора базе данных;Statement createStatement(int resultSetType, int resultSetConcurrency) - метод, создающий объект Statement, который будет создавать объект ResultSet указанного типа и с заданным уровнем параллельности. Параметр resultSetType определяется как ResultSet.TYPE_XXX, а параметр resultSetConcurrency - как ResultSet.CONCUR_XXX;boolean getAutoCommit () - метод, определяющий, установлен ли режим автокоммита;DatabaseMetaData getMetaData () - метод, возвращающий объект DatabaseMetaData, содержащий метаданные для подсоединенной базы данных;int getTransactionIsolation () - метод, возвращающий установленный уровень изоляции;boolean isClosed () - метод, определяющий, закрыто ли соединение;booleanisReadOnly () - метод, определяющий, установлен ли для соединения режим "только чтение";CallableStatement prepare Call(String sql) - метод, создающий объект CallableStatement, используемый для вызова хранимых процедур;CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) метод, создающий объект CallableStatement, который будет создавать объект ResultSet заданного типа и с указанным уровнем параллельности;PreparedStatement prepareStatement (String sql) - метод, создающий объект PreparedStatement, используемый для выполнения параметризированного SQL-оператора;void rollback () - метод, выполняющий откат транзакции;void setAutoCommit(boolean autoCommit) - метод, включающий или отключающий режим автокоммита;void setReadOnly (boolean readOnly) - метод, включающий или отключающий режим "только чтение";void setTransactionIsolation (int level) - метод, устанавливающий заданный уровень изоляции.



Интерфейс RESULTSET


Интерфейс java.sql.ResultSet реализует средства для работы с результирующим набором и предоставляет следующие константы и методы:

static int CONCUR_READ_ONLY - константа, определяющая режим результирующего набора "только чтение";static int CONCUR_UPDATABLE - константа, определяющая, что результирующий набор может быть изменяемым;static int FETCH_FORWARD - константа, определяющая, что строки в результирующем наборе обрабатываются только в одном направлении: от первой к последней;static int FETCH_REVERSE - константа, определяющая, что строки в результирующем наборе обрабатываются только в одном обратном направлении: от последней к первой. Эта константа используется в методе setFetchDirection как подсказка драйверу, которую тот может и игнорировать.static int FETCH_UNKNOWN - константа, указывающая, что порядок, в котором будут обрабатываться строки результирующего набора, не определен;static int TYPE_FORWARD_ONLY - константа, определяющая режим, при котором позиция курсора может перемещаться только в направлении вперед;static int TYPE_SCROLL_INSENSITIVE - константа, определяющая режим, при котором позиция курсора может изменяться в любом направлении, но результирующий набор нечувствителен к изменениям, вносимым другими транзакциями;static int TYPE_SCROLL_SENSITIVE - константа, определяющая режим, при котором позиция курсора может изменяться в любом направлении и результирующий набор чувствителен к изменениям, вносимым другими транзакциями;boolean absolute (int row) - метод, выполняющий перемещение позиции курсора результирующего набора на указанную строку;void afterLast () - метод, устанавливающий позицию курсора в конец результирующего набора после последней строки;void beforeFirst () - метод, устанавливающий позицию курсора в начало результирующего набора перед первой строкой;void cancelRowUpdates () - метод, отменяющий изменения, сделанные в текущей строке результирующего набора;void clearWarnings () - метод, удаляющий все уведомляющие сообщения, полученные для результирующего набора;void close () - метод, закрывающий результирующий набор и освобождающий все JDBC-ресурсы;void deleteRow () - метод, удаляющий текущую строку из результирующего набора и из источника данных;int findColumn (String columnName) - метод, возвращающий индекс столбца результирующего набора по заданному имени;boolean first () - метод, перемещающий позицию курсора на первую строку результирующего набора;Array getArray (int i) и Array getArray (String colName) - методы, возвращающие значение указанного столбца в виде массива (столбец может быть указан по имени или по индексу);BigDecimal getBigDecimal (int columnIndex) и BigDecimal getBigDecimal (String columnName) - методы, возвращающие значение указанного столбца как объект типа java.math.BigDecimal;Blob getBlob (int i) и Blob getBlob (String colName) - методы, возвращающие значение указанного столбца как Blob-объект;boolean getBoolean (int columnIndex) и boolean getBoolean (String columnName) - методы, возвращающие значение указанного столбца как логическое значение;byte getByte (int columnIndex) и byte getByte (String columnName) - методы, возвращающие значение указанного столбца как байт;byte[] getBytes (int columnIndex) и byte[] getBytes (String columnName) - методы, возвращающие значение указанного столбца как массив байтов;Clob getClob (int i) и Clob getClob (String colName) - методы, возвращающие значение указанного столбца как Сlob-объект;int getConcurrency () - метод, возвращающий установленный режим параллельности, который определяется значением CONCUR_READ_ONLY или CONCUR_UPDATABLE;String getCursorName() - метод, возвращающий имя курсора, используемое объектом ResultSet;Date getDate(int columnIndex), Date getDate(String columnName), Date getDate(int columnIndex, Calendar cal) и Date getDate(String columnName, Calendar cal) - методы, возвращающие значение указанного столбца как объект типа java.sql.Date;double getDouble (int columnIndex) и double getDouble (String columnName) - методы, возвращающие значение указанного столбца как значение типа double;int getFetchDirection() - метод, возвращающий используемое направление выборки данных из результирующего набора;int getFetchSize() - метод, возвращающий для объекта ResultSet размер текущей выборки данных;float getFloat(int columnIndex) и float getFloat(String columnName) - методы, возвращающие значение указанного столбца как значение типа float;int getInt(int columnIndex) и int getInt(String columnName) - методы, возвращающие значение указанного столбца как значение типа int;long getLong(int columnIndex) и long getLong(String columnName) - методы, возвращающие значение указанного столбца как значение типа long;ResultSetMetaData getMetaData() - метод, возвращающий в объекте ResultSetMetaData информацию о количестве, типе и свойствах столбцов результирующего набора;int getRow() - метод, возвращающий номер текущей строки;short getShort(int columnIndex) и short getShort(String columnName) - методы, возвращающие значение указанного столбца как значение типа short;Statement getStatement() - метод, возвращающий объект Statement, который был использован для создания данного результирующего набора;String getString(int columnIndex) и String getString(String columnName) - методы, возвращающие значение указанного столбца как значение типа String;Time getTime (int columnIndex), Time getTime (String columnName), Time getTime (int columnIndex, Calendar cal) и Time getTime (String columnName, Calendar cal) - методы, возвращающие значение указанного столбца как значение типа java.sql.Time;Timestamp getTimestamp (int columnIndex), Timestamp getTimestamp (String columnName), Timestamp getTimestamp (int columnIndex, Calendar cal) и Timestamp getTimestamp (String columnName, Calendar cal) - методы, возвращающие значение указанного столбца как значение типа java.sql.Timestamp;int getType () - метод, возвращающий тип данного результирующего набора;SQLWarning getWarnings () - метод, возвращающий первое уведомляющее сообщение, которое было получено для результирующего набора;void insertRow () - метод, вставляющий строку, которая была сформирована как новая строка в специальной области результирующего набора, в результирующий набор и в источник данных;boolean isAfterLast () - метод, определяющий, находится ли позиция курсора после последней строки результирующего набора;boolean isBeforeFirst () - метод, определяющий, находится ли позиция курсора перед первой строкой результирующего набора;boolean isFirst () - метод, определяющий, находится ли позиция курсора на первой строке результирующего набора;boolean isLast () - метод, определяющий, находится ли позиция курсора на последней строке результирующего набора;boolean last () - метод, перемещающий позицию курсора на последнюю строку результирующего набора;void moveToCurrentRow () - метод, возвращающий позицию курсора на текущую строку;void moveToInsertRow () - метод, перемещающий позицию курсора на вставляемую строку;boolean next () - метод, перемещающий позицию курсора на следующую строку результирующего набора;boolean previous () - метод, перемещающий позицию курсора на предыдущую строку результирующего набора;void refreshRow () - метод, выполняющий обновление значений текущей строки значениями из базы данных;void updateRow () - метод, выполняющий внесение изменений значений текущей строки в базу данных;boolean relative(int rows) - метод, перемещающий позицию курсора на указанное количество строк (когда такое перемещение разрешено) вперед, если параметр rows - больше нуля, или назад, если значение rows отрицательное;boolean rowDeleted () - метод, определяющий, была ли строка удалена;boolean rowInserted () - метод, определяющий, была ли текущая строка вставлена;boolean rowUpdated () - метод, определяющий, была ли текущая строка изменена;void setFetchDirection(int direction) - метод, устанавливающий направление обработки строк результирующего набора;void setFetchSize(int rows) - метод, предлагающий JDBCдрайверу ограничить количество строк, выбираемых в результирующий набор из базы данных;boolean wasNull() - метод, определяющий, имел ли последний прочитанный столбец значение SQL NULL;void updateNull(int columnIndex) и void updateNull(String columnName) - методы, устанавливающие значение указанного столбца как null;void updateBigDecimal(int columnIndex, BigDecimal x), void updateBigDecimal(String columnName, BigDecimal x), voidupdateBoolean(int columnIndex, boolean x), void updateBoolean(String columnName, boolean x), void updateBoolean(String columnName, boolean x), void updateByte(int columnIndex, byte x), void updateByte(String columnName, byte x), void updateBytes(int columnIndex, byte[] x), void updateBytes(String columnName, byte[] x), void updateDate(int columnIndex, Date x), void updateDate(String columnName, Date x), void updateDouble(int columnIndex, double x), void updateDouble(String columnName, double x), void updateFloat(int columnIndex, float x), void updateFloat(String columnName, float x), void updateInt(int columnIndex, int x), void updateInt(String columnName, int x), void updateLong(int columnIndex, long x), void updateLong(String columnName, long x), void updateObject(int columnIndex, Object x), void updateObject(int columnIndex, Object x, int scale), void updateObject(String columnName, Object x), void updateObject(String columnName, Object x, int scale), void updateShort(int columnIndex, short x), void updateShort(String columnName, short x), void updateString(int columnIndex, String x), void updateString(String columnName, String x), void updateTime(int columnIndex, Time x), void updateTime(String columnName, Time x), void updateTimestamp(int columnIndex, Timestamp x), void updateTimestamp(String columnName, Timestamp x) - методы, используемые для изменения значений столбцов в соответствии с их типами.



Интерфейс STATEMENT


Интерфейс java.sql.Statement реализует средства, позволяющие выполнять SQL-операторы, и предоставляет следующие методы:

boolean execute (String sql) - метод, выполняющий SQL-оператор, при котором может быть возвращено несколько результирующих наборов;int[] executeBatch () - метод, позволяющий выполнить набор SQL-операторов;ResultSet executeQuery (String sql) - метод, выполняющий SQL-оператор, который возвращает один объект типа ResultSet;int executeUpdate (String sql) - метод, выполняющий SQL-оператор, который не возвращает результирующего набора (INSERT, UPDATE или DELETE);Connection getConnection () - метод, возвращающий объект Connection, который использован для данного объекта типа Statement;Boolean getMoreResults () - метод, выполняющий перемещение на следующий результирующий набор в объекте Statement.



Использование JDBC-драйверов


Язык Java позволяет реализовывать доступ к таблице базы данных как через JDBC-драйверы, так и через мост JDBC-ODBC.

В любом случае для доступа к базе данных выполняется следующая последовательность действий:

Загружается класс драйвера. Это можно выполнить двумя способами: явно загрузить класс драйвера, регистрирующий себя в списке драйверов: // Параметр определяет применение моста JDBC-ODBC Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); // Параметр определяет применение JDBC-драйвера для БД Oracle DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());добавить имя драйвера в список системного свойства jdbc.drivers. Названия драйверов в списке разделяются символом двоеточия (например, jdbc.drivers=my.sql.Driver:wombat.sql.Driver;). При инициализации класс DriverManager просматривает системное свойство jdbc.drivers и в том случае, если пользователь добавил один или несколько драйверов, менеджер драйверов пытается их загрузить.

Устанавливается соединение с базой данных. После загрузки драйвера можно использовать класс DriverManager для открытия соединения с источником данных: // Мост JDBC-ODBC указывается как jdbc:odbc, // а DSN - это имя ODBC источника данных Connection con = DriverManager.getConnection ( "jdbc:odbc:DSN", "login", "password"); // Подключение к БД Oracle для пользователя scott с паролем tiger Connection con = DriverManager.getConnection ( "jdbc:oracle:oci:@datai_com", "scott", "tiger");

Загружать и регистрировать драйверы можно как методом Class.forName, так и методом DriverManager.registerDriver. Но в первом случае экземпляр драйвера создается неявно, а во втором при регистрации драйвера явным образом создается экземпляр драйвера.

При создании объекта типа Connection метод getConnection определяет параметр, содержащий JDBC-URL и передаваемый менеджером драйверов последовательно всем зарегистрированным драйверам: если драйвер распознает URL, то он возвращает экземпляр класса, реализующий интерфейс Connection.
Этот экземпляр класса и возвращает менеджер драйверов в результате вызова метода getConnection.

Далее для извлечения данных из результирующего набора в переменные языка Java выполняются:

Создание результирующего набора при выполнении SQL-оператора SELECT: Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT F1,F2,F3 FROM Table1");Выборка данных из результирующего набора: while (rs.next()) { int x = rs.getInt("F1"); // Имя поля F1 String s = rs.getString("F2"); float f = rs.getFloat("F3"); }

Метод executeQuery используется для создания результирующего набора, а для выполнения SQL-оператора, изменяющего информацию в базе данных, вызывается метод executeUpdate.

После завершения работы с данными необходимо последовательно освободить результирующий набор, оператор и соединение.

Например:

rs.close(); stmt.close(); con.close();

Возвращать результирующий набор может не только выполнение оператора SELECT, но и выполнение хранимой процедуры. Вызов хранимых процедур определяется интерфейсом java.sql.CallableStatement.

Например:

import oracle.jdbc.driver.*; // Для работы с JDBC-драйвером Oracle import java.sql.*; : CallableStatement cstmt; // Вызываемый операторный объект cstmt= con.prepareCall("{packeg1.metod1(?)}") // Оператор вызова // хранимой процедуры packeg1.metod1 cstmt.registerOutParameter(1,OracleTypes.CURSOR); // Регистрация // выходного параметра cstmt.execute(); // Выполнение операторного объекта OracleResultSet ors= (OracleResultSet) ((OracleCallableStatement) cstmt).getCursor(1); // Получение // результирующего набора для БД Oracle // из выходного параметра хранимой процедуры

По умолчанию при подключении через JDBC установлен режим автокоммита. Для применения транзакций этот режим следует отключить, а фиксацию изменений указывать явным вызовом метода Commit.

Например:

Connection con = DriverManager.getConnection ("jdbc:oracle:oci:@datai_com", "scott", "tiger"); con.setAutoCommit(false); : con.commit(); // Фиксация транзакции : con.rollback(); // Откат транзакции



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

import java.sql.*; // Импорт пакета из JDK

public class CreateMyTable { public static void main(String args[]) { String url = "jdbc:odbc:myDataSource"; // myDataSource - // имя источника данных DSN Connection con; String createString = "create table TEST (P1 VARCHAR(32)," + " P2 INTEGER, " + " P3 FLOAT, " + " P4 INTEGER)" ; Statement stmt; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); // Загрузка драйвера

} catch(java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: "); System.err.println(e.getMessage()); } try { // Соединение с источником данных с использованием объекта типа Connection con = DriverManager.getConnection(url, "Login", "Psw"); stmt = con.createStatement(); // Создание объекта оператор класса Statement stmt.executeUpdate(createString); // Выполнение SQL-оператора stmt.close(); // Освобождение объекта "оператор" con.close(); // Закрытие соединения } catch(SQLException ex) { System.err.println("SQLException: " + ex.getMessage()); } } }


Класс DRIVERMANAGER


Класс DriverManager предоставляет средства для управления JDBC драйверами и реализует следующие методы:

static Connection getConnection (String url) - метод, устанавливающий соединение с базой данных, указанной параметром url;static Connection getConnection (String url, String user, String password) - метод, устанавливающий соединение с базой данных, указанной параметром url; параметры user и password определяют имя и пароль пользователя;static int getLoginTimeout () - метод, определяющий максимальный интервал ожидания (в секундах) соединения с базой данных; static void setLoginTimeout (int seconds) - метод, устанавливающий максимальный интервал ожидания (в секундах) соединения с базой данных;static void registerDriver (Driver driver) - метод, выполняющий регистрацию заданного драйвера в списке драйверов менеджера драйверов;static void deregisterDriver (Driver driver) - метод, удаляющий драйвер из списка менеджера драйверов.



Компонент DATASTORE


Компонент DataStore представляет JDS-файл базы данных DataStore.

JBuilder предоставляет набор классов, поддерживающих работу с базой данных DataStore. Основные из них расположены на станице DataExpress палитры компонентов окна дизайнера.

Для того чтобы создать файл базы данных DataStore, надо выполнить следующие действия:

Создать объект типа DataStore. DataStore my_store = new DataStore();Определить имя создаваемой базы данных: my_store.setFileName("BD_DSTORE.jds");Определить имя пользователя, имеющего доступ к создаваемой базе данных: my_store.setUserName("CreateTx");Назначить используемый менеджер транзакций: my_store.setTxManager(new TxManager());Cоздать файл базы данных: store.create();

Для открытия и закрытия базы данных используются методы open и close объекта типа DataStore. Следующий пример иллюстрирует открытие базы данных типа DataStore.

import com.borland.datastore.*; public class CreateNew_DataStore { public static void main(String[] args) { DataStore ds1 = new DataStore(); try { ds1.setFileName("DS1.jds"); // Определяем имя БД java.io.File f = new java.io.File(ds1.getFileName()); ds1.setUserName("CreateTx"); ds1.setTxManager(new TxManager()); if(!f.exists()) ds1.create(); // Создание БД else ds1.open(); ds1.close(); } catch(Exception ex) { System.out.println(ex); } } }

Открыть базу данных DataStore можно несколькими способами:

выполнить метод open класса DataStore;использовать класс DataStoreConnection;выполнить метод getConnection класса DriverManager.

Механизм доступа к базе данных DataStore через JDBC-драйверы не отличается от механизма доступа к любой другой базе данных. В качестве загружаемого драйвера можно использовать com.borland.datastore.jdbc.DataStoreDriver.

Например: Class.forName("com.borland.datastore.jdbc.DataStoreDriver");

Если для открытия соединения используется статический метод getConnection, то в качестве первого параметра ему следует передать URL-адрес расположения файла базы данных, который формируется из двух частей: адреса ПК и полного имени файла.

Адрес ПК указывается следующим образом:

для локального компьютера - "jdbc:borland:dslocal:";для удаленного компьютера - "jdbc:borland:dsremote://localhost/"

Например:

Connection con = DriverManager.getConnection(URL + FILE, "user", "")



Компоненты DATABASE и QUERYDATASET


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

Для того чтобы самым простым способом отобразить значения из таблицы базы данных, следует добавить в проект в окне дизайнера компонент Database и определить для него значение свойства connection, указав используемый драйвер и путь доступа к базе данных или к DSN-источнику данных. При двойном щелчке мышью на свойстве connection JBUILDER откроет диалог Connection (рис. 13.1). Список Driver содержит все доступные приложению JDBC-драйверы.


Рис. 13.1.  Диалог определения свойства Connection компонента Database

Для автоматического отсоединения от базы данных при завершении приложения добавьте в окно компонент dbDisposeMonitor со страницы More dbSwing и установите значение его свойства dataAwareComponent равным this (компонент, при завершении которого будут закрываться все созданные для него соединения с базой данных).

Набор данных может быть указан компонентами TableDataSet и QueryDataSet.

Выполняемый для формирования набора данных SQL-оператор определяется в компоненте QueryDataSet. Этот компонент следует добавить в окно и двойным щелчком мыши на свойстве query открыть диалог определения запроса (рис. 13.2). В нем выбрать используемый объект типа Database и вручную или посредством построителя запросов SQL Builder (рис. 13.3) сформировать код SQL-оператора.


Рис. 13.2.  Редактор свойства query компонента QueryDataSet


Рис. 13.3.  Диалог SQL Builder для формирования SQL-оператора

Выбрав вкладку SQL, в любой момент времени можно просмотреть, как формируется SQL-оператор.

Для визуального отображения данных удобнее всего использовать компонент JdbTable со страницы dbSwing палитры компонентов. Этот компонент следует размещать в контейнере, реализуемом компонентом TableScrollPane.

Связь визуального компонента JdbTable, связь с набором данных определяется значением свойства dataset, выбираемым из предлагаемого списка.

Для того чтобы сохранить в таблице базы данных сделанные пользователем изменения, можно воспользоваться одним из следующих способов:

расположить в окне компонент JdbNavToolBar и связать его с набором данных, установив значение свойства dataset;расположить в окне командную кнопку и добавить для нее блок прослушивания для события действия. В созданный метод обработки события button_actionPerformed ввести следующий код: try { database1.saveChanges(queryDataSet1); } catch (Exception ex) {;}



Параметризированные запросы


Параметризированный запрос позволяет добавить во фразу where условие, при изменении которого автоматически будет меняться и набор данных. Перед параметрами во фразе where указывается знак двоеточия.

Для создания параметризированных запросов используется компонент ParameterRow.

Двойным щелчком на компоненте ParameterRow в окне структуры следует открыть редактор столбцов и добавить по одному столбцу на каждый параметр. Значение свойства columnName будет использоваться во фразе where SQL-оператора.

Например: select * from tbl1 where ind1<= :column1 Таким образом, компонент ParameterRow связывается с набором данных.

Для связывания компонента с набором данных используется или вкладка Where диалога SQL Builder (предварительно указывается используемый компонент ParameterRow на вкладке Parameters диалога Query), или вкладка SQL, на которой выполняется непосредственный ввод фразы WHERE SQL-оператора SELECT.



Построение SQLJ-приложений


Для создания SQLJ-приложения следует сформировать пустой проект, а затем добавить в него SQLJ-файл (команда меню File | New, выбрать пиктограмму SQLJFile).

SQLJ-файл - это файл с кодом программы на языке Java, в который включены SQLJ-операторы.

SQLJ-операторы могут быть двух видов:

SQLJ-объявления;SQLJ-исполняемые операторы.

SQLJ-оператор может иметь следующее формальное описание:

#sql {sql-оператор }; // Оператор без возвращения результирующего набора

или

#sql выражение={sql-оператор}; // Оператор, возвращающий // результирующий набор

Для поддержки возможности подключения во время компиляции SQLJ-транслятора к базе данных, с целью проверки синтаксиса введенного SQL-кода, следует создать объект соединение (команда меню Project | Properties, вкладка SQLJ), установить флажок Check SQL semantics against database shcema, выбрать New Connection и определить параметры подключения к базе данных, введя ее URL-адрес (localhost на локальном компьютере), имя пользователя и пароль).

При формировании SQLJ-приложения среда JDeveloper автоматически вставляет в начало приложения код, выполняющий подключение необходимых библиотек.

Например:

import sqlj.runtime.*; import sqlj.runtime.ref.*; import java.sql.*; import java.sql.Date; import oracle.sql.*; import oracle.sqlj.runtime.Oracle; public class MySQLJ1 { public MySQLJ1() {} // Конструктор public static void main (String[] args) throws SQLException { } }

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

Например:

MySQLJ1 sqlapp= new MySQLJ1(); DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver()); DefaultContext.setDefaultContext( new DefaultContext( "jdbc:oracle:thin:@db.com:1521:ORCL","scott","tiger",false)); if (DefaultContext.getDefaultContext==null){ // Ошибка подключения к базе данных) }

Для доступа к таблице базы данных следует объявить класс итератора (вставить в код класса до метода main).


Например:

#sql public iterator Tbl1Iter ( int f1, string f2, float f3);

Итераторы могут быть именованные и позиционные. SQLJ-транслятор в зависимости от типа итератора генерирует класс итератора, реализующий интерфейс sqlj.runtime.NamedIterator или sqlj.runtime.PositionedIterator. Оба этих интерфейса наследуют интерфейс ResultSetIterator, предоставляющий следующие методы:

getResultSet - извлечение из итератора результирующего набора;next - переход к следующей строке итератора;isClosed - определение, закрыт ли итератор;close - закрытие итератора.

Для использования итератора следует объявить объект типа итератора, описать выполняемый итератор, а затем, используя метод next, последовательно выбирать строки результирующего набора.

Например:

Tbl1Iter t1=null; // Объект типа итератора #sql t1= { SELECT //Выполняемый итератор Fl1 as a1, Fl2 as a2, Fl3 as a3 FROM Tbl1}; while (t1.next()){ System.out.println( t1.a1()); // Вывод значения столбца // результирующего набора System.out.println( t1.a2()); System.out.println( t1.a3()); } t1.close(); // Закрытие итератора


Работа с базами данных


Язык Java позволяет реализовывать два механизма доступа к базам данных:

с применением средств JDBC;с использованием SQLJ.

При реализации доступа через JDBC могут использоваться:

JDBC-драйверы;мост JDBC-ODBC.

При реализации доступа через SQLJ SQL-операторы встраиваются непосредственно в код на языке Java, а затем обрабатываются SQLJ-предкомпилятором. Обычный SQLJ-предкомпилятор ограничивает синтаксис встраиваемых SQL-операторов стандартом SQL-92. Однако при применении SQLJ-предкомпиляторов, ориентированных на конкретные СУБД, допускается использование конструкций языка SQL, реализуемых в этих СУБД. Так, SQLJ-предкомпилятор для Oracle 9i позволяет не только статическое, но и динамическое встраивание SQL-операторов.



Работа с текстовыми файлами


JBUILDER предоставляет компонент "текстовый файл" - TextDataFile, который используется для импортирования данных из текстового файла и экспортирования их обратно.

Компонент набора данных реализуется классом TableDataSet. Он используется для хранения данных, импортируемых из текстового файла. Если для текстового файла существует одноименный файл с расширением SHEMA, то структуру столбцов описывать нет необходимости. Такой файл автоматически создается компонентом TableDataSet при записи в текстовый файл и содержит описание столбцов и другую информацию по набору данных. Для каждого столбца набора данных создается объект типа Column.

В следующем примере иллюстрируется чтение данных из текстового файла и их отображение в компоненте типа JdbTable:

import java.awt.*; import java.awt.event.*; import javax.swing.*; import com.borland.dx.dataset.*; import com.borland.dbswing.*; public class Frame1 extends JFrame { // contentPane - контейнер для размещения // визуальных компонентов JPanel contentPane; BorderLayout borderLayout1 = new BorderLayout(); // Компонент TextDataFile со страницы DataExpress // для работы с текстовым файлом - источником данных TextDataFile textDataFile1 = new TextDataFile(); // Компонент TableDataSet со страницы DataExpress, // реализующий набор данных TableDataSet tableDataSet1 = new TableDataSet(); Column column1 = new Column(); // Создается для // каждого столбца в наборе данных Column column2 = new Column(); Column column3 = new Column(); TableScrollPane tableScrollPane1 = new TableScrollPane(); // Компонент "таблица для набора данных" JdbTable jdbTable1 = new JdbTable(); public Frame1() { // Конструктор // Определение прослушиваемых событий enableEvents(AWTEvent.WINDOW_EVENT_MASK); try { jbInit(); } catch(Exception e) { e.printStackTrace(); } } private void jbInit() throws Exception { column2.setCaption("Столбец2"); //Отображаемый // заголовок столбца column2.setColumnName("NewColumn2"); //Имя столбца column3.setCaption("Столбец3"); column3.setColumnName("NewColumn3"); column1.setCaption("Столбец1"); column1.setColumnName("NewColumn1");


// Тип поля указывается классами из пакета com.borland.dx // Тип данных в столбце column2.setDataType(com.borland.dx.dataset.Variant.STRING); column2.setServerColumnName("NewColumn2"); column2.setSqlType(0); // Тип данных в столбце column3.setDataType(com.borland.dx.dataset.Variant.SHORT); column3.setServerColumnName("NewColumn3"); column3.setSqlType(0); // Тип данных в столбце column1.setDataType(com.borland.dx.dataset.Variant.SHORT); column1.setServerColumnName("NewColumn1"); column1.setSqlType(0); // Определение связи набора данных с текстовым файлом tableDataSet1.setDataFile(textDataFile1); textDataFile1.setFileName("D:\\J8\\ImportText.txt"); textDataFile1.setSeparator(","); // Разделитель между // полями contentPane = (JPanel) this.getContentPane(); // Текущая // панель для размещения компонентов contentPane.setLayout(borderLayout1); // Определяем // компоновку this.setSize(new Dimension(500, 400)); this.setTitle("Чтение данных из текстового файла"); tableDataSet1.setColumns(new Column[] {column1, column2, column3}); // Устанавливаем связь визуальной таблицы с набором данных jdbTable1.setDataSet(tableData Set1); tableScrollPane1.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); tableScrollPane1.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); contentPane.add(tableScrollPane1, BorderLayout.CENTER); tableScrollPane1.getViewport().add(jdbTable1, null); } protected void processWindowEvent(WindowEvent e) { super.processWindowEvent(e); if (e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); // Завершение приложения } } }

Сохранение данных из открытого набора данных в текстовом файле выполняется методом Save.

Следующий пример иллюстрирует сохранение набора данных, выполняемое в обработчике события actionPerformed.

void button1_actionPerformed(ActionEvent e) { try { tableDataSet1.getDataFile().save(tableDataSet1); System.out.println ("Изменения успешно сохранены в файле"); } catch (Exception ex) { System.out.print("При сохранении файла произошла "); System.err.println(" ошибка: " + ex); } }


Реализация доступа к базам данных в среде JBUILDER


При создании программ на языке Java можно придерживаться "чистого" Java, что ограничивает возможности программиста использованием только классов и интерфейсов пакетов JDK. Однако, различные существующие среды программирования, включая и JBuilder, значительно расширяют возможности программиста, предоставляя дополнительные библиотеки классов. Далее будут рассмотрены классы и интерфейсы, применяемые для реализации доступа к базам данных как из пакетов JDK, так и предоставляемые средой JBuilder.

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

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

На следующей схеме представлен механизм взаимодействия компонентов, реализующих работу с базой данных.


Преимущество доступа с использованием JDBC-драйвера заключается в том, что нет необходимости устанавливать эти драйверы на каждой клиентской машине (в отличие от ODBC-драйверов, которые предварительно должны быть зарегистрированы на каждом клиенте).

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

Connection - интерфейс из пакета java.sql, используемый для соединения с источником данных.Database - используется для указания класса драйвера и местоположения базы данных.TextDataFile - указывает используемый текстовый файл.DataStore - для доступа к базе данных DataStore.

Компоненты, представляющие в приложении структуру извлекаемых данных, называются наборами данных. К таким компонентам относятся:

ResultSet - набор данных для "чистого" Java.TableDataSet - позволяет указать имя таблицы или имя текстового файла, представляющих источник данных, и определить параметры столбцов набора данных (тип, размер, значение по умолчанию, заголовок и т.д.).QueryDataSet - используется для определения набора данных в терминах SQL-оператора.


Приложение может содержать компоненты, предназначенные для отображения информации из набора данных. Страница dbSwing палитры компонентов окна дизайнера содержит визуальные компоненты, специально предназначенные для отображения набора данных (эти классы не входят в JDK). К таким компонентам относятся:

JdbCheckBox - флажок.JdbRadioButton - переключатель (радиокнопка).JdbTextField - текстовое поле.JdbComboBox - раскрывающийся список.JdbSlider - бегунокJdbTextPane - многострочное текстовое полеJdbToggleButton - кнопка переключательJdbLabel - надписьJdbTable - таблицаJdbTree - дерево.JdbList - список значений поля текущей записи.

Для навигации по полям и записям набора данных можно использовать компонент JdbNavToolBar,


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

создать производный класс и переопределить метод обработки сообщений actionPerformed();получить ссылку на кнопку компонента и удалить в JdbNavToolBar ActionListener для кнопки, а затем добавить свой ActionListener.

Для определения текущего набора данных, используемого компонентом, следует вызвать метод getFocusedDataSet().

Дополнительно пакет com.borland.dbswing предоставляет компоненты, которые также могут использоваться для навигации по набору данных, включая:

JdbNavField - компонент, наследуемый от JTextField, и позволяющий выполнять поиск в наборе данных введенного значения, и при нахождении его делать найденную запись текущей. Набор данных, в котором выполняется поиск, определяется свойством dataSet, а столбец - свойством columnName. Если свойство columnName не указано, то используется столбец в таблице JdbTable, имеющий фокус, а если такого столбца нет - то первый столбец набора данных;JdbNavComboBox и JdbNavList - компоненты, отображающие список значений столбца набора данных; при выборе элемента списка данная запись становится текущей.Набор данных, по которому выполняется навигация, определяется свойством dataSet, а столбец - свойством columnName (а при отсутствии значения данного свойства - так же как и для компонента JdbNavField);JdbNavTree - компонент, наследуемый от JTree и позволяющий устанавливать текущей записью запись, выбранную в данном компоненте. Отображаемые значения определяются значениями свойств dataSet и columnName.



Приложение может содержать компоненты, предназначенные для отображения информации из набора данных. Страница dbSwing палитры компонентов окна дизайнера содержит визуальные компоненты, специально предназначенные для отображения набора данных (эти классы не входят в JDK). К таким компонентам относятся:

JdbCheckBox - флажок.JdbRadioButton - переключатель (радиокнопка).JdbTextField - текстовое поле.JdbComboBox - раскрывающийся список.JdbSlider - бегунокJdbTextPane - многострочное текстовое полеJdbToggleButton - кнопка переключательJdbLabel - надписьJdbTable - таблицаJdbTree - дерево.JdbList - список значений поля текущей записи.

Для навигации по полям и записям набора данных можно использовать компонент JdbNavToolBar,


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

создать производный класс и переопределить метод обработки сообщений actionPerformed();получить ссылку на кнопку компонента и удалить в JdbNavToolBar ActionListener для кнопки, а затем добавить свой ActionListener.

Для определения текущего набора данных, используемого компонентом, следует вызвать метод getFocusedDataSet().

Дополнительно пакет com.borland.dbswing предоставляет компоненты, которые также могут использоваться для навигации по набору данных, включая:

JdbNavField - компонент, наследуемый от JTextField, и позволяющий выполнять поиск в наборе данных введенного значения, и при нахождении его делать найденную запись текущей. Набор данных, в котором выполняется поиск, определяется свойством dataSet, а столбец - свойством columnName. Если свойство columnName не указано, то используется столбец в таблице JdbTable, имеющий фокус, а если такого столбца нет - то первый столбец набора данных;JdbNavComboBox и JdbNavList - компоненты, отображающие список значений столбца набора данных; при выборе элемента списка данная запись становится текущей.Набор данных, по которому выполняется навигация, определяется свойством dataSet, а столбец - свойством columnName (а при отсутствии значения данного свойства - так же как и для компонента JdbNavField);JdbNavTree - компонент, наследуемый от JTree и позволяющий устанавливать текущей записью запись, выбранную в данном компоненте. Отображаемые значения определяются значениями свойств dataSet и columnName.


Среда проектирования


Среда проектирования Oracle JDeveloper позволяет создавать приложения различных типов на языке Java, включая создание сервлетов, JSP-страниц, клиентских Java-приложений, аплетов, компонентов JavaBeans, компонентов EJB, построение XML-приложений Oracle, формирование хранимых процедур, генерацию форм данных InfoBus, разработку компонентов с архитектурой Business Component for Java (BC4J). Для реализации доступа к базам данных среда JDeveloper позволяет применять как механизм JDBC, так и создавать специальные SQLJ-приложения.

Разработка любого приложения начинается с создания рабочего пространства (команда меню File | New Workspace)., Затем создания проекта (команда меню File | New Project), при котором в зависимости от выбранного типа проекта мастером построения проекта формируется пустой проект или проект указанного типа.



JSP-директивы


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

JSP-директива указывается между символами <%@ и %>.

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

Директива page имеет ряд атрибутов, включая следующие:

contentType - определяет используемый набор символов и MIME-тип ответа (contentType="text/html; charset=windows-1251");import - определяет список импортируемых классов и пакетов. Директива page с атрибутом import может быть вставлена в JSP-файл несколько раз; session - значение true данного атрибута указывает, что при выполнении JSP-файла осуществляется поддержка сеанса;info - данные о JSP-файле, которые доступны посредством вызова Servlet.getServletInfo().

Так, автоматически формируемая при создании JSP-файла строка

<%@ page contentType="text/html; charset=windows-1251" %>

использует JSP-директиву page для определения типа возвращаемого документа и типа кодировки.



Компонент IXCONTROL


Компонент IxControl позволяет устанавливать связь между элементом управления HTML-страницы и полем набора данных.

На вкладке Design окна редактора следует добавить в окно структуры компонент IxControl со страницы InternetBeans палитры компонентов.

Объект ixControl1 будет помещен в секцию Other окна структуры.

Одновременно с этим в код модуля добавляется следующая строка:

IxControl ixControl1 = new IxControl();

Для связывания элемента управления с полем набора данных установите значения следующих свойств объекта ixControl1:

свойство dataset должно указывать используемый набор данных (например, customer);свойство columnName должно определять имя поля набора данных;свойство pageProducer должно указывать компонент типа IxPageProducer (например, ixPageProducer1);свойство controlName должно определять элемент управления формы и устанавливается равным атрибуту name тега input.

Для каждого поля набора данных следует добавить свой компонент типа IxControl.



Компонент IXPAGEPRODUCER


Для реализации доступа к данным можно использовать компоненты страницы InternetBeans палитры компонентов.

На вкладке Design окна редактора следует добавить в окно структуры компонент IxPageProducer со страницы InternetBeans палитры компонентов.

Объект ixPageProducer1 будет помещен в секцию Other окна структуры (рис. 14.2).


Рис. 14.2.  Компонент IxPageProducer страницы InternetBeans

При этом в код модуля добавляется строка:

IxPageProducer ixPageProducer1 = new IxPageProducer();

Для определения связи с модулем данных установите значения следующих свойств объекта ixPageProducer1:

свойство dataModule должно указывать используемый модуль данных (например, DataModule11);свойство htmlFile должно определять имя HTML-файла, содержащего шаблон.

Обратите внимание, что свойство rootPath автоматически будет установлено равным полному пути к указанному имени HTML-файла.

Одновременно с этим в код модуля будет добавлен вызов метода ixPageProducer1.setHtmlFile("C:\\ File1.html");.



Компонент IXTABLE


Компонент IxTable определяет динамически генерируемый набор данных.

Для определения набора данных установите значения следующих свойств объекта IxTable:

свойство pageProducer должно указывать объект типа IxPageProducer;свойство dataset должно определять набор данных;свойство elementId определяет таблицу HTML-файла и устанавливается равным атрибуту id тега table.



Методы DOPOST и DOGET


Методы doPost и doGet кода сервлета используются для обработки HTTP-запроса и формирования возвращаемой HTML-страницы.

При использовании компонента IxPageProducer в тело метода doGet следует вставить только одну строку:

ixPageProducer1.servletGet(this, request, response);

Метод servletGet класса IxPageProducer выполняет следующие начальные действия по обработке GET HTTP-запроса:

создает для запроса пользователя новый объект типа HttpSession;при необходимости устанавливает тип запроса, соответствующий шаблону;предоставляет страницу с динамическим содержанием, записываемую в поток вывода.

При использовании компонента IxPageProducer в тело метода doPost могут быть вставлены следующие строки:

// Получение объекта модуля данных: DataModule1 dm = (DataModule1) ixPageProducer1.getSessionDataModule(request.getSession()); ixPageProducer1.servletPost(this, request, response); // Отображение данных, переданных из формы doGet(request, response);

Метод doPost в данном примере используется для обработки параметров запроса. Для динамического формирования HTML-документа вызывается метод doGet.

Метод servletPost класса IxPageProducer выполняет начальные действия по обработке POST HTTP-запроса и, если была нажата кнопка submit, то вызывает обработчик события submitPerformed.



Модуль данных


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

Для создания модуля данных выполните следующие действия:

Выполните команду меню File|New и на вкладке General выберите пиктограмму Data Module.При этом в поле Class name будет отображаться имя создаваемого модуля данных (по умолчанию - DataModule1). При включенном флажке Invoke Data Modeler после завершения данного диалога будет предложен диалог Data Modeler для определения SQL-запросов к подключаемой посредством JDBC базе данных.Установите подключаемый источник данных. Для этого в диалоге Data Modeler выполните команду меню Database|Add Connection URL или команду меню Database|Open Connection URL.При создании нового подключения в диалоге New URL установите значение поля Driver, выбрав это значение из предлагаемого списка (например, com.borland.datastore.jdbc.DataStoreDriver), и поля URL, указав путь к источнику данных. Параметры текущего подключения отображаются на панели Definition диалога Data Modeler (рис. 14.1).


Рис. 14.1.  Диалог Data Modeler - страница Columns

После определения подключения и формирования SQL-запроса следует создать модуль данных, выполнив команду меню File|Save, а затем завершить диалог Data Modeler, выполнив команду меню File|Exit.

Диалог Data Modeler позволяет определить соединение с источником данных и автоматически сформировать SQL-оператор, выполняющий запрос данных.

Для формирования запроса на панели Available columns следует выбрать имена столбцов и скопировать их на панель Selected columns. Страница Where диалога Data Modeler позволяет сформировать условие, а страницы Order By и Group By служат для задания порядка сортировки и группирования данных, получаемых в результирующем наборе.

Для того чтобы просмотреть результат сформированного запроса, следует перейти на страницу Test диалога Data Modeler и щелкнуть по кнопке Execute Query.


При создании модуля данных JBuilder автоматически формирует код класса модуля данных, реализующего интерфейс DataModule.

Например:

package myservlet_database1;

import java.awt.*; import java.awt.event.*; import com.borland.dx.dataset.*; import com.borland.dx.sql.dataset.*;

public class DataModule1 implements DataModule { static private DataModule1 myDM; private Database database1 = new Database(); private QueryDataSet customer = new QueryDataSet(); // Переменная customer будет применяться как ссылка // на используемый набор данных public static DataModule1 getDataModule() { // Статической метод getDataModule используется для // создания объекта типа DataModule1 if (myDM == null) { myDM = new DataModule1(); } return myDM; } public DataModule1() { // конструктор try { jbInit(); } catch(Exception e) { e.printStackTrace(); } } private void jbInit() throws Exception { customer.setQuery( new com.borland.dx.sql.dataset.QueryDescriptor( database1, "SELECT CUSTOMER.CUST_NO, CUSTOMER.CUSTOMER, CUSTOMER.PHONE_NO, CUSTOMER.CITY " + "FROM CUSTOMER", null, true, Load.ALL)); // Метод setQuery устанавливает значение свойства query // компонента типа QueryDataSet database1.setConnection( new com.borland.dx.sql.dataset.ConnectionDescriptor "jdbc:borland:dslocal:C:\\JBuilder8\\samples\\JDataStore\\ datastores\\employee.jds", "Sample", "", false, "com.borland.datastore.jdbc.DataStoreDriver")); } // Метод setConnection определяет соединение с источником данных public Database getDatabase1() { return database1; } public QueryDataSet getCustomer() { return customer; } }

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

Добавьте в проект новый файл, разместив его в каталоге с файлами используемого web-приложения. Этот файл будет выполнять роль HTML-шаблона, применяемого сервлетом для динамического формирования HTML-документа.Введите в окне редактора кода в созданный HTML-файл текст шаблона.


Например: <html> <body><h1> Таблица Customer</h1> <table id="tbl1" align="CENTER" border="1" > <tr><th>Поле1</th></tr> <tr><td>-</td></tr></table> <form method="POST"> Pole1:</p> <input type="text" id="P1" name="Col1" size="30"> <input type="submit" name="submit" value="Submit"> </p></form> </body></html>

Для того чтобы определить используемый модуль данных, следует:

Откомпилировать проект.Выполнить команду меню Wizards|Use DataModule.

Опция Create New Instance of Data Module диалога Use DataModule Wizard используется в том случае, если предполагается использовать модуль данных для одной формы.

Опция Share (Static) Instance of DataModule позволяет использовать модуль данных несколькими формами приложения, разделяя один экземпляр модуля.

Опция Caller sets Inctance with setModule() позволяет использовать для одной формы несколько модулей данных, выполняя соответствующую настройку в окне дизайнера.

В результате добавления модуля данных в файле Servlet1 появится объявление переменной dataModule11 типа DataModule1 (например: DataModule1 dataModule11;), а в метод jbInit будет добавлен вызов метода getDataModule, используемого для создания объекта модуля данных (например, dataModule11 = myservlet_database1.DataModule1.getDataModule();).


Обработка событий


Компонент IxControl может выполнять обработку событий. Так, если данный компонент связан с командной кнопкой Submit, то для обработки события следует перейти на вкладку Events и создать обработчик события submitPerformed.

Например:

ixControl2.addSubmitListener(new com.borland.internetbeans.SubmitListener() { public void submitPerformed(SubmitEvent e) { ixControl2_submitPerformed(e); } }); : void ixControl2_submitPerformed(SubmitEvent e) {

}

Вставьте в тело метода обработчика события submitPerformed следующий код:

// Запрос текущего модуля данных: DataModule1 dm = (DataModule1) ixPageProducer1.getSessionDataModule(e.getSession()); // передача и сохранение данных, введенных пользователем, // в источнике данных, определенным модулем данных: dm.getCustomer().post(); dm.getCustomer().saveChanges(); // Метод getCustomer - это автоматически сформированный метод модуля //данных DataModule1.java, возвращающий объект типа QueryDataSet

Методы post и saveChanges класса QueryDataSet используются для передачи и сохранения изменений в базе данных.



Применение ASP-страниц


Серверные ASP-страницы реализуются как текстовые HTML-файлы с расширением asp, которые содержат сценарии на языке JScript или VBScript. ASP-сценарии записываются между тегами <% и %>.

Доступ к базам данных выполняется из серверных ASP-сценариев посредством вызова методов интерфейса ADO (ActiveX Data Object).

При создании ASP-сценариев можно использовать следующие объекты:

Application - определяет общедоступные переменные.Request - позволяет получить доступ к информации, передаваемой в HTTP-запросе.Response - формирует данные, передаваемые WEB-браузеру.Server - содержит методы, позволяющие создавать объекты и выполнять ASP-файлы.Session - объект, описывающий сеанс конкретного пользователя.

Для работы с базами данных в ASP-файлах удобно использовать объектный интерфейс ADO, который создан на базе OLE DB. Объектная модель ADO представляется набором последовательно используемых объектов, включая следующие:

Connection - объект, используемый для соединения с источником данных;Command - объект, используемый для выполнения команд (SQL-операторов) и хранимых процедур;Recordset - объект "результирующий набор", который создается в результате выполнения команды;Field - объект, позволяющий реализовать доступ к столбцу результирующего набора; Parameters - объект, позволяющий работать с входными и выходными параметрами команд и хранимых процедур;Errors - объект, используемый для описания возникающих ошибок.

При применении ADO для соединения с базой данных в ASP-сценарии следует использовать объект ADODB.Connection, а для работы с результирующим набором - объект ADODB.Recordset.

Следующий пример иллюстрирует ASP-сценарий, выполняющий отображение записей из таблицы базы данных:

<% var con,rs; con=Server.CreateObject("ADODB.Connection"); con.Open ("MyDB", "User1", ""); srtSQL="Select * from t1"; rs= Server.CreateObject("ADODB.Recordset"); rs.Open (strSQL, con); %> <TABLE> while (! rs.EOF) { %> <TR> <TD> <%= rs.Fields("field1")%> </TD> <TD><%= rs.Fields("field2")%> </TD> </TR> <% rs.MoveNext(); } %> </TABLE> <% rs.Close; con.Close; %>


Метод CreateObject ASP- объекта Server создает объект, указываемый параметром. Метод Open ADO-объекта Connection устанавливает соединение с базой данных. Метод MoveNext объекта Recordset выполняет переход к следующей записи.

Объект Recordset позволяет выполнять модификацию записей, используя методы:

AddNew - добавление новой записи;Delete - удаление записи;Update - обновление записи.

Например:

<% rs.AddNew; rs("field1")="123"; rs.Update; %>

Для реализации поиска указываемого значения в столбце результирующего набора следует использовать методы объекта Recordset:

Find - поиск на основе заданного условия; Seek - поиск по ключевому полю.

Для наложения на открытый результирующий набор некоторого фильтра вызывается метод Filter объекта Recordset.

Для создания результирующего набора можно использовать метод Execute объекта Command.

Например:

<%@LANGUAGE=VBSCRIPT%> <! ASP-файл > <html> <body> <% dim con, rs, sql1 sql1="SELECT * FROM Tbl1;" set con = Server.CreateObject("ADODB.Connection") con.Open "DSN = MyDB" ' Устанавливаем соединение set rs = con.execute(sql1) ' Открываем результирующий набор if rs.BOF and rs.EOF then ' Результирующий набор пустой Response.Write("Нет строк") else rs.MoveFirst Do While Not rs.EOF ' Доступ по имени столбца Response.Write(rs("F1") & " " & rs("F2") & "<br>") rs.MoveNext Loop end if rs.close set rs = nothing %> </body> </html>

Доступ к значениям столбцов может быть реализован через объект типа Recordset как по имени столбца, так и по его номеру. Отображаемые значения в ASP-сценариях указываются после оператора =.

Например:

<TABLE border = 1> <% Do while (Not rs.eof) %> <tr> <% For Index=0 to (rs.fields.count-1) %> <td ><% = rs(Index)%></td> ' Доступ по номеру ' столбца <% Next %> ' Цикл по столбцам </tr> <% rs.MoveNext Loop ' Цикл по строкам результирующего набора %> </TABLE>


Применение бинов JDBCBEAN и HTTPJDBCBEAN для реализации доступа к базе данных


В состав JBuilder Enterprise входит сервер IAS (версия сервера зависит от версии среды JBuilder), по умолчанию инсталлируемый в каталог \Inprise\AppServer.

Данный WEB-сервер содержит в каталоге Inprise\AppServer\examples\beans\inpriseexamples\beans файлы JDBCBean.java и HttpJDBCBean.java, которые можно использовать для упрощения процесса публикации данных на JSP-страницах. Файл JDBCBean.java представляет собой бин, содержащий свойства, определяющие подключение к базе данных, и выполняемый SQL-оператор. Файл HttpJDBCBean.java применяется для определения параметров, используемых бином JDBCBean.

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

Открыть в среде JBuilder файл JDBCBean.java.Добавить в проект новый компонент JavaBean, выполнив команду меню File|New, а затем на вкладке New указав пиктограмму JavaBean.В поле ClassName ввести имя создаваемого бина (например, JDBCBean).В качестве наследуемого класса указать java.lang.Object.Скопировать в созданный файл вместо собственного кода код из ранее открытого файла JDBCBean.java, а затем закрыть скопированный файл.Отредактировать первую строку, содержащую директиву package, указав имя текущего пакета проекта.

Перейти на вкладку Bean окна содержания и в редакторе бинов перейти на вкладку Properties.

Эта вкладка содержит следующие свойства бина, позволяющие выполнить SQL-запрос:

classname - имя используемого JDBC-драйвера;

url - расположение источника данных;

username - имя пользователя, подключаемого к базе данных;

password - пароль пользователя;

query - выполняемый SQL-оператор.

Для каждого из перечисленных свойств существует get-метод и set-метод.

Свойства cols и rows (число столбцов и строк в результирующем наборе) используются как простые переменные только внутри бина. Для доступа к их значениям служат свойства columnCount и rowCount, имеющие get-методы.

Свойство result используется для формирования отображаемого результата.

Создать второй компонент JavaBean и аналогичным образом скопировать в него файл HttpJDBCBean.java.


Класс HttpJDBCBean наследует классу JDBCBean.

Бин HttpJDBCBean использует свой метод processRequest для обработки запроса. Этот метод должен быть непосредственно вызван из JSP-файла для определения значений всех свойств бина JDBCBean.

Создать JSP-файл, использующий бины JDBCBean и HttpJDBCBean.

В файле JDBCBean.java выполнение SQL-запроса осуществляется методом go на основе значений, установленных для свойств данного бина. Так, этот метод реализует следующие основные действия:

Выполняет загрузку класса драйвера: Class.forName(classname);Устанавливает соединение с источником данных, указываемым свойством url: Connection con=DriverManager.getConnection(url,username,password);Создает объект типа Statement: Statement stmt = con.createStatement();Выполняет SQL-запрос, определяемый свойством query: ResultSet rs = stmt.executeQuery(query);Записывает в свойства cols, rows и result метаданные текущего запроса: ResultSetMetaData rsmd = rs.getMetaData(); cols = rsmd.getColumnCount(); rows = 0;

В файле HttpJDBCBean.java. метод processRequest выполняет присвоение значений всем свойствам бина JDBCBean, получая эти значения из параметров запроса.

Метод processRequest определяет значения параметров запроса через объект request типа HttpServletRequest:

if ((_p = request.getParameter("classname")) != null) { classname = _p; }

Так, если параметр classname определен, то значение одноименного свойства устанавливается равным его значению.

Если все необходимые параметры заданы, то вызывается метод go бина JDBCBean: this.go();



Класс HttpJDBCBean наследует классу JDBCBean.

Бин HttpJDBCBean использует свой метод processRequest для обработки запроса. Этот метод должен быть непосредственно вызван из JSP-файла для определения значений всех свойств бина JDBCBean.

Создать JSP-файл, использующий бины JDBCBean и HttpJDBCBean.

В файле JDBCBean.java выполнение SQL-запроса осуществляется методом go на основе значений, установленных для свойств данного бина. Так, этот метод реализует следующие основные действия:

Выполняет загрузку класса драйвера: Class.forName(classname);Устанавливает соединение с источником данных, указываемым свойством url: Connection con=DriverManager.getConnection(url,username,password);Создает объект типа Statement: Statement stmt = con.createStatement();Выполняет SQL-запрос, определяемый свойством query: ResultSet rs = stmt.executeQuery(query);Записывает в свойства cols, rows и result метаданные текущего запроса: ResultSetMetaData rsmd = rs.getMetaData(); cols = rsmd.getColumnCount(); rows = 0;

В файле HttpJDBCBean.java. метод processRequest выполняет присвоение значений всем свойствам бина JDBCBean, получая эти значения из параметров запроса.

Метод processRequest определяет значения параметров запроса через объект request типа HttpServletRequest:

if ((_p = request.getParameter("classname")) != null) { classname = _p; }

Так, если параметр classname определен, то значение одноименного свойства устанавливается равным его значению.

Если все необходимые параметры заданы, то вызывается метод go бина JDBCBean: this.go();


Публикация данных с использованием классов TQUERYTABLEPRODUCER и TDATASETTABLEPRODUCER


Для публикации данных из базы данных на HTML-страницы можно использовать следующие классы:

TQueryTableProducer - для создания HTML-страницы, содержащей таблицу с данными, являющимися результатом выполнения SQL-запроса. Параметры выполняемого SQL-запроса передаются в HTTP-запросе.TDataSetTableProducer - для создания HTML-страницы, содержащей таблицу с данными из сформированного набора данных.

Компонент типа TDataSetTableProducer при формировании HTML-страницы, как правило, обрабатывает следующие события:

OnCreateContent - при создании содержимого HTML-страницы;OnFormatCell - при формировании содержимого отдельной ячейки таблицы;OnGetTableCaption - при получении заголовка создаваемой таблицы.

Для того чтобы создать серверное приложение, отображающее на HTML-странице поля таблицы базы данных, следует:

Создать новое приложение, запускаемое на web-сервере (команда меню File|New|Other, пиктограмма Web Server Application), и выбрать тип создаваемого приложения.Для реализации доступа к источнику данных расположить в web-модуле компонент набора данных Table (или компонент Query) со страницы BDE палитры компонентов. Установить значение свойств компонента Table1 (DatabaseName и TableName).Двойным щелчком мыши в web-модуле на компоненте Table1 вызвать редактор полей и определить поля, которые будут включены в результирующий набор (для компонента Query - сформировать значение свойства SQL).Разместить в web-модуле компонент DataSetTableProducer со страницы Internet палитры компонентов.Для определения связи с источником данных установить значение свойства DataSet компонента DataSetTableProducer1 равным Table1, выбрав значение из предлагаемого списка.Свойство Caption компонента DataSetTableProducer1 можно использовать для определения заголовка, отображаемого перед таблицей. Для формирования начальных и завершающих тегов HTML-страницы используются значения свойств Header и Footer. Содержание самой таблицы определяется свойством Columns.Следует определить начальные теги формируемой HTML-страницы, введя в свойство Header следующие строки: <HTML> <HEAD> <TITLE> Запрос данных из таблицы </TITLE> </HEAD> <BODY>


Тег BODY открывает область, в которой размещается отображаемое содержимое HTML-страницы.Следует определить завершающие теги формируемой HTML-страницы, введя в свойство Footer следующие строки: </BODY> </HTML>

Далее можно вызывать редактор свойства Column. Для этого или в инспекторе объектов выполните двойной щелчок мышью на значении свойства Column компонента DataSetTableProducer1, или в web-модуле выполните двойной щелчок мышью на компоненте DataSetTableProducer1.

Для добавления всех столбцов в диалоге Editing.DataSetTableProducer1.Columns выполните команду контекстного меню Add All Fields. Каждый добавленный столбец может быть доступен как DataSetTableProducer1.Columns[номер_столбца_начиная_с_0].

Для определения элемента действия для web-модуля выполните двойной щелчок мышью на компоненте WebModule1 и в диалоге Editing WebModule1.Actions добавьте новый элемент действия (кнопка Add New).Для добавленного объекта действия WebActionItem1 следует создать обработчик события OnAction и ввести в него код, определяющий значение свойства Content объекта ответа. Например: Response.Content:= DataSetTableProducer1.Content; Свойство Content объекта DataSetTableProducer1 содержит текст сформированной таблицы с соответствующими HTML-тегами.

Сформированная HTML-страница будет отображать таблицу с набором данных, который определен компонентами Table и DataSetTableProducer.



Тег BODY открывает область, в которой размещается отображаемое содержимое HTML-страницы.Следует определить завершающие теги формируемой HTML-страницы, введя в свойство Footer следующие строки: </BODY> </HTML>

Далее можно вызывать редактор свойства Column. Для этого или в инспекторе объектов выполните двойной щелчок мышью на значении свойства Column компонента DataSetTableProducer1, или в web-модуле выполните двойной щелчок мышью на компоненте DataSetTableProducer1.

Для добавления всех столбцов в диалоге Editing.DataSetTableProducer1.Columns выполните команду контекстного меню Add All Fields. Каждый добавленный столбец может быть доступен как DataSetTableProducer1.Columns[номер_столбца_начиная_с_0].

Для определения элемента действия для web-модуля выполните двойной щелчок мышью на компоненте WebModule1 и в диалоге Editing WebModule1.Actions добавьте новый элемент действия (кнопка Add New).Для добавленного объекта действия WebActionItem1 следует создать обработчик события OnAction и ввести в него код, определяющий значение свойства Content объекта ответа. Например: Response.Content:= DataSetTableProducer1.Content; Свойство Content объекта DataSetTableProducer1 содержит текст сформированной таблицы с соответствующими HTML-тегами.

Сформированная HTML-страница будет отображать таблицу с набором данных, который определен компонентами Table и DataSetTableProducer.


Реализация серверных приложений в среде DELPHI


Среда Delphi позволяет создавать приложения, выполняемые на web-сервере, с целью формирования HTML-страницы, публикующей данные их базы данных. Это могут быть приложения следующих типов:

ISAPI/NSAPICGIWIN-CGI.

web-браузер по указанному URL-адресу запрашивает данные от WEB-сервера. При выполнении запроса устанавливается TCP/IP соединение с сервером, и данные передаются в формате HTML. После завершения передачи данных TCP/IP соединение разрывается.

При этом передаваемая HTML-страница может быть как HTML-файлом, так и результатом выполнения на web-сервере некоторого приложения.



Создание серверного приложения


web-браузер посылает в качестве запроса URL-адрес серверного приложения, а Web-сервер запускает это приложение и передает ему параметры запроса. Сформированная в результате выполнения серверного приложения HTML-страница возвращается WEB-браузеру web-сервером.

Для того, чтобы создать серверное приложение (например CGI), следует:

Создать новый проект (команда меню File|New|Other, вкладка New, пиктограмма Web Server Application).В диалоге New Web Server Application указать тип создаваемого проекта (например CGI Stand-alone executable).

В результате будет создан проект, содержащий главный файл приложения и модуль Unit1, имеющий следующий код:

{Модуль Unit1.pas} unit Unit1; interface uses SysUtils, Classes, HTTPApp; type TWebModule1 = class(TWebModule) private { Private declarations } public { Public declarations } end; var WebModule1: TWebModule1; implementation {$R *.DFM} end.

Для того чтобы добавить код, формирующий HTML-страницу, следует:

Для объекта TWebModule1 выполнить двойной щелчок мышью на свойстве Action.В диалоге Editing WebModule1.Actions создать новый элемент (кнопка Add New).Создать для объекта WebActionItem1 обработчик события OnAction. При этом автоматически будет сформирован следующий код: procedure TWebModule1.WebModule1WebActionItem1Action( Sender: TObject; Request: TWebRequest; // Объект запроса - // позволяет получать данные о выполняемом запросе Response: TWebResponse; // Объект ответа - // используется для формирования ответа // (HTML-страницы var Handled: Boolean); begin

end;

Сформировать в созданном обработчике события код, формирующий текст HTML-страницы. Этот текст должен быть записан в свойство Content объекта Response.

Свойству Content можно присваивать значение, оформленное как массив строк. Это может быть реализовано следующимспособом:

посредством конкатенаций нескольких строковых значений;

Например:

Response.Content:='<I><База данных MyDB </I> '+ <P> <B> Таблица TBL1 </B></P>;

с применением переменной типа TStringList, реализующей работу с массивом строк.
В этом случае следует объявить переменную данного типа, создать ее, а затем, используя метод add, добавить в нее все строки формируемой HTML-страницы.

Например:

var aPage : TStringList; i: integer; begin aPage:= TStringList.Create; aPage.Add(' База данных MyDB '+ '<BR>'); aPage.Add(Таблица TBL1 ' + '<BR>'); aPage.Add('f1 = ' + DataSource1.DataSet.FieldByName('F1').AsString+ '<BR>'); // Последовательно записываем в созданный массив строк // все значения из открытого набора данных Response.Content := aPage.Text; aPage.Free;

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

Например: http://localhost/webpub/ProjectDB.exe.

В том случае, если необходимо реализовать не только публикацию данных, но и предоставить средства их редактирования, отображение данных следует выполнять внутри формы в компонентах типа "поле ввода". Кнопка типа submit должна будет вызывать серверное приложение, которое будет использовать получаемые из запроса данные для обновления таблицы базы данных.


Создание сервлета, используемого для публикации данных


Процесс разработки сервлета, выполняющего публикацию данных на HTML-страницы с применением компонентов страницы InternetBeans, состоит из следующих этапов:

Создание нового проекта и размещение в нем Web-приложения.Добавление в Web-приложение нового сервлета (используя команду меню File|New и выбирая на вкладке Web пиктограмму Servlet). Для возможности обработки GET и POST HTTP-запросов в мастере Servlet Wizard на панели Implement methods следует установить флажки для методов doGet и doPost.Создание модуля данных и определение подключаемого источника данных.Создание HTML-файла, отображающего форму для публикации данных.Добавление в сервлет компонентов IxPageProducer и IxControl, реализующих связь формируемого HTML-документа с модулем данных и элемента управления - с объектом сервлета.Формирование в сервлете кода методов doPost и doGet, формирующих возвращаемую web-сервером HTML-страницу.



Страница INTERNETBEANS


Страница InternetBeans среды JBuilder содержит компоненты, позволяющие заменять на этапе выполнения сервлета "пустые" данные на данные, предоставляемые модулем данных.

Компонент IxPageProducer предназначен для связывания HTML-файла с используемым модулем данных.

Компонент IxControl позволяет связать элемент управления из HTML-шаблона с объектом сервлета. Этот объект может использоваться для определения связи с полем таблицы модуля данных. Для каждого текстового элемента управления из HTML-файла должен быть создан отдельный объект типа IxControl.

Компонент IxTable позволяет отображать в HTML-документах динамически генерируемые таблицы набора данных.



Вызов методов компонентов JAVABEANS из JSP-файла


Для того чтобы создать новый JSP-файл, использующий рассмотренный выше бин, выполните следующие действия:

Создайте новый JSP-файл, выполнив команду меню File|New, а затем на вкладке Web выбрав пиктограмму JavaServer Page.Снимите флажки Generate submit form и Generate sample bean и завершите данный диалог.

В окне редактора после тега <body> введите следующий код:

<jsp:useBean id="jdbc_bean" scope="application" class="myjspjdbc1.HttpJDBCBean" />

JSP-действие jsp:useBean создает объект типа HttpJDBCBean с идентификатором jdbc_bean

Атрибут class определяет класс создаваемого объекта, квалифицированный именем пакета.

Введите после описания действия jsp:useBean следующий код, выполняющий подключение всех файлов из пакета myjspjdbc1: <%@ page import = "myjspjdbc1.*" session="false" %>Выполните вызов метода processRequest класса HttpJDBCBean: <% jdbc_bean.processRequest(request, response); %>

В качестве параметров метода указываются предопределенные переменные request и response, используемые для доступа к параметрам запроса и к формированию ответа.Введите код шаблона, отображающий заголовок JSP-страницы. Например: <p><center><font size=2><b>JSP</b></font></center></p> <p >Доступ к базе данных через JDBC</p><hr></p>

Атрибут size тега font определяет размер шрифта, а тег hr используется для отображения горизонтальной линии.Используйте тег FORM для создания формы, в которую будут помещаться результаты SQL-запроса. Например, введите следующий код: <FORM action=Jsp1.jsp method=post>

</FORM>

При обработке формы будет использоваться этот же файл Jsp1.jsp.Сформируйте код, выполняющий определение параметров запроса. Для этого, например, используя тег <TABLE>, разместите в форме таблицу, первый столбец которой будет содержать имя запрашиваемого параметра, а второй столбец - текстовое поле, предназначаемое для ввода значения.

Для того чтобы создать новый JSP-файл, использующий рассмотренный выше бин, выполните следующие действия:

Создайте новый JSP-файл, выполнив команду меню File|New, а затем на вкладке Web выбрав пиктограмму JavaServer Page.Снимите флажки Generate submit form и Generate sample bean и завершите данный диалог.

В окне редактора после тега <body> введите следующий код:

<jsp:useBean id="jdbc_bean" scope="application" class="myjspjdbc1.HttpJDBCBean" />

JSP-действие jsp:useBean создает объект типа HttpJDBCBean с идентификатором jdbc_bean

Атрибут class определяет класс создаваемого объекта, квалифицированный именем пакета.

Введите после описания действия jsp:useBean следующий код, выполняющий подключение всех файлов из пакета myjspjdbc1: <%@ page import = "myjspjdbc1.*" session="false" %>Выполните вызов метода processRequest класса HttpJDBCBean: <% jdbc_bean.processRequest(request, response); %>

В качестве параметров метода указываются предопределенные переменные request и response, используемые для доступа к параметрам запроса и к формированию ответа.Введите код шаблона, отображающий заголовок JSP-страницы. Например: <p><center><font size=2><b>JSP</b></font></center></p> <p >Доступ к базе данных через JDBC</p><hr></p>

Атрибут size тега font определяет размер шрифта, а тег hr используется для отображения горизонтальной линии.Используйте тег FORM для создания формы, в которую будут помещаться результаты SQL-запроса. Например, введите следующий код: <FORM action=Jsp1.jsp method=post>

</FORM>

При обработке формы будет использоваться этот же файл Jsp1.jsp.Сформируйте код, выполняющий определение параметров запроса. Для этого, например, используя тег <TABLE>, разместите в форме таблицу, первый столбец которой будет содержать имя запрашиваемого параметра, а второй столбец - текстовое поле, предназначаемое для ввода значения.


Каждое текстовое поле создаваемой таблицы будет соответствовать свойству бина JDBCBean.

Например:

<table> <tr> <td> JDBC-драйвер:</td> <td><input type="text" name="classname" value=" <%= jdbc_bean.getClassname() %>" size="40"></td> <td></td> </tr>

<!- JSP-выражение <%= jdbc_bean.getClassname() %>" формирует начальное значение текстового поля, вызывая метод getClassname для определения значения свойства classname объекта с идентификатором jdbc_bean. ->

<tr> <td></td> <td><i>sun.jdbc.odbc.JdbcOdbcDriver</i></td> </tr>

<! - Класс sun.jdbc.odbc.JdbcOdbcDriver реализует JDBC-ODBC драйвер для доступа к источнику данных. Загрузка драйвера выполняется на ПК, на котором запущен Web-сервер.-->

<tr> <td>JDBC URL-адрес источника данных:</td> <td><input type="text" name="url" value=" <%= jdbc_bean.getUrl() %>" size="40"></td> </tr> <tr> <td></td>> <td><i>jdbc:odbc:MyDB</i></td> </tr> <tr> <td>Имя пользователя:</td> <td><input type="text" name="username" value=" <%= jdbc_bean.getUsername() %>" size="25"></td> </tr> <tr> <td>Пароль:</td> <td><input type="text" name="password" value=" <%= jdbc_bean.getPassword() %>" size="25"></td> </tr> <tr> <td>Выполняемый SQL-оператор:</td> <td><textarea cols=35 rows=3 name="query" wrap="soft"> <%= jdbc_bean.getQuery() %></textarea></td>

<!-- Тег textarea используется для создания текстовой области. Атрибуты cols и rows определяют размер создаваемой области. -->

</tr></table>

Тег input с атрибутом type="text" определяет текстовое поле.


Атрибут name тега input определяет имя параметра, передаваемого JSP-файлу при обработки формы. Формируемый POST-запрос будет содержать список параметров, указываемых атрибутами name, со значениями, указываемыми атрибутами value.

Добавьте в форму кнопку Submit, при щелчке пользователя по которой на web-сервер будет передан POST-запрос с параметрами, указанными в форме.

Например:

<input type="submit" name="submit" value="Выполнить SQL-оператор">

В результате выполненных действий WEB-сервер получит POST-запрос, который будет обработан указанным в теге FORM JSP-файлом. При этом как результат выполнения SQL-оператора будет создан результирующий набор. Далее JSP-файл, используя методы бина, может получить доступ к сформированной информации.

Для того чтобы отобразить на формируемой HTML-странице данные результирующего набора, выполните следующие действия:



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

Например:

<% if (jdbc_bean.getColumnCount() != 0){ %><% -- JSP-скриплет --%> <table cellspacing="0" cellpadding="5" border="0" BGCOLOR=#EDECE5> <% for (int i=0; i<jdbc_bean.getColumnCount(); i++){ String str0 = jdbc_bean.getColumnLabels(i); %>

<!-- Метод getColumnLabels бина JDBCBean возвращает заголовок столбца с указанным индексом --> <th><%= str0 %></th> <% } %> <% } %>

Запишите JSP-скриплет, формирующий строки результирующего набора в формате HTML.

Например:

<% for (int row=1; row<jdbc_bean.getRowCount(); row++) {%> <tr></tr> <% for (int col=0; col<jdbc_bean.getColumnCount(); col++) { String str1 = jdbc_bean.getCell(col, row); %> <td ><%= str1 %></td> <% } %> <% } %> </table>

Приведенный выше JSP-скриплет использует метод getRowCount бина JDBCBean для выполнения цикла по количеству строк, а метод getColumnCount - для выполнения цикла по количеству столбцов.


Метод getCell бина JDBCBean возвращает массив строк, содержащий значения всех полей одной записи результирующего набора.

Перед выполнением JSP-файла следует откомпилировать классы используемых бинов.

В JBuilder для компиляции данных классов в проект следует добавить библиотеку Servlet. Для этого предварительно скопируйте файл библиотеки Servlet.library в каталог проекта. Для подключения библиотеки выполните команду меню Project|Project Properties и на вкладке Paths выберите страницу Required Libraries. Далее для добавления новой библиотеки щелкните мышью на кнопке Add и в секции Project выберите библиотеку Servlet.

Добавленная библиотека будет отображена в списке библиотек на вкладке Required Libraries.

Для выполнения JSP-файла на Web-сервере Tomcat выделите в окне проекта имя JSP-файла и выполните для него команду контекстного меню Web Run.

После введения значений в отображаемый на вкладке Web View окна содержания результат выполнения JSP-файла, web-сервер формирует HTML-страницу (рис. 14.3), содержащую результат выполнения JSP-скриптов.


Рис. 14.3.  Результат выполнения web-сервером JSP-файла

Далее приводится код используемых классов (JDBCBean и HttpJDBCBean):

// Код файла JDBCBean.java: package myjspjdbc1; import java.io.*; import java.util.*; import java.sql.*; public class JDBCBean { String classname ; // JDBC-драйвер String url ; String username ; String password ; String query ; // SQL-оператор. int rows, cols; // Число строк и столбцов результирующего набора Vector result; public void setClassname(String classname) { this.classname = classname; } public String getClassname() { return classname; } public void setUrl(String url) { this.url = url; } public String getUrl(){ return url; } public void setUsername(String username) { this.username = username; } public String getUsername() { return username; } public void setPassword(String password) { this.password = password; } public String getPassword() { return password; } public void setQuery(String query) { this.query = query; } public String getQuery() { return query; }