Обработка данных в объектных базах данных, таких как db4o - PullRequest
8 голосов
/ 10 марта 2010

Одна вещь, которую я постоянно смущал при использовании объектной базы данных, такой как db4o , это то, как вы должны обрабатывать сложные миграции, которые обычно выполняются SQL / PL-SQL.

Например, представьте, что у вас есть таблица в реляционной базе данных с именем my_users. Первоначально у вас был столбец с именем «полное_имя», теперь, когда ваше программное обеспечение находится в V2, вы хотите удалить этот столбец, разделить полные имена на пустое пространство и поместить первую часть в столбец с именем «first_name», а вторую - в столбец. по имени фамилия. В SQL я просто заполнил бы столбцы «first_name» и «second_name», а затем удалил исходный столбец с именем «full_name».

Как бы я сделал это в чем-то вроде db4o? Должен ли я написать программу на Java, которая создает сценарии для поиска всех объектов User.class, устанавливая для full_name значение null при установке first_name и last_name? Когда я сделаю свой следующий SVN-коммит, не будет никакого свойства field / bean-компонента, соответствующего full_name, это будет проблемой? Кажется, как будто для использования в производственном приложении, где моя «схема» меняется, я хотел бы написать скрипт для переноса данных из версии x в версию x + 1, а затем в версии x + 2 фактически удалить свойства, которые я пытаюсь избавиться от версии x + 1, поскольку я не могу написать скрипт Java для изменения свойств, которые больше не являются частью моего типа.

Кажется, что отчасти проблема в том, что СУБД разрешает объект, на который вы ссылаетесь, на основе простого нечувствительного к регистру имени, основанного на строках, в языке, подобном типизации Java, сложнее, чем это, вы не можете ссылаться на свойство если getter / setter / field не является членом класса, загруженного во время выполнения, поэтому вам, по сути, нужно иметь 2 версии вашего кода в одном и том же скрипте (хм, пользовательские загрузчики классов звучат как боль), иметь новую версию вашего класса хранятся принадлежащие другому пакету (звучит грязно), или используйте стратегию версии x + 1 x + 2, о которой я упоминал (требует гораздо большего планирования). Возможно, есть какое-то очевидное решение, которое я никогда не нашел в документах db4o.

Есть идеи? Надеюсь, в этом есть какой-то смысл.

Ответы [ 2 ]

10 голосов
/ 11 марта 2010

Во-первых, db4o обрабатывает «простые» сценарии , такие как автоматическое добавление или удаление поля . При добавлении поля для всех существующих объектов сохраняется значение по умолчанию. Когда вы удаляете поле, данные существующего объекта все еще находятся в базе данных, и вы все равно можете получить к ним доступ. Поле переименования и т. Д. специальные вызовы рефакторинга .

Теперь по вашему сценарию вы бы сделали что-то вроде этого:

  1. Удалите поля 'full_name', добавьте новые поля 'first_name' и 'second_name'
  2. Итерация по всем объектам Address
  3. Доступ к старому полю через API-интерфейс StoredClass
  4. Разделить, изменить, обновить и т. Д. Значение. Установите новые значения в новом поле и сохраните объект.

Давайте предположим, что у нас есть класс Address. Поле 'full_name' было удалено. Теперь мы не хотим копировать его в «имя» и «фамилию». Тогда это может пойти так (Java):

    ObjectSet<Address> addresses = db.query(Address.class);
    StoredField metaInfoOfField = db.ext().storedClass(Address.class).storedField("full_name", String.class);
    for (Address address : addresses) {
        String fullName = (String)metaInfoOfField.get(address);
        String[] splitName = fullName.split(" ");
        address.setFirstname(splitName[0]);
        address.setSurname(splitName[1]);
        db.store(address);
    }

Как вы и предлагали, вы бы писали код миграции для каждой версии. Если это поле больше не является частью вашего класса, вы должны получить к нему доступ через API-интерфейс StoredField, как описано выше.

Вы можете получить список всех «сохраненных» классов с помощью ObjectContainer.ext().storedClasses(). С StoredClass.getStoredFields() вы можете получить список всех полей магазина, независимо от того, больше не существует ли поле в вашем классе. Если класс больше не существует, вы все равно можете получить объекты и получить к ним доступ через класс 'GenericObject'.

Обновление: для более сложных сценариев, когда необходимо перенастроить базу данных за несколько шагов версии.

Например, в версии v3 адрес-объект выглядит совершенно иначе. Таким образом, «сценарий миграции» для версий от v1 до v2 больше не содержит требуемых полей (имя и верное имя в моем примере). Я думаю, что есть несколько возможностей для обработки этого.

  1. (Предполагая Java для этой идеи. Конечно, есть эквивалент в .NET). Вы можете сделать шаг миграции Groovy-script . Так что каждому, что каждый скрипт не мешает другому. Затем вы определяете «классы», необходимые классы для миграции. Таким образом, каждая миграция имеет свои собственные миграционные классы. С aliases вы бы связали ваши классы groovy -igration с реальными java-классами.
  2. Создание классов рефакторинга для сложных сценариев. Также свяжите эти классы с псевдонимами .
2 голосов
/ 11 марта 2010

Я делаю здесь несколько диких снимков, потому что я не слишком много рефакторинг данных в своей жизни.

Вы проводите странное сравнение: если вы хотите выполнить «горячую миграцию» базы данных, вам, вероятно, придется использовать описанный вами подход x+1, x+2, но я не совсем знаю - Я бы тоже не знал, как это сделать с SQL, так как я не эксперт по БД.

Однако, если вы переносите «холодный», вы можете просто сделать это за один шаг, создав новый объект из старых данных, сохранить новый объект, удалить старый объект для каждого объекта в хранилище. См. db4o ссылка .

Но, честно говоря: тот же процесс в СУБД также сложен, потому что вам придется деактивировать проверки ограничений (и, возможно, триггеры и т. Д.), Чтобы фактически выполнить операцию - возможно, не в приведенном вами примере, но для большинства реальных случаев. В конце концов, разделение строк настолько легко, что выигрыша будет мало.

В SQL я бы просто заполнил столбцы "first_name" и "second_name"

Да, с помощью простой операции разбиения строки вы можете просто сделать это. Но в типичном сценарии рефакторинга вы реструктурируете объекты на основе больших и сложных наборов правил, которые могут быть нелегко выразить в SQL, могут потребоваться сложные вычисления или внешние источники данных.

Для этого вам также придется написать код.

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

...