Мир InterBase. Архитектура, администрирование и разработка приложений баз данных в InterBase/FireBird/Yaffil - А Ковязин
Шрифт:
Интервал:
Закладка:
rs Close
'альтернативой auto_commit=true является установка
'(после успешного подключения к база данных)
'свойства сессии Session AutoCommit=true.
'В ADODB это одно и то же,
'поскольку на одно подключение - одна сессия
'через это же свойство можно "выключить" глобальное
'разрешение на автоматический
'запуск транзакции, что дальше и демонстрируется
cn Properties("Session AutoCommit") = False
cmd.CommandText = "select * from rdb$database"
Set rs = cmd Execute
'выдаст олибку "Automatic transaction is disabled"
При программировании на C++ принципы взаимодействия с сессией точно такие же. Но в C++ сессия будет отдельным объектом.
В нижеследующем примере на C++ демонстрируется трюк, который часто используется для моделирования принудительного завершения транзакций. Дело в том, что InterBase реализует многоверсионную архитектуру данных (подробнее о многоверсионности данных см. главу "Транзакции Параметры транзакций" (ч 1)) Одной из особенностей такой архитектуры является то, что любые транзакции желательно завершать подтверждением (commit), а не откатом (lollback).
Однако, как правило, транзакцию, в рамках которой выполняется операция чтения данных, не подтверждают, а откатывают. Особенно в случае достаточно длинного участка кода, который генерирует исключения в случае непредвиденных ошибок загрузки информации Поэтому и нужно принудительно завершать транзакции так. как это показано в нижеследующем примере.
try
{
t_db_data_source cn;
_THROto_OLEDB_FAILED(en,attacn("provider=LCPI.IBProvxCter.1,"
"data source=localhost:d:\database\employee.gdb;"
"user id=gamer;"
"password=vermut"));
t_db_session session;
// метод create перегружен для разных типов аргумента, поэтому
// можно передавать
// как C++ объект (t_db_data_source), так и IUnknown источника
// данных
_THROW_OLEDB_FAILED(session,create(en)) ,
// запуск транзакции
_THROW_OLEDB_FAILED(session,start_transaction() ) ;
// создаем объект для принудительного завершения транзакции
t_auto_mem_fun_l<HRESULT,bool,t_db_session>
_auto_commit_(session, /*commit_retaining=*/false, &t_db_session::commit);
//... теперь, что бы ни произошло, транзакция
// будет "закоммичена"
throw runtime_error("This is my test error");
}
catcn(const exception& exc)
{
cout<< "error:"<<exc.what()<<endl;
}
По умолчанию библиотека не генерирует исключений, поэтому даже если сбой произошел из-за проблем с самой базой данных, то принудительное завершение транзакции, выполняемое в деструкторе _auto_commit_, не возбудит исключения.
Распределенные транзакции
Еще одним способом инициирования транзакции является подключение сессии к координатору распределенных транзакций. В общих чертах, координатор представляет собой сессию, транслирующую вызовы собственных интерфейсов управления транзакциией в идентичные групповые вызовы интерфейсов списка дочерних сессий. Как правило, от пользователя не требуется никакого участия для подключения к координатору. За это отвечает окружение, у которого пользовательский код запрашивает подключение к базе данных.
На практике распределенные транзакции обычно используются в СОМ+ (MTS) и Microsoft Distributed Query. В первом случае существует следующая особенность. Когда компоненты просят у своего окружения предоставить им подключение к базе данных, то на каждый такой запрос создается отдельная пара источник данных - сессия. Связано это с описанным выше принципом работы пула подключений. Если компонентов, обрабатывающих пользовательский запрос, очень много и они работают с одной и той же базой данных с идентичными параметрами подключения, то имеет смысл один раз получить подключение и потом предоставлять его компонентам. Иначе в распределенной транзакции будет использовано множество сессий, каждая из которых соответствует процессу или потоку на сервере базы данных, что может привести к резкому снижению производительности сервера СУБД.
Использование нескольких сессий в ADODB
Несмотря на то что модель объектов ADODB не предоставляет прямой возможности одновременного использования нескольких сессий с одним источником данных, это ограничение можно обойти. Решение основывается на применение внутреннего интерфейса ADOConnectionConstruction компоненты ADODB.Connection.
void clone_adodb_connection(IDispatch* pCurrentConnection,
IDispatchPtr& spNewConnection)//throw
{
//объявляем типы смарт-указателей для интерфейсов
//конструирования ADODB-подключения
DECLARE_IPTR_TYPE(ADOConnectionConstruction);
DECLARE_IPTR_TYPE(ADOConnectionConstructionl5);
//1 получаем источник данных, привязанный к pCurrentConnection
ADOConnectionConstructionPtr
spConstruct(pCurrentConnection);
ADOConnectionConstruetionl5Ptr
spConstructlS(pCurrentConnection);
if(!spConstruct && !spConstruct15)
t_ole_error::throw_error("Объект - не ADODB.Connection",
E_INVALIDARG);
IUnknownPtr spDataSource;
//берем указатель на OLE DB-источник данных
if((bool)spConstruct15)
spConstruct15->get_DSO(&spDataSource.ref_ptr());
if(!spDataSource && (bool)spConstruct)
spConstruct->get_DSO(&spDataSource.ref_ptr());
if(!spDataSource)
t_ole_error::throw_error(
"ADODB.Connection не инициализирован",E_FAIL);
//2 создаем новую сессию для spDataSource ------------------
IUnknownPtr spNewSession;
IDBCreateSessionPtr spDBCreateSession(spDataSource);
assert((bool) spDBCreateSession);
if(FAILED(spDBCreateSession->CreateSession
(NULL,IID_IUnknown,kspNewSession.ref_ptr())))
{
t_ole_error::throw_error(
"Ошибка создания новой сессии",E_FAIL);
}
assert((bool)spNewSession);
//3 создаем новый экземпляр ADODB.Connection ----------------
IUnknownPtr spUnkNewConnection;
HRESULT hr=SafeCreateInstance("ADODB.Connection",
NULL,CLSCTX_INPROC,IID_IUnknown,
(void**)&spUnkNewConnection.ref_ptr());
if(FAILED(hr))
t_ole_error::throw_error(
"Ошибка создания "ADODB.Connection"",hr);
spConstruet = spUnkNewConnection;
spConstruct15=spUnkNewConnection;
if(!spConstructlS && !spConstruct)
throw t_ole_error(
"Ошибка подключения к "ADODB.Connection"",E_FAIL);
spDataSource->AddRef(); //ADO не вызывает для них AddRef spNewSession->AddRef();
//при конструировании ADODB.Connection
//пытается установить свои свойства,
//в результате чего, если IBProvider уже подключен
//к базе данных, может произойти ошибка
//тем не менее подключение уже сконструировано
//и вполне работоспособно.
if((bool)spConstructlS) //IID_Connect-on15
{
spConstruct15->WrapDSOandSession(spDataSource,spNewSession);
hr = spUnkNewConnection ->
Querylnterface(IID_ConnectionlS,spNewConnection);
}
else //IID_Connection
{
spConstruct->WrapDSOandSession(spDataSource,spNewSession);
hr = spUnkNewConnection ->
Querylnterface(IID_Connection,spNewConnection);
}
if (FAILED(hr))
t_ole_error::throw_error(
"Ошибка получения IDispatch из ADODB.Connection",hr);
assert((bool)spNewConnection);
//всё — spNewConnection подключен к тому же
//источнику данных, но обладает
//собственной сессией.
}//cione_adodb_connection
Чтение метаданных
Помимо управления транзакциями, сессия предоставляет еще одну полезную возможность - получение метаданных для базы данных (о метаданных см. главу "Структура базы данных InterBase" (ч. 4)). Поскольку в некоторых системах, например в Microsoft Distributed Query, операция получения метаданных выполняется очень часто, то IBProvider хранит информацию о них в оперативной памяти (т. е. кеширует). Кэширование метаданных можно настраивать для обеспечения оптимального быстродействия. Определить режим кеширования можно через свойство инициализации источника данных "schema_cache" и свойство сессии - "Session Schema Cache". Этим свойствам можно присваивать следующие значения:
* Кэширование запрещено. Данные будут всегда перечитываться.
* Глобальное кеширование на уровне Data Source. Это режим по умолчанию.
* Кеширование на уровне Session.
Если при запросе метаданных сессия содержит явно запущенную транзакцию, то провайдер не будет использовать дополнительную внутреннюю транзакцию для получения данных, а воспользуется уже существующей. Если явно запущенной транзакции нет, то IBProvider автоматически запустит внутреннюю транзакцию с уровнем изоляции, указанной в свойстве сессии "Autocommit Isolation Levels". Для запрещения автоматического запуска провайдером внутренних транзакций для извлечения метаданные необходимо определить в строке инициализации источника данных "inner_trans=false" или установить свойство сессии "Session InnerTrans=false".
Получение и вывод списка таблиц базы данных:
ADODB
Dim cn As New ADODB.Connection
Call cn.Open("file name=d:databaseemployee.ibp")
Dim rs As ADODB.Recordset
Set rs = cn.OpenSchema(adSchemaTables)
Cells.Clear
Dim col As Long, row As Long
row = 1
'печать названия колоник
For col = 0 To rs.Fields.Count - 1
Cells(row, col + 1) = rs(col).Name
Next col
'печать содержимого
While Not rs.EOF
row = row + 1
For col = 0 To rs.Fields.Count - 1
Cellsfrow, col + 1) = rs(col).Value
Next col
rs.MoveNext
Wend
Здесь следует обратить внимание на одну особенность. Спецификация OLE DB для некоторых полей таблиц метаданных определяет типы, несовместимые с VARIANT, например UI8. Поэтому при попытке получения значения из этих полей через ADODB может возникнуть ошибка;
C++
try
{
t_db_data_source сn;
_THROW_OLEDB_FAILED(cn,attach("file
name=d:\database\employee.ibp"));
t_db_session session;
_THROW_OLEDB_FAILED(session,create(сn));
//библиотека напрямую не поддерживает
//работу с интерфейсом получения
//наборов информационной схемы,
//поэтому напишем необходимый код "в лоб".
IDBSchemaRowsetPtr spSR(session.session_obj());
if(!spSR)
t_ole_error::throw_error(
"query interface [IDBSchemaRowset]",spSR.m_hr);