Статьи

Управление исходным кодом вашей базы данных с Liquibase

Недавно я начал использовать Liquibase в проекте для отслеживания изменений базы данных в нашем приложении Java Enterprise. Я должен сказать, что мне нравится, как это работает. Это делает развертывание моего приложения (или его нового выпуска) в другой среде более простым и (более) надежным. В прошлом мне приходилось предоставлять сценарий базы данных для администратора баз данных, который должен был выполняться сразу после или до того, как я повторно развернул свой файл EAR / WAR со всеми проблемами, которые идут с этой процедурой (сбой сценария / недоступность DBA и т. Д.). Сейчас я не буду говорить, что с этим решением не может быть проблем, но с точки зрения разработчика оно облегчает жизнь.

Вот как это работает для простого проекта Maven с некоторыми веб-сервисами, которые поддерживаются базой данных MySQL . Поскольку я развернул веб-службы в файле WAR на JBoss, я решил, что сценарии Liquibase запускаются экземпляром ServletContextListener . Чтобы иметь возможность протестировать скрипт базы данных без необходимости развертывания приложения, я также встроил плагин Maven для Liquibase в свой файл pom. Таким образом, я могу запускать сценарии liquibase вручную с Maven для моей локальной базы данных разработки.

Сначала добавьте необходимые зависимости в Maven pom.xml:

01
02
03
04
05
06
07
08
09
10
11
<dependency>                                          
    <groupId>org.liquibase</groupId>                     
    <artifactId>liquibase-core</artifactId>              
    <version>2.0.5</version>                             
</dependency>                                         
<dependency>                                          
    <groupId>mysql</groupId>                          
    <artifactId>mysql-connector-java</artifactId>     
    <version>5.1.15</version>                         
    <scope>provided</scope>                           
</dependency>

Зависимость MySQL необходима только для возможности запуска сценариев Liquibase с Maven. Само приложение использует источник данных, который управляется контейнером, в данном случае JBoss. Чтобы иметь возможность запускать сценарии Liquibase с Maven, я определил плагин так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<plugin>                                                                      
    <groupId>org.liquibase</groupId>                                          
    <artifactId>liquibase-maven-plugin</artifactId>                           
    <version>2.0.1</version>                                                  
    <configuration>                                                           
        <changeLogFile>db.changelogs/my-changelog-master.xml</changeLogFile>  
        <driver>com.mysql.jdbc.Driver</driver>                                
        <url>jdbc:mysql://localhost:3306/MyDB</url>                           
        <username>admin-user</username>                                             
        <password>*****</password>                                             
    </configuration>                                                          
    <executions>                                                              
        <execution>                                                           
            <goals>                                                           
                <goal>update</goal>                                           
            </goals>                                                          
        </execution>                                                          
    </executions>                                                             
</plugin>

Как я уже сказал, я выполняю сценарии, вызывая Liquibase с помощью Servlet Listener. Для этого добавьте следующие элементы в файл «web.xml» внутри файла WAR:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
...
    <context-param>
        <param-name>liquibase.changelog</param-name>
        <param-value>db.changelogs/db.changelog-master.xml</param-value>
    </context-param>
    <context-param>
        <param-name>liquibase.datasource</param-name>
        <param-value>java:jboss/datasources/LiquibaseDS</param-value>
    </context-param>
 
    <listener>
        <listener-class>liquibase.integration.servlet.LiquibaseServletListener</listener-class>
    </listener>
...

Здесь мы видим, что я определил два параметра:

    • liquibase.changelog

Этот параметр относится к местоположению classpath главного файла db.changelogs. Важно отметить, что этот путь совпадает с тем, который используется в плагине Maven, если вы работаете с той же базой данных. Посмотрите эту проблему, с которой я столкнулся при использовании разных путей.

    • liquibase.datasource

Это относится к JNDI-имени источника данных, который будет использоваться со сценарием Liquibase. Поскольку этому пользователю базы данных необходимы права на создание объектов базы данных, таких как таблицы, я предлагаю создать отдельный источник данных для использования с приложением. Этот источник данных должен затем соединиться с пользователем, который имеет права только на доступ к данным и не может создавать или удалять объекты.

Последняя часть — это создание файлов db.changelogs. Как рекомендуется здесь, в качестве лучшей практики я обращаюсь к «основному» файлу изменений в конфигурации. В этом главном файле журнала изменений я имею в виду отдельные сценарии. Например, у меня в папке «resources» есть следующий «главный» скрипт:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
    <include file="db.changelog-1.0.xml" relativeToChangelogFile="true" />
    <include file="db.changelog-2.0.xml" relativeToChangelogFile="true" />
</databaseChangeLog>

В сценарии ‘db.changelog-1.0.xml’ я создаю несколько таблиц с набором изменений на основе XML:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    <changeSet id="1" author="pascalalma">
        <createTable tableName="TEST_TABLE">
            <column name="ID" type="bigint(20)" autoIncrement="true">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="CODE" type="varchar(20)">
                <constraints nullable="false" unique="true"/>
            </column>
            <column name="DESCRIPTION" type="varchar(200)"/>
            <column name="VALUE" type="varchar(200)"/>
        </createTable>
        <addUniqueConstraint columnNames="CODE,DESCRIPTION"
                             constraintName="CODE_UK"
                             tableName="TEST_TABLE"/>
        <createTable tableName="ANOTHER_TEST_TABLE">
            <column name="ID" type="bigint(20)" autoIncrement="true">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="CODE" type="varchar(20)">
                <constraints nullable="false" unique="true"/>
            </column>
            <column name="TEST_CODE" type="bigint(20)">
            </column>
        </createTable>
        <addForeignKeyConstraint baseColumnNames="TEST_CODE"
                                 baseTableName="ANOTHER_TEST_TABLE"
                                 constraintName="TEST_OTHER_FK"
                                 referencedColumnNames="CODE"
                                 referencedTableName="TEST_TABLE"/>
    </changeSet>
</databaseChangeLog>

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

В ‘db.changelog-2.0.xml’ я показываю, как загрузить данные в созданные таблицы, используя в качестве входных данных файл CSV:

01
02
03
04
05
06
07
08
09
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    <changeSet id="2" author="pascalalma">
    <loadData file="db.changelogs/mydata.csv"
              schemaName="MyDB"
              quotchar="'"
              tableName="TEST_TABLE"/>
    </changeSet>
</databaseChangeLog>

В «mydata.csv» я определяю строки для загрузки в таблицу:

1
2
3
4
CODE,DESCRIPTION,VALUE
'T01','Not specified','FOO'
'T02','Wrongly specified','BAR'
'T03','Correct','FOO-BAR'

В первой строке CSV-файла указаны столбцы в порядке, в котором поля определены в других строках. Это очень удобный способ загрузки исходных данных в таблицы базы данных. Когда все это на месте, вы можете запустить Liquibase из Maven. Сначала соберите проект с помощью mvn clean install, а затем введите mvn liquibase: update:

Экран-выстрел-2013-06-25-на-08-37-55

Теперь, если мы посмотрим на результат в схеме базы данных, мы увидим следующие таблицы:

Экран-выстрел-2013-06-25-на-08-39-11

Мы видим, что наши таблицы с данными создаются. Мы также видим еще две таблицы: «DATABASECHANGELOG» и «DATABASECHANGELOGLOCK». Они используются Liquibase для определения состояния базы данных. Если я разверну файл WAR с источником данных в этой базе данных, Liquibase увидит, что сценарии уже запущены, и не выполнит их снова. Однако, если я удалю таблицы, сценарии будут выполнены так же, как и для новой базы данных. Как видите, это может упростить развертывание новых выпусков вашего программного обеспечения, по крайней мере, части базы данных.