Путеводитель по db4o для Java-разработчика: Введение и общий обзор возможностей (исходники)

Источник: developerworks
Тед Ньюворд, директор, Neward & Associates

Бытует мнение, что времена соперничества между различными подходами к хранению данных остались в прошлом, и у реляционной модели не осталось конкурентов. Однако, тот, кто считает, что подобное положение дел принесло мир и спокойствие в души программистов, наверное, давно не пробовал использовать реляционные таблицы в качестве источника данных для объектов Java. Эта статья открывает серию из нескольких статей, в которых популярный автор и лектор Тед Ньюворд подробно рассказывает о db4o - объектно-ориентированной альтернативе современным реляционным базам данных.

Казалось, что соперничество между различными моделями хранения данных закончилось еще к тому моменту, как я стал программистом. Oracle и другие производители сумели убедительно доказать преимущества как реляционного подхода в целом, так и SQL - стандартизированного языка запросов. Мне даже не приходилось использовать ни одну из моделей, которые предшествовали реляционной, например, IMS или файловую в качестве долговременного хранилища данных. Все говорило том, что клиент-серверная архитектура - это всерьез и надолго.

А затем в один прекрасный день я впервые открыл для себя С++. Он оказал необратимое влияние на мое восприятие окружающего мира, как, впрочем, и на многих, кому посчастливилось познакомиться с этим языком в те времена. Главным результатом стало то, что я перенял объектную модель программирования взамен традиционной, в центре которой были функции и данные. Множество разработчиков тех лет вдруг перестали обсуждать элегантные структуры данных и "скрытие информации". В нашу речь прочно вошли новые термины, такие как полиморфизм , инкапсуляция и наследование .

 
Об этой серии
В последние десять лет хранение и поиск информации прочно ассоциировались исключительно с реляционными СУБД. Однако недавно появилась тенденция к изменению текущего положения дел. В частности, Java-разработчики все больше жалуются на плохую совместимость объектной и реляционной моделей данных (так называемый "object-oriented mismatсh") и с нетерпением ждут решения этой проблемы. Все это, а также появление применимых на практике альтернатив привело к возрождению интереса к объектно-ориентированному хранению и поиску данных. Серия "Путеводитель по db4o для Java-разработчика" представляет db4o - объектную базу данных с открытым кодом. db4o дополняет существующие объектно-ориентированные языки, системы и принципы проектирования. Скачайте db4o со страницы проекта - она вам понадобится для запуска примеров.
 

Развитие баз данных происходило по схожему сценарию: однажды стало казаться, что мир стоит на пороге новой перспективной технологии - объектных СУБД . Хранение данных в объектном виде, да еще и вкупе с использованием объектно-ориентированного языка вроде С++ (или Java - его набиравшего популярность родственника) выглядело как предел мечтаний программиста.

Однако в реальности все произошло несколько иначе. Пик популярности ООСУБД пришелся на конец 1990-х годов, а затем они отошли в тень. Все, что так недавно было модным и волнующим, стало выглядеть запутанным и зачастую проприетарным. Так закончился второй раунд борьбы технологий хранения данных, и вновь победу одержала реляционная модель (даже несмотря на то что многие производители РСУБД поддерживают работу с объектами в том или ином виде).

Единственное, что нарушает стройную картину доминирования РСУБД - это то, что некоторые из горячих сторонников ООСУБД все еще живы, о чем свидетельствует появление db4o.

Объекты и таблицы

Несмотря на то что термин объектно-реляционное несоответствие (object-relational impedance mismatch) звучит как тема академической лекции, он имеет и сугубо практический смысл, заключающийся в различиях между объектными и реляционными системами в том, что касается взаимодействия между сущностями. На первый взгляд может показаться, что объектная и реляционная модели должны быть хорошо совместимы, но при ближайшем рассмотрении выявляются фундаментальные различия.

Во-первых, объекты не обязаны иметь явные идентификаторы. Как правило, за идентификацию объектов отвечает неявный или скрытый указатель this , который фактически представляет собой адрес в памяти. В отличие от объектов строки любой таблицы явно идентифицируются при помощи первичных ключей, построенных на некотором множестве атрибутов. Во-вторых, инкапсуляция в реляционных базах данных осуществляется за счет скрытия деталей реализации обработки запросов и других операций над данными, в то время как в объектных системах инкапсуляция работает на уровне объектов (с учетом того, что класс может наследовать поведение от родителей). Но самое главное заключается в том, что реляционная модель является замкнутой системой, в которой результат одной операции - набор кортежей - может служить в качестве входных данных для другой. В частности, благодаря этому возможно использование вложенных операторов SELECT. Это невозможно при использовании объектной модели. Также в качестве результата не могут возвращаться частично созданные объекты. Объекты в ООСУБД едины и неделимы, в отличие от РСУБД, которые способны возвращать данные, описываемые подмножеством колонок таблицы или набора таблиц.

Таким образом, между объектами в том виде, в каком они реализуются в языках типа Java, C++ или C# и таблицами (отношениями), реализованными в современных РСУБД, таких как SQL Server, Oracle или DB2, находится пропасть, а наведение мостов, естественно, ложится на плечи программистов.

Проблемы отображения

Раньше разработчики пытались связывать объектную и реляционную модели, вручную описывая соответствие (mapping) объектов и таблиц. Одним из ярких примеров подобных действий является выполнение операторов SQL через JDBC и сохранение результатов в полях объектов. При этом естественным образом возникает вопрос, есть ли более простой способ? В поисках такого способа многие из разработчиков начинают использовать программные средства, автоматизирующие объектно-реляционные отображения (ORM), например Hibernate.

Однако использование Hibernate (а также JPA, JDO, Castor JDO, Toplink и других ORM) не решает всех проблем с отображением между моделями, а скорее скрывает эти проблемы в конфигурационных файлах. Более того, не покидает ощущение искусственности и громоздкости в подобных отображениях. Например, при попытке сопоставить элегантную многоуровневую объектную модель, построенную на принципах наследования, таблице (или набору таблиц) придется искать непростые компромиссы. В частности, выбор между производительностью обработки запросов и соблюдением правил нормализации может в какой-то момент привести к конфликту между разработчиками и администраторами базы данных.

Серьезная проблема заключается в том, что очень сложно спроектировать масштабную модель данных, описывающую ту или иную область, например, как в книгах Мартина Фаулера (Martin Fowler) или Эрика Эванса (Eric Evans), если заранее знать, что придется либо ограничивать модель в соответствии с возможностями базы данных, либо жертвовать некоторыми функциями базы данных, связанными с выполнением операций над моделью, либо и тем, и другим.

А теперь представьте себе ситуацию, при которой не требуется жертвовать ни тем, ни другим.

Введение в db4o: ООСУБД возвращаются

Библиотека db4o - это недавно выпущенный представитель семейства объектных СУБД, предлагающий давно известную идеологию "чистого объектного хранилища" новому поколению разработчиков (кстати, бытует мнение, что ретро становится модным в наше время). Использование db4o проще всего проиллюстрировать на примере простого класса для представления человека (листинг 1).

Замечание: если вы не успели сделать этого раньше, то скачайте db4o, которая понадобится вам для понимания материала и компиляции примеров, приводимых в статьях данной серии.

Листинг 1. Класс Person

        package com.tedneward.model;

public class Person
{
    public Person()
    { }
    public Person(String firstName, String lastName, int age)
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
    
    public String getFirstName() { return firstName; }
    public void setFirstName(String value) { firstName = value; }
    
    public String getLastName() { return lastName; }
    public void setLastName(String value) { lastName = value; }
    
    public int getAge() { return age; }
    public void setAge(int value) { age = value; }

    public String toString()
    {
        return 
            "[Person: " +
            "firstName = " + firstName + " " +
            "lastName = " + lastName + " " +
            "age = " + age + 
            "]";
    }
    
    public boolean equals(Object rhs)
    {
        if (rhs == this)
            return true;
        
        if (!(rhs instanceof Person))
            return false;
        
        Person other = (Person)rhs;
        return (this.firstName.equals(other.firstName) &&
                this.lastName.equals(other.lastName) &&
                this.age == other.age);
    }
    
    private String firstName;
    private String lastName;
    private int age;
}

Класс Person весьма примитивен и не требует дополнительных пояснений. Однако, как легко предположить, в дальнейшем он будет развиваться, обрастая новыми интересными свойствами и возможностями, например для представления информации о детях, супруге и т.д. Все это добавится в следующих статьях; на данный момент нам будет достаточно упрощенной версии.

Если бы наше приложение использовало Hibernate, то для сохранения экземпляров класса Person в базе данных потребовалось бы еще несколько шагов:

  1. Создать реляционную схему, описав необходимые таблицы и типы колонок.
  2. Создать файлы отображения, в которых устанавливается соответствие между таблицами и колонками в базе данных и полями объектов в модели.
  3. Внутри приложения пришлось бы открывать соединение с базой данных через Hibernate. В терминологии Hibernate соединения называются сессиями . Затем для сохранения и выборки объектов можно было бы использовать программный интерфейс Hibernate.

В db4o сохранение объекта осуществляется на удивление просто (листинг 2).

Листинг 2. Аналог INSERT в db4o

        import com.db4o.*;

import com.tedneward.model.*;

public class Hellodb4o
{
    public static void main(String[] args)
        throws Exception
    {
        ObjectContainer db = null;
        try
        {
            db = Db4o.openFile("persons.data");

            Person brian = new Person("Brian", "Goetz", 39);
            
            db.set(brian);
            db.commit();
        }
        finally
        {
            if (db != null)
                db.close();
        }
    }
}

Вот, собственно, и все. Не требуются ни реляционная схема, ни файлы отображения; все, что нужно - это запустить клиентское приложение, а после завершения его работы проверить содержимое каталога на предмет наличия "базы данных", в данном случае - файла с названием persons.data .

Извлечение экземпляра Person из базы данных выглядит немного похоже на то, как работают некоторые системы ORM. Схожесть заключается в том, что самый простой способ получить объект - это задать подобный ему в условиях поиска (этот подход получил название query-by-example или "найти подобные"). Другими словами, необходимо создать прототип объекта того же типа, в полях которого указать параметры поиска, а затем передать его db4o в качестве запроса. В результате будут выбраны все объекты, удовлетворяющие заданным критериям. Пример показан в листинге 3.

Листинг 3. Аналог SELECT в db4o (версия 1)

        import com.db4o.*;

import com.tedneward.model.*;

public class Hellodb4o
{
    public static void main(String[] args)
        throws Exception
    {
        ObjectContainer db = null;
        try
        {
            db = Db4o.openFile("persons.data");

            Person brian = new Person("Brian", "Goetz", 39);
            Person jason = new Person("Jason", "Hunter", 35);
            Person brians= new Person("Brian", "Sletten", 38);
            Person david = new Person("David", "Geary", 55);
            Person glenn = new Person("Glenn", "Vanderberg", 40);
            Person neal = new Person("Neal", "Ford", 39);
            
            db.set(brian);
            db.set(jason);
            db.set(brians);
            db.set(david);
            db.set(glenn);
            db.set(neal);

            db.commit();
            
            // Find all the Brians
            ObjectSet brians = db.get(new Person("Brian", null, 0));
            while (brians.hasNext())
                System.out.println(brians.next());
        }
        finally
        {
            if (db != null)
                db.close();
        }
    }
}

Запустите пример, и вы увидите, что результатом запроса будет набор из двух объектов.

Но, но, но...!

Теперь, прежде чем вы обвините меня в вопиющей предвзятости, рассмотрим некоторые из аргументов критиков db4o.

Но db4o не может сравниться с Oracle, SQL Server и DB2!
Безусловно, это так. По своему масштабу db4o ближе к MySQL или HSQL, чьих возможностей хватает для нужд множества проектов. При этом разработчики db4o уделяют повышенное внимание снижению накладных расходов, что делает ее привлекательным для небольших или встраиваемых систем. Также учитывайте, что в статье специально приводятся элементарные примеры. Поэтому, каковы бы ни были результаты сравнения db4o с другими продуктами, ее возможности выходят далеко за пределы того, что демонстрируется в данной статье.
Но я не могу запрашивать данные через JDBC!
Это тоже верно, хотя у разработчиков db4o была мысль создать драйвер JDBC, который позволил бы писать SQL-запросы к объектной базе данных - своего рода "реляционно-объектное отображение". В итоге от этой идеи было решено отказаться, потому что особой необходимости в этом не было, к тому же, по слухам, были проблемы с производительностью. Однако весь смысл использования ООСУБД в том, что используются только объекты (POJO) и ничего больше. Какой смысл использовать SQL, если данные не хранятся в табличном виде?
Но как получать доступ к данным из других приложений?
Ответ зависит от того, что вы имеете в виду, говоря о "других приложениях". Если речь идет о Java-приложениях, то они могут просто использовать класс Person, передавая его экземпляр в класс ObjectContainer точно так же, как показано выше. В ООСУБД определения классов играют роль схемы, поэтому их достаточно для поиска и выборки данных. Однако если вас интересует доступ к данным из приложений, написанных на других языках, то тут все сложнее.

Данные оказываются фактически недоступными из приложений на языках, не поддерживаемых db4o, например, C++ или Python (если, конечно, не создать промежуточный слой на Java). Существует версия db4o для C# и других языков платформы .NET. При этом используется тот же формат данных, что и в Java, благодаря чему данные Java-объектов могут храниться в объектах .NET. Если же под "другими приложениями" вы понимаете всевозможные средства на основе SQL для построения отчетов, работающие с базами данных через стандартные интерфейсы типа ODBC или JDBC, то использование db4o (как, впрочем, любой ООСУБД) будет, скорее всего, сопряжено с рядом неудобств. Те из вас, кто с недоверием относится к db4o, могут смело считать, что средств для генерации отчетов для db4o пока нет. Однако имейте в виду, что есть множество проектов, целью которых является создание подобных продуктов. Кроме того, в db4o существует возможность репликации - копирования данных в формате db4o в РСУБД.

Но этого всего лишь файл!
В данном конкретном случае это так. Но при этом у db4o есть гибкий набор возможностей, позволяющих варьировать способ хранения данных, включая использование легковесного клиент-серверного решения. Однако не стоит ожидать от db4o той же избыточности при хранении данных, как от полнофункциональной РСУБД (правда, некоторые ООСУБД предоставляют подобные возможности).
Но каждый повторный запуск примера приводит к дублированию данных!
Здесь нам придется вернуться к обсуждению вопроса идентификации - первого интересного аспекта, различающего объектную и реляционную модели. Как уже упоминалось ранее, объекты идентифицируются при помощи неявного указателя "this", который в случае Java представляет собой адрес в памяти. В терминологии ООСУБД он называется OID ( идентификатор объекта ). Именно он играет роль первичного ключа в объектных базах данных.

Создаваемые объекты изначально не ассоциированы ни с одним OID, поэтому при сохранении в базе данных им присваивается некий уникальный идентификатор. Таким образом, происходит дублирование данных аналогично тому, что наблюдается в РСУБД, когда для каждой вставляемой строки генерируется новый первичный ключ (для этого может использоваться последовательность или автоинкрементное поле). Другими словами, в том, что касается первичных ключей, ООСУБД ведут себя так же, как РСУБД. Вся проблема заключается лишь в том, что сами первичные ключи представляют собой совсем не то, чего ожидают разработчики, имеющие опыт работы с РСУБД.

Подводя итоги, стоит сказать, что db4o - это попытка решить конкретный набор проблем, она не задумывалась в качестве единственно верного и универсального средства хранения данных. На самом деле это именно то, что отличает db4o от всех предшествующих ей ООСУБД. Целью создания db4o отнюдь не является убеждение IT-индустрии полностью отказаться от использования реляционных баз данных.

Заключение

Крайне маловероятно, что какая либо модель хранения и управления данными сможет в обозримом будущем вытеснить традиционные централизованные реляционные базы данных. Гарантией этого является множество продуктов, созданных за долгое время доминирования РСУБД, а также большое количество программистов, "зацикленных" на идее использования баз данных. На самом деле db4o никогда не проектировалась и не позиционировалась в качестве технологии, способной бросить вызов РСУБД.

Однако можно прийти к интересным выводам, размышляя над ООСУБД в контексте многослойных, слабо связанных приложений, которые сейчас пользуются спросом при создании сервисно-ориентированных систем. Если предположить, что целью является ослабление зависимостей между компонентами (сервисами, слоями и т.д.), то становится очевидно, что единственным реальным показателем зависимости является связь между клиентом сервиса и используемым API (или форматом XML). Типы данных, объектная модель, многопользовательская база данных - все эти варианты хранения данных, по сути, являются лишь деталями реализации. При этом количество вариантов организации хранения данных, применимых в различных ситуациях, выросло на порядок.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=19321