Выбрать главу

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

Управление транзакциями через SQL

Помимо управления транзакцией через OLE DB-интерфейсы сессии, IBProvider осуществляет специальную поддержку SQL-запросов вида: "SET TRANSACTION...", "COMMIT" и "ROLLBACK". В этом случае будет использоваться транзакция, принадлежащая сессии. Управление транзакциями через SQL-запросы позволяет указывать специфические параметры контекста транзакции, которые не стандартизированы в OLE DB и которые возможно использовать благодаря особенностям InterBase API.

Примеры работы с транзакциями

Ниже приведены примеры, демонстрирующие способы запуска и завершения транзакций, поддерживаемые OLE DB-провайдером.

ADODB:

явное управление транзакцией:

Dim en As New ADODB.Connection

сn.Provider = "LCPI.IBProvider.1"

Call en.Open(

"data source=localhost:d:\database\employee.gdb",

"gamer","vermut")

'стандартный способ

сn IsolationLevel = adXactRepeatableRead

сn.BeginTrans

'...

'можно указать использование commit retaining

'cn.Attributes = adXactAbortRetaining + adXactCommitRetaining

cn.CoimutTrans

'управление транзакцией через SQL (можно использовать специфику

InterBase)

Dim cmd As New ADODB.Command

cmd.ActiveConnection = en

cmd.CommandText = "set transaction"

cmd.Execute

'...

cmd CommandText = "rollback"

cmd Execute

автоматический запуск транзакций - разрешение и запрещение:

Dim en As New ADODB.Connection

en Provider = "LCPI.IBProvider.1"

'auco_commit=true включение автоматических транзакций

'для всех сессий

Call cn.Open(

"data source=localhost:d:\database\employee.gdb;auto_commit=true",

"gamer", "vermut")

Dim cmd As New ADODB.Command

Dim rs As ADODB Recordset

cmd ActiveConnection = cn

cmd.CommandText = "select * from rdb$database"

Sec rs = cmd.Execute 'транзакция запускается неявно

'...

'и Судет завершена при закрытии результирующего множества

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