Путеводитель по db4о для Java-разработчика: Транзакции, распределенный доступ и безопасность

FAQ

В предыдущих статьях серии я освещал основы объектно-ориентированной работы с данными в db4o. Одним из вопросов, которые я обошел вниманием, является использование ООСУБД в Web-приложениях и чем оно отличается от использования в приложениях Swing или SWT. Кто-то может сказать, что я проигнорировал целый круг вопросов, которые обязан знать любой .NET- или Java-разработчик.

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

В этой статье — последней части серии «Руководство db4о для Java-разработчика» — я расскажу о возможностях, которые должны присутствовать в любой системе для хранения данных, будь то объектно-ориентированная, реляционная или какая-либо еще. Вздохните поглубже и приготовьтесь слушать про обеспечение безопасности, распределенный доступ и транзакционность в db4o.

Поддержка множественных соединений

В примерах всех предыдущих частей подразумевалось, что только одно клиентское приложение будет работать с базой данных. Поэтому будет достаточно всего одного логического соединения, через которое будет проходить весь обмен данными. Это вполне логичное допущение для приложения Swing или SWT, работающего с базой данных настроек или локальным хранилищем. Но это гораздо менее реалистично для Web-приложения, даже если допустить, что все данные будут храниться в слое Web-представления.

В db4o открытие дополнительного логического соединения с базой данных является тривиальной задачей, даже если база данных расположена на локальном диске. Все, что требуется – это создать объект ObjectServer и получить от него экземпляр класса ObjectContainer. Если указать объекту ObjectServer, чтобы он ожидал входящие соединения через порт 0, то он будет работать во «встроенном» (embedded) режиме. Таким образом, в нашем пробном тесте он не будет открывать реальные порты TCP/IP, что могло бы помешать другим приложениям (листинг 1).

Листинг 1. Множественные соединения во встраиваемом режиме
@Test public void letsTryMultipleEmbeddedClientConnections()
{
ObjectServer server = Db4o.openServer(«persons.data», 0);

try
{
ObjectContainer client1 = server.openClient();
Employee ted1 = (Employee)
client1.get(
new Employee(«Ted», «Neward», null, null, 0, null))
.next();
System.out.println(«client1 found ted: » + ted1);

ObjectContainer client2 = server.openClient();
Employee ted2 = (Employee)
client2.get(
new Employee(«Ted», «Neward», null, null, 0, null))
.next();
System.out.println(«client2 found ted: » + ted2);

ted1.setTitle(«Lord and Most High Guru»);
client1.set(ted1);
System.out.println(«set(ted1)»);

System.out.println(«client1 found ted1: » +
client1.get(
new Employee(«Ted», «Neward», null, null, 0, null))
.next());

System.out.println(«client2 found ted2: » +
client2.get(
new Employee(«Ted», «Neward», null, null, 0, null))
.next());

client1.commit();
System.out.println(«client1.commit()»);

System.out.println(«client1 found ted1: » +
client1.get(
new Employee(«Ted», «Neward», null, null, 0, null))
.next());

System.out.println(«client2 found ted2: » +
client2.get(
new Employee(«Ted», «Neward», null, null, 0, null))
.next());

client2.ext().refresh(ted2, 1);
System.out.println(«After client2.refresh()»);
System.out.println(«client2 found ted2: » +
client2.get(
new Employee(«Ted», «Neward», null, null, 0, null))
.next());

client1.close();
client2.close();
}
finally
{
server.close();
}
}

Обновление представления объектов

Обратите внимание на строки для отладочного вывода в середине тестового примера. Очень важно постоянно контролировать состояние объектов, потому что db4o хранит ссылки на объекты, доступ к которым был получен ранее. Необходимо точно знать, как и когда изменения в данных объектах становятся видимыми для второго клиента.

Например, после вызова метода set(ted1) db4o помечает объект ted1 как «грязный» и требующий обновления. Но само обновление не происходит до момента подтверждения неявной транзакции с помощью вызова commit() класса ObjectContainer. В этот момент данные сохраняются на диске, но client2 по-прежнему видит предыдущее состояние объектов. Убедитесь в этом, просмотрев отладочный вывод демонстрационного тестового примера. Ведь вместе с браузером у вас открыто и окно консоли, в котором вы запускаете тесты, читая статьи этой серии, не так ли?

С этой проблемой легко справиться: client2 должен обновить свое представление графа объектов в памяти, вызвав метод refresh() объекта extension, который можно получить с помощью метода ext(). При этом необходимо помнить о глубине активации: она определяет, насколько далеко db4o должен углубиться в граф, обновляя состояние объектов. В нашем случае достаточно глубины, равной единице, но вообще решение должно приниматься с учетом конкретной ситуации.

client2 увидит изменения сразу после обновления представления объектов. В этом легко убедиться, выполнив запрос, возвращающий новое имя главы компании (возможно, оно выглядит несколько эгоистично).