Статьи

Создание версий вашей базы данных с помощью Liquibase

Большинство приложений, которые мы разрабатываем, управляются с помощью какой-то системы контроля версий. Но как насчет баз данных, используемых этими приложениями? Чаще мы вносим изменения в наши базы данных разработки, тестирования и производства вручную. Такой подход может работать для приложения только с одним или двумя разработчиками, но в большой команде с несколькими разработчиками становится трудно поделиться изменениями со всеми.

В этой статье мы обсудим Liquibase, инструмент с открытым исходным кодом для управления и контроля версий изменений базы данных. Это помогает нам организовывать инкрементные изменения базы данных в различные наборы изменений и применять их к базе данных.

Liquibase — не единственный инструмент управления версиями / миграции баз данных. Существует много решений, таких как миграции Doctrine 2, миграции Rails AR, DBDeploy и так далее. Первые два варианта — отличные решения, но они зависят от платформы. DBDeploy относительно прост, но он не так богат, как Liquibase. Liquibase решает многие проблемы, которые не решаются различными инструментами миграции баз данных, такими как поддержка нескольких разработчиков, различные системы СУБД, ветвление и т. Д.

Кроме того, серьезным недостатком большинства инструментов является то, что они не знают об изменениях. Вместо того чтобы сфокусировать сделанные изменения, они сравнивают два снимка схемы базы данных для создания сценария миграции. Так, например, переименование столбцов рассматривается как операция удаления + добавления, которая может привести к потере данных. Liquibase осведомлен об изменениях.

Давайте посмотрим, как использовать Liquibase в наших проектах.

Как работает Liquibase

Если вы используете Mac с brew, установить Liquibase очень просто. Просто запустите brew install Liquibase и все готово. То же самое относится и к Ubuntu, sudo apt-get install liquibase сделает это за вас. Двоичный файл Liquibase — это кроссплатформенное Java-приложение, что означает, что вы можете загрузить JAR и использовать его в Windows, Mac или Linux. Рекомендуется хранить его в папке проекта, чтобы любой пользователь проекта мог использовать его без каких-либо установок.

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

В файле журнала изменений вы организуете изменения в разных наборах изменений. Набор изменений может содержать одно или несколько изменений, которые вы хотите применить к своей базе данных. Каждый набор изменений может быть уникально идентифицирован с использованием атрибутов id и author вместе с путем к классу файла журнала изменений. Liquibase создает таблицу ( databasechangelog ) в вашей базе данных для отслеживания успешно примененных изменений. Liquibase последовательно просматривает каждый набор изменений и проверяет, были ли они уже применены, сравнивая контрольную сумму в таблице databasechangelog changelog. Он применит изменение, если оно еще не было выполнено или если на нем есть тег runAlways .

Начиная

Для демонстрации я создал базу данных с именем application на моем локальном сервере MySQL, а также файл журнала изменений. Вы можете хранить его в папке своего проекта или в отдельном месте, но файлы журнала изменений должны находиться под контролем версий.

Вот первая версия нашего файла журнала изменений без наборов изменений.

 <?xml version="1.0" encoding="UTF-8"?> <!--db.changelog.xml--> <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"> </databaseChangeLog> 

В командной строке перейдите в папку, в которой вы храните файл журнала изменений, и выполните следующую команду:

  liquibase --driver = com.mysql.jdbc.Driver 
      --classpath = .. / Библиотека / MySQL-разъем-Java-5.1.21-bin.jar 
      --changeLogFile = db.changelog.xml 
      --url = "JDBC: MySQL: // локальный / приложение" 
      --username = DbUser 
      --password = секрет 
      Обновить 

Если Liquibase может подключиться к базе данных, используя заданные имя пользователя и пароль, она должна создать две таблицы в базе данных приложения, DATABASECHANGELOG и DATABASECHANGELOGLOCK , и показать следующий вывод:

  ИНФОРМАЦИЯ 02.08.12 10:19: liquibase: успешно получена блокировка журнала изменений
 ИНФОРМАЦИЯ 02.08.12 10:19: liquibase: Создание таблицы истории базы данных с именем: `DATABASECHANGELOG`
 ИНФОРМАЦИЯ 02.08.12 10:19: liquibase: чтение из `DATABASECHANGELOG`
 ИНФОРМАЦИЯ 02.08.12 10:19: liquibase: чтение из `DATABASECHANGELOG`
 ИНФОРМАЦИЯ 02.08.12 10:19: liquibase: успешно снята блокировка журнала изменений
 Обновление Liquibase успешно 

В приведенной выше команде обязательны все параметры, кроме classpath . driver указывает имя класса драйвера базы данных, который мы хотим использовать. changeLogFile — это имя нашей базы данных изменений. url указывает строку подключения к базе данных JDBC, которая включает тип сервера, имя хоста и имя базы данных. classpath — это место, где вы храните классы, такие как коннектор базы данных, используемые Liquibase.

Вместо указания параметров командной строки при каждом запуске Liquibase вы можете хранить их в файле свойств Java с именем liquibase.properties в том же каталоге. Затем вы можете запустить просто liquibase <command> Файл свойств будет выглядеть так:

  # liquibase.properties
 водитель: com.mysql.jdbc.Driver
 classpath: ../lib/mysql-connector-java-5.1.21-bin.jar
 url: jdbc: mysql: // localhost / application
 changeLogFile: db.changelog.xml
 имя пользователя: dbuser
 пароль: секрет 

Теперь давайте создадим пользовательскую таблицу с полями ID, name и email, добавив набор изменений в db.changelog.xml . Вот обновленный XML:

 <?xml version="1.0" encoding="UTF-8"?> <!--db.changelog.xml--> <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"> <changeSet id="1" author ="shameer"> <createTable tableName="user"> <column name="id" type="int"> <constraints primaryKey="true" nullable="false" /> </column> <column name="name" type="varchar(50)"> <constraints nullable="false" /> </column> <column name="email" type="varchar(128)"> <constraints unique="true" nullable="false" /> </column> </createTable> </changeSet> </databaseChangeLog> набор <?xml version="1.0" encoding="UTF-8"?> <!--db.changelog.xml--> <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"> <changeSet id="1" author ="shameer"> <createTable tableName="user"> <column name="id" type="int"> <constraints primaryKey="true" nullable="false" /> </column> <column name="name" type="varchar(50)"> <constraints nullable="false" /> </column> <column name="email" type="varchar(128)"> <constraints unique="true" nullable="false" /> </column> </createTable> </changeSet> </databaseChangeLog> 

createTable принимает имя таблицы в tableName и столбцы в качестве вложенных тегов Вы указываете столбцы в этой таблице с тегом column с его name и datatype качестве обязательных атрибутов. column поддерживает ряд других полезных атрибутов, которые могут иметь смысл не во всех случаях. Например, вы можете установить автоинкремент столбца, используя autoIncrement="true" .

Любые ограничения столбцов указываются тегами constraints . В нашем примере столбец id имеет первичный ключ, а не ограничения NULL, а столбец email имеет уникальное ограничение.

Запустите Liquibase и посмотрите на результат:

  обновление ликвазы

 ИНФОРМАЦИЯ 8/4/12 7:16: liquibase: успешно получена блокировка журнала изменений
 ИНФОРМАЦИЯ 04.08.12 7:16: liquibase: Создание таблицы истории базы данных с именем: `DATABASECHANGELOG`
 ИНФОРМАЦИЯ 8/4/12 7:16: liquibase: чтение из `DATABASECHANGELOG`
 ИНФОРМАЦИЯ 8/4/12 7:16: liquibase: чтение из `DATABASECHANGELOG`
 ИНФОРМАЦИЯ 04.08.12 7:16: liquibase: ChangeSet db.changelog.xml :: 1 :: shameer успешно запущен за 74 мс
 ИНФОРМАЦИЯ 8/4/12 7:16: liquibase: успешно снята блокировка журнала изменений
 Обновление Liquibase успешно 

Если вы посмотрите на базу данных, вы увидите user таблицу со следующей структурой:

  *************** 1. ряд ***************
   Поле: id
    Тип: int (11)
    Ноль: НЕТ
     Ключ: PRI
 По умолчанию: NULL
   Дополнительно: 
 *************** 2. строка ***************
   Поле: имя
    Тип: варчар (50)
    Ноль: НЕТ
     Ключ: 
 По умолчанию: NULL
   Дополнительно: 
 *************** 3. ряд ***************
   Поле: электронная почта
    Тип: varchar (128)
    Ноль: НЕТ
     Ключ: UNI
 По умолчанию: NULL
   Дополнительно: 

Вы заметили, что у нас нет столбца с автоинкрементом в таблице? Мы можем добавить второй набор изменений, чтобы изменить таблицу, сделав следующие изменения:

  • Изменить столбец id на auto_increment
  • Переименовать name столбца в fullname name
  • Добавить новый age столбца
 <changeSet id="2" author="shameer"> <addAutoIncrement tableName="user" columnName="id" columnDataType="int" /> <renameColumn tableName="user" oldColumnName="name" newColumnName="fullname" columnDataType="varchar(100)"/> <addColumn tableName="user"> <column name="age" type="SMALLINT"/> </addColumn> </changeSet> 

Здесь тег addAutoIncrement будет автоматически увеличивать столбец id . renameColumn переименует столбец, приняв имя столбца, подлежащего изменению, в oldColumnName и новое имя в newColumnName .

Давайте снова запустим Liquibase, а затем посмотрим на таблицу user . Вы должны увидеть два измененных столбца и новый столбец age .

Создание файла журнала изменений

Что если вы уже начали свой проект без Liquibase, а затем понимаете, что с изменениями в базе данных будет сложно справиться без какого-либо механизма автоматизации? Liquibase поставляется с удобной функцией для создания файла журнала изменений из существующих схем. Используйте следующую команду для создания файла liquibase.properties изменений с именем, указанным в liquibase.properties .

  liquibase generateChangeLog 

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

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

Вот шаги для создания файла журнала изменений для вновь созданных таблиц в базе данных:

  1. Сделайте резервную копию базы данных, используя mysqldump
  2. Создать временную базу данных
  3. Импортировать дамп во временную базу данных
  4. Отменить все изменения, сделанные в исходной базе данных
  5. Создайте журнал изменений, используя Liquibase.
  liquibase diffChangeLog 
 --referenceUrl = JDBC: MySQL: // локальный / application_temp 
 --referenceUsername = DbUser 
 --referencePassword = секрет 

Это добавит новый набор изменений в db.changelog.xml для создания отсутствующих таблиц в базе данных.

 <changeSet author="shameerc (generated)" id="1344051849388-1"> <createTable tableName="temp"> <column autoIncrement="true" name="id" type="INT"> <constraints nullable="false" primaryKey="true"/> </column> <column name="name" type="VARCHAR(200)"/> <column name="value" type="TEXT"/> </createTable> </changeSet> 

После того, как вы сгенерировали, вы должны проверить и проверить это в вашей локальной базе данных, прежде чем вы передадите его на контроль версий. Также будьте осторожны при создании таких файлов журнала изменений, так как Liquibase будет рассматривать переименованные столбцы как столбец удаления + добавления, что приведет к потере данных.

Откат изменений

До сих пор мы смотрели только на внесение изменений в нашу схему базы данных. Но что если нам нужно отменить изменения после их применения к базе данных? Мы можем легко откатить изменения, примененные через Liquibase, используя команды отката.

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

  откат жидкостной базыСчет 1 

Liquibase позволяет нам добавлять контрольные точки в нашу базу данных, помечая ее текущее состояние. Это помогает нам легко вернуться к определенному состоянию, просто вызвав его тег.

  тег жидкой основы checkpoint_1 

Затем вы можете вернуться к этому состоянию после внесения некоторых изменений, выполнив следующую команду.

  Контрольная точка отката жидкостной базы_1 

Иногда вы можете проверить SQL, примененный для отката изменений. Для этого замените rollback на rollbackSql и rollbackCount на rollbackCountSql . Liquibase напечатает результат в стандартный вывод, который можно перенаправить в файл и сохранить для последующего выполнения.

Вы можете указать пользовательские операторы, которые будут выполняться в теге rollback для каждого журнала изменений. При выполнении отката Liquibase будет применять эти операторы вместо SQL отката по умолчанию.

 <changeSet id="6" author="shameer"> <modifyDataType columnName="value" newDataType="TEXT" tableName="temp"/> <rollback> <dropTable tableName="temp"/> </rollback> </changeSet> 

Вы можете проверить это, создав откат SQL после применения набора изменений.

  жидкостная откаткаCountSql 1

 - Блокировка базы данных
 - Откат Набор изменений: db.changelog.xml :: 6 :: shameer: ​​:( Контрольная сумма: 3: 29343b94088e34367e51a6633f572b81)
 DROP TABLE `temp`;

 УДАЛИТЬ ИЗ `DATABASECHANGELOG` WHERE ID = '6' И AUTHOR = 'shameer' И FILENAME = 'db.changelog.xml';

 - снять блокировку базы данных
 Откат Liquibase успешен 

Условно применяя изменения

Иногда вам может потребоваться проверить определенное условие перед выполнением набора изменений, например убедиться, что таблица не содержит данных, прежде чем вы ее отбросите. preConditions — это полезный тег, который позволяет вам проверять различные условия в вашей базе данных.

 <changesSet id="8" author="shameer"> <preConditions onFail="CONTINUE"> <sqlCheck expectedResult="0">select count(*) from user</sqlCheck> </preConditions> <dropTable tableName="user"/> </changesSet> 

В этом случае Liquibase сначала проверит, содержит ли пользовательская таблица какие-либо записи, и если это так, то продолжит работу без выполнения этого набора изменений. Если условие является критическим, вы можете остановить выполнение, установив onFail="HALT" вместо CONTINUE .

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

Резюме

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

Изображение через Fotolia