Статьи

db4o: простая стойкость POJO

Начиная от мобильных приложений до веб-приложений и от простой старой 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 для поездки , вы не пожалеете об этом!