Начиная от мобильных приложений до веб-приложений и от простой старой Java до диалектов Scala или Groovy, современный Java-разработчик всегда нуждается в асе в дыре, когда речь идет о сохранении данных. В идеале вы ищете решение, которое даст вам достаточно мощности, чтобы справиться со сложностью вашего домена, и при этом быть достаточно простым, чтобы повысить вашу производительность, избегая болезненных конфигураций или крутых кривых обучения.
С одной стороны этого сценария у вас есть реляционные базы данных с инструментами ORM, которые заставляют вас пройти несколько этапов, таких как создание файла сопоставления объектов, файла конфигурации базы данных, вспомогательного класса для инициализации фабричной фабрики сеансов и сопоставлений ассоциаций классов (т.е. время, потраченное на загрузку, установку, настройку, а затем написание большого количества XML). С другой стороны есть просто старая старая сериализация (в основном заканчивающаяся в файлах XML). Но с сериализацией Java вы быстро сталкиваетесь с критическим предостережением: объекты должны быть помечены путем реализации интерфейса java.io.Serializable. Однако простое добавление «реализует Serializable» в определение класса не делает его сериализуемым автоматически. Переменные экземпляра класса также должны быть сериализуемыми (облом).Если это не так и вы пытаетесь сериализовать класс, будет выдано исключение. К сожалению, сериализация в Java не является прямым решением для сохранения объектов. Есть много дополнительных ловушек, связанных с процессом сериализации, таких как проблемы управления версиями объекта, нарушения идентификации объекта и проблемы развития схемы (среди прочих).
Прямо посередине, будучи достаточно мощным для работы со сложными доменами, но настолько простым, что вы можете хранить объекты с одной строкой кода, есть db4o , механизм персистентности, который был задуман для облегчения жизни разработчика Java. Зачем определять отдельную схему для ваших данных, если ваши классы определяют ее идеально? С db4o ваша объектная модель является вашей схемой базы данных. Когда вы определяете структуру ваших классов, вы не только делаете заявление о том, как должны выполняться ваши объекты, но и о том, как они должны быть постоянными! И зачем соглашаться с сериализацией объекта? Сериализация — это популярная альтернатива, позволяющая избежать сложных настроек персистентности, но она может обрабатывать очень ограниченное количество простых сценариев.
Так что, если бы вы могли обладать мощью базы данных ACID с простотой использования сериализации объектов?
В этой статье я покажу вам, как использовать различные функции db4o в различных сценариях на основе Java, от мобильных приложений до корпоративных приложений, с акцентом на реальных примерах, взятых из реальных проектов на основе db4o.
Использование db4o в рамках Griffon
Griffon — это среда приложений, похожая на Grails, для разработки настольных приложений в Groovy. Плагин db4o (
griffon-db4o v0.1 ) обеспечивает легкий доступ к функциям базы данных с использованием db4o.
После установки плагин генерирует файлы Db4oConfig.groovy (который содержит определение источника данных) и BootstrapDb4o.groovy (который определяет ловушки init / destroy для данных, которые будут манипулироваться во время запуска / завершения работы приложения). Наконец, новый динамический метод с именем
withDb4o внедряется во все контроллеры, предоставляя вам доступ к
экземпляру com.db4o.ObjectContainer , с помощью которого вы сможете совершать вызовы в базу данных.
Рассмотрим класс:
class Person {
int id
String name
String lastname
}
Данные начальной загрузки могут быть заполнены с помощью этого простого кода:
class BootstrapDb4o {
def init = { db4o ->
db4o.store(new Person(id: 1, name: "Danno", lastname: "Ferrin"))
db4o.store(new Person(id: 2, name: "Andres", lastname: "Almiray"))
db4o.store(new Person(id: 3, name: "James", lastname: "Williams"))
db4o.store(new Person(id: 4, name: "Guillaume", lastname: "Laforge"))
db4o.store(new Person(id: 5, name: "Jim", lastname: "Shingler"))
db4o.store(new Person(id: 6, name: "Josh", lastname: "Reed"))
db4o.store(new Person(id: 7, name: "Hamlet", lastname: "D'Arcy"))
}
def destroy = { db4o ->
}
}
Реализация контроллера, который будет реагировать на событие приложения, загружать данные во временный список и обновлять model.personsList внутри EDT так же просто, как это:
class SampleController {
def model
def onStartupEnd = { app ->
withDb4o { db4o ->
def tmpList = db4o.query(Person)
edt { model.personsList.addAll(tmpList) }
}
}
}
db4o и Scala
Не удивительно, что db4o идеально подходит для Scala . Это настолько просто, что вы можете реализовать репликацию основных / подчиненных постоянных объектов в менее чем 100 строках кода Scala. Код для обновления ведомого сводится к следующему:
private def bringSlaveUpToDate() {
println("starting replication...")
val replication = Replication.begin(master, slave, replicationListener)
val changes = replication.providerA().objectsChangedSinceLastReplication().iterator()
if (!changes.hasNext) {
println("Nothing to replicate")
return
}
while (changes.hasNext()) {
val changed = changes.next();
replication.replicate(changed)
}
removeCommitListener
try {
replication.commit()
} finally {
listenToMasterCommits
}
println("replication finished")
}
И использование этого
агента репликации Scala удивительно просто:
package instant;
import com.db4o._
import com.db4o.config._
import scala.actors.Actor._
import scala.concurrent.SyncVar
case class Item(name: String)
object Main {
def main(args : Array[String]) : Unit = {
deleteFile("master.db4o"); deleteFile("slave.db4o")
val slave = Db4o.openFile(configuration, "slave.db4o")
val masterServer = Db4o.openServer(configuration, "master.db4o", -1)
val masterClient = masterServer.openClient(configuration)
for (i <- 1 to 10) {
masterClient.store(Item("Item " + i))
}
masterClient.commit()
try {
val agent = new ReplicationAgent(masterServer.ext.objectContainer, slave)
agent.start()
val finished = new SyncVar[boolean]
val masterUser = actor {
for (i <- 10 to 150) {
val item = Item("Item " + i)
masterClient.store(item)
println("Committing " + item)
masterClient.commit()
}
finished.set(true)
}
finished.get
println("stopping agent")
agent.stop
} finally {
withErrorHandling { masterServer.close }
withErrorHandling { slave.close }
}
}
def withErrorHandling(block : => Unit) {
try {
block
} catch {
case e => println(e.toString)
}
}
def configuration = {
val c = Db4o.newConfiguration()
c.generateUUIDs(ConfigScope.GLOBALLY)
c.generateVersionNumbers(ConfigScope.GLOBALLY)
c
}
def deleteFile(fname: String) = new java.io.File(fname).delete()
}
Сервлет с db4o
db4o также можно использовать в классической веб-среде Java, поскольку база данных работает в поточно-безопасном режиме. К сервлетам может обращаться несколько потоков, поскольку транзакции в db4o работают в поточно-ориентированном режиме.
Сначала вы копируете библиотеку db4o в
каталог WEB-INF / lib . Это позволяет вашему приложению получить доступ к классу, ответственному за создание сервера и клиента. Затем вам нужно настроить доступ к файлу базы данных и предоставить средства для создания ObjectServer для каждого приложения. Один из отличных способов сделать это состоит в создании ObjectServer при запуске контекста приложения и закрытии соединения после завершения контекста. Мы можем сделать это, разработав
класс Listener :
package myproject.dao;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import com.db4o.Db4o;
import com.db4o.ObjectServer;
public class Db4oServletContextListener implements ServletContextListener {
public static final String KEY_DB4O_FILE_NAME = "db4oFileName";
public static final String KEY_DB4O_SERVER = "db4oServer";
private ObjectServer server = null;
public void contextInitialized(ServletContextEvent event) {
close();
ServletContext context = event.getServletContext();
String filePath = context.getRealPath("WEB-INF/db/" + context.getInitParameter(KEY_DB4O_FILE_NAME));
server = Db4o.openServer(filePath, 0);
context.setAttribute(KEY_DB4O_SERVER, server);
context.log("db4o startup on " + filePath);
}
public void contextDestroyed(ServletContextEvent event) {
ServletContext context = event.getServletContext();
context.removeAttribute(KEY_DB4O_SERVER);
close();
context.log("db4o shutdown");
}
private void close() {
if (server != null) {
server.close();
}
server = null;
}
}
Это очень легко понять код выше. Когда контекст запущен, создается экземпляр db4oServer. А при остановке соединение с базой данных прекращается. Не забудьте настроить прослушиватель контекста приложения в
файле web.xml, находящемся в каталоге
WEB-INF :
<listener>
<listener-class>meuprojeto.dao.Db4oServletContextListener</listener-class>
</listener>
Обратите внимание, что вы должны предоставить полное имя класса Listener (включая пакет). Наконец, пришло время настроить путь к файлу db4o, используемый слушателем:
<context-param>
<param-name>db4oFileName</param-name>
<param-value>web.yap</param-value>
</context-param>
Постоянство Android: db4o
Когда вы рассмотрите SQLite, базу данных defacto для Android, и сравните ее с db4o, вы поймете, что при использовании постоянства объектов в мобильном приложении есть много преимуществ: более простое обслуживание кода благодаря простоте и возможность создавать множество новых, инновационных приложений, основанных на более сложных моделях данных. В отличие от жестких предопределенных таблиц SQL, db4o позволяет хранить динамические данные в свободной форме, которые могут быть изменены или исправлены в любое время (что особенно полезно при отправке обновлений пользователям). Кроме того, db4o обеспечивает эффективную репликацию данных с помощью своей системы репликации db4o (dRS), еще одного недостающего элемента в программном стеке Android.
Давайте рассмотрим операцию вставки и обновления с SQLite на Android:
//SQLite Insert
public void addPassword(PassEntry entry) {
ContentValues initialValues = new ContentValues();
initialValues.put("password", entry.password);
initialValues.put("description", entry.description);
initialValues.put("username", entry.username);
initialValues.put("website", entry.website);
initialValues.put("note", entry.note);
db.insert(DATABASE_TABLE, null, initialValues);
}
//SQLite Update
public void updatePassword(long Id, PassEntry entry) {
ContentValues args = new ContentValues();
args.put("password", entry.password);
args.put("description", entry.description);
args.put("username", entry.username);
args.put("website", entry.website);
args.put("note", entry.note);
db.update(DATABASE_TABLE, args,"id=" + Id, null);
}
Если вы выберете db4o, вы можете избавиться от кода выше и заменить его следующим способом:
//db4o Upsert
public void savePassword(PassEntry entry) {
if(entry.id == 0)
entry.id = getNextId();
db().store(entry);
db().commit();
}
который служит как для вставки, так и для обновления (хранилище действует как обновление, если объект уже существует, и это автоматически определяется db4o).
db4o Интеграция с Spring
Хотите использовать db4o для легкого сохранения объектов в вашем приложении на базе Spring ? Попробуйте расширение db4o, которое существует уже некоторое время, модуль ‘
spring-db4o ‘ от Spring Modules. Любой, кто интегрирует db4o в корпоративное приложение, может получить выгоду от декларативного управления транзакциями, трансляции исключений и простоты настройки.
Конфигурация довольно проста. Чтобы создать объектный контейнер db4o на основе памяти , можно использовать следующую конфигурацию:
<bean id="memoryContainer" class="org.db4ospring.ObjectContainerFactoryBean">
<property name="memoryFile">
<bean class="com.db4o.ext.MemoryFile"/>
</property>
</bean>
Для ObjectContainer, подключенного к (удаленному) серверу:
<bean id="remoteServerContainer" class="org.db4ospring.ObjectContainerFactoryBean">
<property name="hostName" value="localhost"/>
<property name="port" value="123"/>
<property name="user" value="foo"/>
<property name="password" value="bar"/>
</bean>
При создании файла базы данных на основе локального ObjectContainer можно добиться с помощью определения компонента, такого как:
<bean id="fileContainer" class="org.db4ospring.ObjectContainerFactoryBean">
<property name="databaseFile" value="classpath:db4o-file.db"/>
</bean>
Основными классами модуля db4o, которые используются на практике, являются
Db4oTemplate и
Db4oCallback . Шаблон переводит исключения db4o в иерархию исключений Spring Data Access (что упрощает интеграцию db4o с другими средами персистентности, поддерживаемыми Spring) и отображает большинство методов интерфейса db4o
ObjectContainer и
ExtObjectContainer , позволяя использовать одну строку :
db4oTemplate.activate(personObject, 4); // or
db4oTemplate.releaseSemaphore("myLock");
Модуль db4o также обеспечивает интеграцию с отличной поддержкой транзакций Spring через
класс Db4oTransactionManager . Поскольку операторы db4o всегда выполняются внутри транзакции, разграничение транзакции Spring можно использовать для фиксации или отката запущенной транзакции в определенных точках в ходе выполнения.
Итог
Многие разработчики Java уже наслаждаются простотой сохранения объектов с помощью db4o. Прежде чем выбрать реляционное хранилище данных с ORM-отображением или сериализацией XML для вашего следующего проекта, спросите себя, можете ли вы сделать вещи проще и быстрее, просто используя db4o.
Возьмите db4o для поездки , вы не пожалеете об этом!