Библиотеки доступа к данным (ADAM)

Существует два основных подхода к хранению данных приложения в объектах: создание классов бизнес-объектов, специфичных для каждого приложения, и хранение данных в стандартных (не зависящих от приложения) объектных структурах. Первый подход считается более удобным для прикладного программиста и шире распространен (особенно в Enterprise-приложениях и, вообще, в приложениях с базами данных). Однако сложности, возникающие в первом подходе при изменении структуры данных приложения, лишь отчасти решаются за счет средств синхронизации базы данных с бизнес-объектами (их getter/setter-методами). Эти сложности связаны с тем, что, например, при добавлении атрибута к объекту необходимо производить правки как минимум в трех местах – не только в базе данных и в местах непосредственного использования атрибута (например, в пользовательском интерфейсе), но и в классе бизнес-объекта. Средства синхронизации могут автоматически вносить правки в базу данных по бизнес-объекту (или наоборот), однако на запуск синхронизации требуются немалые трудозатраты. Более того, достаточно часто необходимо вносить правки также и в XML-настройки объектно-реляционного отображения (ORM), которые обычно предполагаются ORM-библиотеками. Все это в 1.5–2.5 раза увеличивает трудозатраты на изменение структуры данных по сравнению с теоретически возможными значениями.

В тех довольно типичных случаях, когда структура данных меняется достаточно часто в процессе разработки приложения (а число мест использования одних и того же объекта/атрибута минимально), становится намного предпочтительнее второй подход, тоже объектный, но не предполагающий классов бизнес-объектов. В этом подходе различные объекты с точки зрения языка программирования имеют один тип, и он определяется интерфейсом доступа к данным объекта по их именам. А именно, интерфейс содержит getter/setter-методы для полей различных типов: getString(String), getInt(String) и т.д., а также для полей, являющихся ссылками на другой объект или множество других объектов. Недостатком подхода можно назвать отсутствие проверки ошибок в именах полей на этапе компиляции (впрочем, этот недостаток не проявляется, если приблизить подход к первому и хранить названия часто используемых полей в виде строковых констант в классе приложения). Код использование объектов во втором подходе выглядит менее наглядно, однако он может быть даже короче аналогичного кода в первом подходе, поскольку нет необходимости в приведении типов к конкретным классам бизнес-объектов. Но главное – трудозатраты на изменение структуры близки к минимально возможным значениям: правки вносятся только в месте хранения данных (БД или файл) и в месте использования данных, и нигде больше. Причем число мест явного использования конкретных атрибутов во втором подходе зачастую можно свести к одному (в вычислительных системах) или даже к нулю (если атрибут стандартным образом отображается в интерфейсе с помощью форм типа описанных в разделе 6.2.3, а приложение чисто информационное, т.е. не содержит логики типа вычислений, зависимых от этого конкретного атрибута). В последнем случае получается уникальный факт, что для получения результата достаточно изменить структуру данных всего в одном месте (причем не в коде, а в базе данных или файле).

Именно на базе второго подхода построена основная часть проекта ADAM (Abstract Data Access Models), в частности, – фреймворк ADM, содержащий интерфейс объекта Data, его нехранимые (transient) реализации, а также самую простую хранимую (persistent) реализацию – через XML-файлы (имеются ввиду файлы более компактного и удобного формата, чем создает XML-сериализация Java). Фреймворк ADM может быть использован даже вне задачи хранения данных в постоянной памяти (persistence) – например, с целью абстракции высокоуровневых компонентов пользовательского интерфейса (форм, деревьев, таблиц и других компонентов, оперирующих понятиями объектов и их атрибутов) от структуры данных конкретных приложений. Поэтому там имеются несколько пакетов, обеспечивающих модели данных для пользовательских интерфейсов (независящих от интерфейсной библиотеки и, вообще, от графической или web-ориентированной природы интерфейса), в частности – механизм обработки разнообразных событий.

Расширением концепций фреймворка ADM для хранения данных (persistence) является фреймворк ADMStore, базовый интерфейс которого (PersistentData extends Data) содержит идентификатор и основные методы жизненного цикла хранимого объекта (удаление, а также два механизма изменения – через вызов setter-методов привязанного к объекту «изменяльщика» getChanger() и через сохранение изменений, ранее сделанных setter-методами самого объекта). Существенным является то, что природа идентификатора хранимого объекта в интерфейсах фреймворка не фиксируется – для разных реализаций это может быть или длинное целое число, или пара {тип данных + целое число, уникальное в рамках типа}, или другие варианты. Во фреймворке ADMStore имеется поддержка обеих концепций хранимости (как stateless, так и stateful), причем имеется как простая stateful-реализация хранимого объекта (в которую все атрибуты объекта загружаются сразу), так и stateful-реализация, подгружающая (swizzle) поля лишь при необходимости (подгружающая каждое поле по отдельности или подгружающая сразу все поля кроме сложных полей типа ссылок/списков/больших объектов или …). Весь функционал хранимых объектов делегируется «интерфейсам таблиц» типа DataStorage (а также интерфейсу DataLoader для подгрузки объекта по частям). Реализации DataStorage создают объекты PersistentData в результате поиска по критериям (или прямого обращения по идентификатору или навигации по ссылкам); в свою очередь, объекты DataStorage создаются по именам с помощью реализации корневого объекта-фабрики DataStorageFactory, инкапсулирующую соединение с базой данных (или информацию о директории с файлами данных). Все созданные объекты, естественно, кэшируются (кэш иногда имеет смысл отключать только для stateless объектов, чтобы повысить скорость выборок за счет увеличения требований к памяти в клиентской программе). Во фреймворке имеется несколько уровней «продвинутости» (комбинирующихся интерфейсов DataTable, DataStorage, PersistentDataFinder, ExtendedDataFinder – в последнем случае возможна пересылка в базу данных произвольных критериев поиска на SQL) – чтобы для простых приложений (например, хранящих данные в файлах и/или не имеющих функционала ненавигационного поиска данных) не требовалось реализовывать (и загружать в память) весь спектр возможностей библиотеки доступа к базам данных.

Следует также заметить, что рассмотренный в начале данного раздела «первый подход к хранению данных в объектах» также поддерживается в ADMStore в виде интерфейсов бизнес-объектов и соответвующих таблиц/фабрик. Правда, единственная имеющаяся реализация этих интерфейсов делегирует всю работу реализациям основных интерфейсов фреймворка, основанных на концепции Data, т.е. на втором подходе (и такое делегирование, по-видимому, ведет к некоторой потере производительности). Впрочем, в ADMStore и в других библиотеках проекта ADAM достаточно классов, чтобы поклонники первого подхода могли реализовать его на базе указанных интерфейсов с минимальными трудозатратами. Кроме того, в ADMStore имеются необходимые для первого подхода средства синхронизации – генераторы классов бизнес-объектов из структуры реляционных таблиц (из DDL-скриптов), учитыывающие принятые в конкретном приложении правила соответствия имен и типов.

Аналогично структуре таблиц и фабрик фреймворка ADMStore, построены также интерфейсы библиотеки необъектного доступа к табличным данных AStore, которая предоставляет данные в виде массивов значений атрибутов таблиц или в виде итераторов по строкам/ячейкам. Если речь идет о базах данных, то AStore можно рассматривать как надстройку над JDBC, избавляющую от написания простого SQL-кода (которого в приложениях бывает от 70 до 100%) и упрощающую рутинные операции, в т.ч. за счет выделения объекта таблицы. Однако в некоторых случаях наиболее важным преимуществом AStore (как и ADMStore) является независимость приложения от типа источника данных – возможность сменить реализацию интерфейсов (например, сменить реализацию доступа к реляционной БД через стандартный интерфейс уровня обращения на реализацию «прямого доступа», работающую с конкретной объектной, реляционной или многомерной базой данных через нестандартный интерфейс – как правило, более быстрый).

Кроме реализации, работающей с базами данных на уровне JDBC (через библиотеку ARDB, см. ниже), ADMStore содержит реализацию своих интерфейсов (правда, лишь интерфейсов не самого продвинутого уровня, см. выше), хранящую данные в текстовых файлах простой табличной структуры. XML-формат в данном случае был бы менее производителен и менее читабелен для рядового пользователя; в случае, если эти факторы несущественны, можно использовать ARDB в сочетании с [существующей реализацией интерфейсов JDBC через текстовые файлы]). Конечно, для большого объема хранимых данных файловая реализация неэффективна (он не содержит никаких оптимизаций поиска; по крайней мере, пока), однако для многих однопользовательских одноуровневых приложений она намного более удобна для пользователя и для деплоймента (в т.ч. и потому, что не требует регистрации «базы данных» в операционной системе).

Уникальной особенностью проекта ADAM является взаимосвязь между фреймворками ADMStore (объектный доступ) и AStore (необъектный доступ). Их интерфейсы полностью независимы (даже метаданные о таблицах хранятся в разных классах – с похожей структурой, но с разной терминологией); однако в состав фреймворка ADMStore входит классы-адаптеры – реализации его интерфейсов через интерфейсы фреймворка AStore. Это позволяет добиться еще большей гибкости, а также уменьшить объем работы (и не допустить дублирований) при реализации конкретных библиотек доступа к данным. Впрочем, для объектного доступа к реляционным базам данных в библиотеке ADMStoreGUI (формально не входящей в ADAM) имеется также устаревшая с точки зрения структуры (но более экономичная) реализация, которая напрямую работает с базой данных через JDBC, без создания дополнительных объектов библиотеки AStore (которые, в свою очередь, создают объекты библиотеки ARDB). Слово GUI в названии библиотеки ADMStoreGUI означает, что ее классы умеют создавать хранимые объекты, которые реализуют интерфейсы из GUI-фреймворков (экземпляры таких объектов автоматически представляются в графических компонентах проектов AGUI и ADMGUI с правильными именами и иконками). Все реализации библиотек доступа к данным из ADMStoreGUI также обеспечивают механизм подгрузки содержимого stateful объекта данных (за исключением имени) лишь при первом обращении к любому полю (кроме имени), что существенно экономит ресурсы приложения (прежде всего, память).

Наконец, для тех, кого не интересуют фреймворки доступа к данным (интерфейсная часть рассмотренных выше библиотек), может быть полезна входящая в проект ADAM библиотека ARDB, не содержащая ни одного интерфейса и поэтому более простая для понимания. Она упрощает работу с JDBC, однако (в отличие от фреймворка AStore с собственными интерфейсами) она не абстрагирует клиента от JDBC полностью – в частности, результаты выборок через нее получаются клиентом в виде экземпляров JDBC-интерфейса java.sql.ResultSet.

Joomla Templates by Joomlashack