учебники, программирование, основы, введение в,

 

Реализация доступа к базам данных в среде VISUAL STUDIO.NET на языке С++ с применением ATL

Классы, используемые для доступа к БД
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 - используется для доступа к результирующему набору без указания параметров.

Подключение к БД
Для подключения к базе данных следует создать объект типа 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()
Команды и таблицы
Классы команды и таблицы позволяют реализовывать доступ к результирующим наборам. Эти классы наследуются от класса CAccessorRowset, как это показано на следующей схеме:
В данной схеме TAccessor может быть любым допустимым типом аксессора, а TRowset - любым допустимым типом результирующего набора; TMultiple определяет тип результирующего набора (один или несколько).
В среде Visual Studio.NET мастер ATL OLE DB Consumer позволяет определить, какой класс будет использован для создания результирующего набора. Класс CTable следует применять в том случае, если результирующий набор только один и не требуется использование параметров.
Применение класса CCommand открывает более широкие возможности:

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

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

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

  1. Откройте результирующий набор, выполнив функцию Open.
  2. При использовании аксессора типа 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;
}

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

Технология 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)

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

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

Метод 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( );

Добавление строк
Метод 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 - для получения полей длины и статуса.

Отображение результирующего набора в элементах управления 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.

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика