Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

SQLAlchemy

Глава 20 из книги "Архитектура приложений с открытым исходным кодом", том 2.

Оригинал: SQLAlchemy
Автор: Michael Bayer
Перевод: А. Панин

20.3. Использование DBAPI

В основании SQLAlchemy находится подсистема для взаимодействия с базами данных посредством DBAPI. Сам по себе DBAPI представлен не отдельной библиотекой, а исключительно спецификацией. Поэтому реализации спецификации DBAPI доступны для определенных целевых баз данных, таких, как MySQL или PostgreSQL или в качестве альтернативы для определенных адаптеров для баз данных, не совместимых с DBAPI, таких, как ODBC и JDBC.

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

Система диалектов

Интерфейс, реализуемый в рамках DBAPI является чрезвычайно простым. Его ключевыми компонентами являются сам модуль DBAPI, объект соединения и объект курсора - курсор базы данных представляет контекст определенного запроса и ассоциированные с ним результаты. Простое взаимодействие с этими объектами, направленное на установление соединения с базой данных и извлечение данных из нее, может быть реализовано следующим образом:
connection = dbapi.connect(user="user", pw="pw", host="host")
cursor = connection.cursor()
cursor.execute("select * from user_table where name=?", ("jack",))
print "Результирующие столбцы:", [desc[0] for desc in cursor.description]
for row in cursor.fetchall():
    print "Строка:", row
cursor.close()
connection.close()

В рамках SQLAlchemy реализован фасадный класс для классического взаимодействия с DBAPI. Точкой входа этого фасадного класса является вызов create_engine, с помощью которого устанавливается соединение и собирается конфигурационная информация. В качестве результата выполнения вызова возвращается экземпляр класса Engine. Этот объект представляет только способ осуществления запроса через DBAPI, причем последний никогда непосредственно не раскрывается.

Для простого выполнения запросов объект Engine предоставляет интерфейс, известный под названием "интерфейс явного исполнения запросов" ("implicit execution interface"). Работа по созданию и закрытию соединения с базой данных и курсора посредством DBAPI выполняется незаметно для разработчика:
engine = create_engine("postgresql://user:pw@host/dbname")
result = engine.execute("select * from table")
print result.fetchall()
В версии SQLAlchemy 0.2 был впервые представлен объект Connection, позволяющий явно выполнять этапы процесса соединения с базой данных посредством DBAPI:
conn = engine.connect()
result = conn.execute("select * from table")
print result.fetchall()
conn.close()

Возвращаемый методом execute класса Engine или Connection результат называется ResultProxy и предоставляет интерфейс, аналогичный интерфейсу курсора в DBAPI, но с большим набором функций. Объекты Engine, Connection и ResultProxy связаны с модулем DBAPI и являются экземплярами определенного соединения DBAPI и определенного курсора DBAPI соответственно.

На заднем плане объект Engine ссылается на объект, называемый Dialect. Dialect является абстрактным классом, для которого существует множество реализаций, причем каждая из этих реализаций предназначена для работы с определенной комбинацией DBAPI и базы данных. Объект Connection, создаваемый на стороне объекта Engine, будет ссылаться на этот объект Dialect при принятии всех решений, которые могут варьироваться в зависимости от используемых DBAPI и базы данных.

После создания объект Connection будет создавать и поддерживать рабочее соединение DBAPI из репозитория, известного, как Pool, который также ассоциирован с объектом Engine. Репозиторий Pool ответственен за создание новых соединений DBAPI и обычно за сохранение их в расположенном в памяти пуле для периодического использования.

В процессе исполнения запроса объектом Connection создается дополнительный объект с именем ExecutionContext. Этот объект существует с момента исполнения запроса в течение периода существования объекта ResultProxy. Он также может быть доступен как специфический подкласс для некоторых комбинаций DBAPI и баз данных.

Рисунок 20.2 иллюстрирует все эти объекты и их взаимоотношения с другими объектами, а также с компонентами DBAPI.

API объектов Engine, Connection, ResultProxy
Рисунок 20.2: API объектов Engine, Connection, ResultProxy

Обработка различий интерфейсов DBAPI

Перед рассмотрением задачи обработки различий интерфейсов DBAPI, давайте для начала рассмотрим суть существующей проблемы. Спецификация DBAPI, на данный момент второй версии, написана в форме наборов объявлений API, которые позволяют реализовывать значительно отличающиеся по поведению интерфейсы, а также оставляют большое количество недокументированных областей. В результате существующие реализации DBAPI демонстрируют значительные отличия в некоторых областях, включая возможность или невозможность передачи строк языка Python в кодировке Unicode; способ получения "последнего добавленного идентификатора", являющегося автоматически генерируемым первичным ключом, после выполнения запроса INSERT; а также способ указания и интерпретации граничных значений. Эти интерфейсы также ведут себя индивидуально в зависимости от используемых типов данных, в ситуациях, когда производится обработка бинарных данных, точных числовых данных, дат, логических данных, а также строк в кодировке Unicode.

SQLAlchemy решает эту проблему, допуская различия в классах Dialect и ExecutionContext путем использования множества уровней подклассов. Рисунок 20.3 иллюстрирует отношение между объектами Dialect и ExecutionContext в случае использования диалекта psycopg2. Класс PGDialect реализует специфичные для базы данных PostgreSQL возможности, такие, как поддержка типа данных ARRAY и каталогов схем; класс PGDialect_psycopg2 реализует возможности, специфичные для реализации DBAPI psycopg2, включающие обработчики строк в кодировке Unicode и поддержку управления курсором на стороне сервера базы данных.

Простая иерархия классов Dialect/ExecutionContext
Рисунок 20.3: Простая иерархия классов Dialect/ExecutionContext

Вариант описанного выше шаблона проектирования оправдывает себя при работе с реализацией DBAPI, поддерживающей множество баз данных. Примерами таких реализаций являются pyodbc, которая может взаимодействовать с неограниченным количеством баз данных посредством ODBC и zxjdbc, предназначенная только для использования совместно с языком Jython и работающая с JDBC. Описанное выше отношение классов реализуется путем использования смешанного класса из пакета sqlalchemy.connectors, который реализует особенности работы интерфейса DBAPI, свойственные для множества баз данных. Рисунок 20.4 иллюстрирует стандартные функции класса sqlalchemy.connectors.pyodbc, разделяемые между pyodbc-специфичными диалектами для баз данных MySQL и Microsoft SQL Server.

Стандартные функции DBAPI, разделяемые между иерархиями диалектов
Рисунок 20.4: Стандартные функции DBAPI, разделяемые между иерархиями диалектов

Объекты Dialect и ExecutionContext предоставляют возможность для описания каждого взаимодействия с базой данных и DBAPI, включая то, как должны форматироваться аргументы функции соединения, а также как должны обрабатываться специальные данные в процессе исполнения запроса. Объект Dialect также является фабрикой для компиляции синтаксических конструкций языка SQL, которая осуществляет корректное оформление SQL-запроса для целевой базы данных, а также преобразования типов объектов данных в соответствии с тем, как объекты данных языка Python должны упаковываться и распаковываться при использовании целевых интерфейса DBAPI и базы данных.


Продолжение статьи: Описание схемы