Есть пара подходов, которые я использовал ранее или использую в настоящее время:
Номер последовательной версии
У большинства тех, кто использует этот подход, есть отдельная программа, которая получает номер версии из базы данных, а затем выполняет все операторы, связанные с версиями базы данных, превышающими этот номер, и, наконец, обновляет номер версии в базе данных.
Таким образом, если версия 37 и в приложении для обновления есть операторы, связанные с версиями 1–38, она пропустит 1–37 и выполнит операторы, чтобы привести базу данных к версии 38.
Я видел реализации, которые также позволяют операторам понижения для каждой версии отменять действия обновления, и это позволяет перенести базу данных с версии 38 обратно на версию 37.
В моей ситуации у нас было обновление базы данных в самом приложении, и мы не имели понижения версии. Поэтому изменения контролировались исходным кодом, поскольку являлись частью приложения.
Направленный ациклический граф
В более недавнем проекте я предложил другой подход. Я использую классы, которые являются узлами направленного ациклического графа, чтобы инкапсулировать операторы для выполнения конкретных обновлений базы данных для каждой конкретной функции / исправления / etc. Каждый узел имеет атрибут для объявления своего уникального имени и имен любых узлов, от которых он зависел. Эти атрибуты также используются для поиска в сборке всех узлов обновления.
Корневой узел по умолчанию задается как узел зависимости для любых узлов без зависимостей, и этот узел содержит операторы для создания таблицы migrationregister
, в которой перечислены имена уже примененных узлов. После сортировки всех узлов в последовательный список они выполняются по очереди, пропуская уже примененные.
Все это содержится в отдельном приложении от основного приложения, и они контролируются исходным кодом в одном и том же хранилище, так что, когда разработчик заканчивает работу над функцией и изменениями базы данных, связанными с ней, они фиксируются вместе в тот же набор изменений. Если вы извлекаете изменения для функции, вы также извлекаете изменения в базе данных. Кроме того, главное приложение просто нуждается в списке ожидаемых имен узлов. Любые дополнительные или отсутствующие, и он знает, что база данных не совпадает.
Я выбрал этот подход, потому что проект часто имеет параллельную разработку несколькими разработчиками, причем каждый разработчик иногда имеет более чем одну вещь в разработке (ветвистая разработка, иногда очень ветвь). Жонглирование номерами баз данных было довольно болезненно. Если все начали с версии 37, и «Алиса» что-то запускает и использует версию 38, поэтому она изменит свою базу данных, и «Боб» также начнет работу, которая должна изменить базу данных, а также использует версию 38, кому-то потребуется в конечном итоге изменить , Допустим, Боб заканчивает работу и отправляет на сервер. Теперь Алиса, когда она извлекает ревизию Боба, должна изменить версию для операторов на 39 и установить версию своей базы данных обратно на 37, чтобы изменения Боба были выполнены, но затем она выполняет снова .
Но когда все, что происходит, когда Алиса извлекает ревизию Боба, заключается в том, что в списке имен узлов просто есть новый узел миграции и другая строка, все просто работает.
Мы используем Mercurial (распределенный), а не SVN (клиент-сервер), поэтому у нас такой подход работает так хорошо.