Flyway — подключение, настройка библиотеки и плагина flyway-maven-plugin.
Базовый обзор библиотеки Flyway. Подключение к проекту, настройка библиотеки и использование плагина flyway-maven-plugin.
Используемые технологии и библиотеки
- Flyway-maven-plugin 4.0.3
- Oracle 11g XE
1. Описание задачи
Научиться использовать flyway-maven-plugin в java проекте. Прописать необходимые maven зависимости в pom.xml. Описать базовые соглашения при работе с библиотекой Flyway.
2. Структура проекта
Для базовой настройки flyway нам необходима только папка resources. Внутри находится скрипт создания пользователя (схемы) для базы данных Oracle 11g XE (create_user_tablespace.sql), базовый скрипт создания таблиц в схеме (V0_0_00__create_tables.sql), а так же пример вставки данных в эти таблицы (V0_0_01__insert_init_data.sql). Обратите внимание на папку target. Важно, чтобы после сборки она выглядела похожим образом (будет подробнее описано ниже).
3. pom.xml
Библиотека поддерживает различные способы взаимодействия flyway и базы данных. В этой статье мы рассмотрим очень легкий в использовании способ — использование плагина flyway-maven-plugin. Итак в начале полный пример pom.xml.
pom.xml:
1 2 3 4 5 6 7 8 9 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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>ru.javastudy</groupId> <artifactId>js-flyway</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jdk.version>1.8</jdk.version> <ojdbc6.version>12.1.0.2</ojdbc6.version> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>${jdk.version}</source> <target>${jdk.version}</target> </configuration> </plugin> <plugin> <groupId>org.flywaydb</groupId> <artifactId>flyway-maven-plugin</artifactId> <version>4.0.3</version> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <id>copy-resources</id> <phase>validate</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <encoding>UTF-8</encoding> <!--classpath:db/migration is target/classes/db/migration--> <outputDirectory>${project.build.outputDirectory}</outputDirectory> <resources> <resource> <directory>src/main/resources/</directory> <filtering>true</filtering> </resource> </resources> </configuration> </execution> </executions> </plugin> </plugins> <!--Instructing the resources plugin to filter certain directories--> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build> <profiles> <profile> <id>js-flyway</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <!--common properties for database connection--> <db.driver>oracle.jdbc.driver.OracleDriver</db.driver> <db.url>jdbc:oracle:thin:@localhost:1521:XE</db.url> <db.username>js</db.username> <db.password>jsadmin</db.password> <db.schema>JS</db.schema> <db.adm.user>admin</db.adm.user> <db.adm.pass>123456</db.adm.pass> <!-- Properties are prefixed with flyway. --> <flyway.user>${db.adm.user}</flyway.user> <flyway.password>${db.adm.pass}</flyway.password> <flyway.schemas>${db.schema}</flyway.schemas> <flyway.driver>${db.driver}</flyway.driver> <flyway.url>${db.url}</flyway.url> <flyway.locations>db/migration/init/</flyway.locations> </properties> </profile> </profiles> <dependencies> <!--need for flyway plugin--> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>${ojdbc6.version}</version> </dependency> </dependencies> <!-- for oracle ojdbc --> <repositories> <repository> <id>codelds</id> <url>https://code.lds.org/nexus/content/groups/main-repo</url> </repository> </repositories> </project> |
На первый взгляд xml файл может показаться большим. На самом деле большая часть настроек для простого запуска не нужна и достаточно использовать настройки maven по умолчанию. Но похожие настройки используются в других статьях на сайте и поэтому разберем их поподробнее.
1 2 3 4 5 6 7 8 9 |
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>${jdk.version}</source> <target>${jdk.version}</target> </configuration> </plugin> |
Переопределенные настройки компиляции проекта, чтобы всегда использовалась нужная версия jdk. Не является необходимым для нашей задачи.
1 2 3 4 5 |
<plugin> <groupId>org.flywaydb</groupId> <artifactId>flyway-maven-plugin</artifactId> <version>4.0.3</version> </plugin> |
Собственно сам плагин flyway. Поддерживает множество настроек конфигурации. Автор вынес их в профиль, который будет описан ниже.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <id>copy-resources</id> <phase>validate</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <encoding>UTF-8</encoding> <!--classpath:db/migration is target/classes/db/migration--> <outputDirectory>${project.build.outputDirectory}</outputDirectory> <resources> <resource> <directory>src/main/resources/</directory> <filtering>true</filtering> </resource> </resources> </configuration> </execution> </executions> </plugin> |
Настройка плагина работы с ресурсами. Если не использовать профили, многомодульную структуру проекта, то эта часть не обязательная. По умолчанию всё и так скопируется и подхватится maven. После описания других настроек мы вернемся к этой части и поясним зачем это было добавлено.
1 2 3 4 5 6 |
<resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> |
Указывает путь фильтрации ресурсов maven-resource-plugin. Пояснение будет ниже.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<profiles> <profile> <id>js-flyway</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <!--common properties for database connection--> <db.driver>oracle.jdbc.driver.OracleDriver</db.driver> <db.url>jdbc:oracle:thin:@localhost:1521:XE</db.url> <db.username>js</db.username> <db.password>jsadmin</db.password> <db.schema>JS</db.schema> <db.adm.user>admin</db.adm.user> <db.adm.pass>123456</db.adm.pass> <!-- Properties are prefixed with flyway. --> <flyway.user>${db.adm.user}</flyway.user> <flyway.password>${db.adm.pass}</flyway.password> <flyway.schemas>${db.schema}</flyway.schemas> <flyway.driver>${db.driver}</flyway.driver> <flyway.url>${db.url}</flyway.url> <flyway.locations>db/migration/init/</flyway.locations> </properties> </profile> </profiles> |
В проекте используется один профиль, который включен по умолчанию. Вот здесь и записаны все необходимые настройки для flyway плагина. По соглашению они должны начинаться с <flyway.**>. Настройки являются обязательными (они могут быть перенесены непосредственно в описание плагина выше). Здесь используется двойная конструкция в виде определения сначала свойств <db.**>, а потом вставка их в <flyway.**>${db.**}></flyway.**>. Можно сразу же вставить ссылку на БД, имя пользователя и т.п. в теги флайвей. Конструкция с отдельными свойствами для БД (<db.**>) удобна в случае повторения этих данных в разных частях pom.xml и внешних настройках вроде datasource.properties.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<dependencies> <!--need for flyway plugin--> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>${ojdbc6.version}</version> </dependency> </dependencies> <!-- for oracle ojdbc --> <repositories> <repository> <id>codelds</id> <url>https://code.lds.org/nexus/content/groups/main-repo</url> </repository> </repositories> |
В проекте используется база данных от Oracle, которая требует указания драйвера для подключения. Т.к. драйвер отсутствует в центральном репозитории, то здесь указан дополнительный, откуда и будет скачан драйвер ojdbc6 (версия указана вначале pom.xml).
Теперь поясним зачем нужен resource plugin и дополнительные настройки. В структуре проекта вы могли заметить файл datasource.properties. В нем не указаны в явном виде настройки подключения к бд, а прописано следующее:
1 2 3 4 |
db.driver=${db.driver} db.url=${db.url} db.username=${db.username} db.password=${db.password} |
Как видите это те же самые свойства, что были указаны в профиле. Такая запись очень удобна, если над проектом работает много разработчиков и имеется несколько разных БД. Достаточно просто поменять профиль (у каждого разработчика он свой) и проект будет собираться с нужным url, username, password и др. Вот здесь и вступает в игру maven-resource-plugin.
- В фазе validate (сразу после clean) плагин произведет фильтрацию в файлах <directory>src/main/resources</directory>. В нашем случае будет найден файл datasource.properties и в нем будет изменено db.username=${db.username} на db.username=js (из настроек в профиле);
- Файлы будут скопированы в путь <outputDirectory>${project.build.outputDirectory}</outputDirectory>. В нашем случае это target/classes.
4. Sql скрипты
Отдельно вынесу описание под кат используемых скриптов.
create_user_tablespace.sql:
1 2 3 4 5 6 7 8 9 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 37 38 39 40 41 42 43 44 45 |
--select username from dba_users; --select tablespace_name from dba_tablespaces; --select * from v$datafile; --DROP TABLESPACE jss_tablespace including contents and datafiles cascade constraints;/*if need to drop*/ --CREATE ROLE with admin grants; create role ADMINDBA; -- grant DBA,Connect and Resource permission to this role (not sure this is necessary if you give admin option) GRANT CONNECT,RESOURCE, DBA to ADMINDBA; --Give admin option to user. A newly created user cannot connect to the database until --granted the CREATE SESSION system privilege. GRANT CREATE SESSION TO ADMINDBA WITH ADMIN OPTION; GRANT ALL PRIVILEGES TO ADMINDBA IDENTIFIED BY ADMIN; -- grant all privileges to <roleName> GRANT unlimited tablespace to ADMINDBA;-- give unlimited tablespace grant --CREATE ADMIN USER FOR MANAGEMENT APPLICATION USER CREATE USER ADMIN IDENTIFIED BY '123456'; -- create user with password --GRANTS GRANT ADMINDBA to ADMIN; --CREATE USER FOR APPLICATION (with restricted grants) --CREATE TABLESPACE create tablespace js_tablespace datafile 'js_tablespace.dat' size 10M autoextend on; create temporary tablespace js_tablespace_temp tempfile 'js_tablespace_temp.dat' size 5M autoextend on; --CREATE ROLE with basics grants; create role SIMPLEDBA; grant unlimited tablespace to SIMPLEDBA; grant create session to SIMPLEDBA; grant create table to SIMPLEDBA; grant create any sequence to SIMPLEDBA; --CREATE USER create user JS identified by jsadmin default tablespace js_tablespace temporary tablespace js_tablespace_temp; --GRANTS GRANT SIMPLEDBA to JS; GRANT unlimited tablespace to JS; |
V0_0_00__create_tables.sql:
1 2 3 4 5 6 7 8 9 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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
PURGE RECYCLEBIN; -- -- category -- CREATE TABLE category ( id varchar2(9) NOT NULL, name varchar2(30) NOT NULL, parent_id varchar2(9) default NULL, PRIMARY KEY (id), CONSTRAINT category_parent_fk FOREIGN KEY (parent_id) REFERENCES category (id) ); COMMENT ON TABLE category IS 'Hierarchy of product categories'; CREATE INDEX cat_parent_fki ON category (parent_id); -- -- product -- CREATE TABLE product ( ean_code varchar2(13) NOT NULL, name varchar2(30) NOT NULL, category_id varchar2(9) NOT NULL, price number(8,2) NOT NULL, manufacturer varchar2(30) NOT NULL, notes varchar2(256) NULL, description clob NULL, image blob NULL, PRIMARY KEY (ean_code), CONSTRAINT product_category_fk FOREIGN KEY (category_id) REFERENCES category (id) ); COMMENT ON TABLE product IS 'products'; CREATE INDEX product_category_fki ON product (category_id); -- -- user -- CREATE TABLE users ( id number(10) NOT NULL, name varchar2(30) NOT NULL, email varchar2(100) NOT NULL, password varchar2(16) NOT NULL, active number(1) default 1 NOT NULL, description varchar2(100) NULL, version number(10) DEFAULT 0, PRIMARY KEY (id), constraint active_flag check (active in (0,1)) ); COMMENT ON TABLE users IS 'shop users'; -- -- role -- CREATE TABLE role ( id number(10) NOT NULL, name varchar2(16) NOT NULL, user_id number(10) NOT NULL, description varchar2(100) NULL, PRIMARY KEY (id), CONSTRAINT users_role_fk FOREIGN KEY (user_id) REFERENCES users (id) ); COMMENT ON TABLE role IS 'roles of the shop users'; CREATE INDEX users_role_fki on role (user_id); -- -- customer -- CREATE TABLE customer ( id number(10) default 0 NOT NULL, category char(1) NOT NULL, salutation varchar2(10) NULL, first_name varchar2(30) NOT NULL, last_name varchar2(30) NOT NULL, birth_date date, PRIMARY KEY (id), CONSTRAINT customer_user_fk FOREIGN KEY (id) REFERENCES users (id) ); COMMENT ON TABLE customer IS 'users of type ''customer'''; CREATE INDEX customer_name on customer (first_name, last_name); -- -- order -- CREATE TABLE orders ( id number(10) NOT NULL, customer_id number(10) NOT NULL, total_price number(8,2) NOT NULL, created_at timestamp with time zone NOT NULL, PRIMARY KEY (id), CONSTRAINT order_customer_fk FOREIGN KEY (customer_id) REFERENCES customer (id) ); COMMENT ON TABLE orders IS 'product orders'; CREATE INDEX order_customer_fki on orders (customer_id); -- -- order_item -- CREATE TABLE order_item ( id number(10) NOT NULL, order_id number(10) NOT NULL, number_of_items number(10) default 1 NOT NULL, product_ean_code varchar2(13) NOT NULL, total_price number(8,2) NOT NULL, PRIMARY KEY (id), CONSTRAINT order_item_order_fk FOREIGN KEY (order_id) REFERENCES orders (id), CONSTRAINT order_item_product_fk FOREIGN KEY (product_ean_code) REFERENCES product (ean_code) ); COMMENT ON TABLE order_item IS 'order items'; CREATE INDEX order_item_order_fki ON order_item (order_id); CREATE INDEX order_item_product_fki ON order_item (product_ean_code); -- -- sequence -- CREATE SEQUENCE seq_id_gen START WITH 1; CREATE SEQUENCE users_seq START WITH 1 INCREMENT BY 1 NOMAXVALUE; --TRIGGERS CREATE OR REPLACE TRIGGER users_id_trg BEFORE INSERT ON USERS FOR EACH ROW BEGIN if :new.ID is NULL THEN SELECT users_seq.nextval into :new.id FROM dual; END IF; END; / |
V0_0_01__insert_init_data.sql:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
INSERT INTO USERS (name, email, password, active, description) VALUES ('admin','admin@jsshop.ru', '1', '1', 'admin user'); INSERT INTO USERS (NAME, EMAIL, PASSWORD, ACTIVE, DESCRIPTION) VALUES ('user', 'user@jsr.ru', '234', 1, 'simple user'); INSERT INTO USERS (NAME, EMAIL, PASSWORD, ACTIVE, DESCRIPTION) VALUES ('customer', 'costumer@mail.ru', '32999', 1, 'customer 1'); INSERT INTO USERS (NAME, EMAIL, PASSWORD, ACTIVE, DESCRIPTION) VALUES ('Lenchik Pirozhkov', 'pirozhok@povidlo.ru', '#lskdj$JDKL', 1, 'vkusnyashka'); INSERT INTO ROLE (ID, NAME, USER_ID, description) VALUES (1, 'Administrator', (select id from users where name = 'admin'), 'Administrator role'); INSERT INTO ROLE (ID, NAME, USER_ID, description) VALUES (2, 'User', (select id from users where name = 'user'), 'User role'); INSERT INTO ROLE (ID, NAME, USER_ID, description) VALUES (3, 'Customer', (select id from users where name = 'customer'), 'Customer role'); INSERT INTO ROLE (ID, NAME, USER_ID, description) VALUES (4, 'Customer', (select id from users where name = 'Lenchik Pirozhkov'), 'Customer role'); |
5. Описание работы с Flyway
Для начала работы необходимо создать схему (не таблицы — это мы сделаем автоматически!) в нашей базе данных. Эта работа не входит в тему статьи, но вы можете просто воспользоваться скриптом create_user_tablespace.sql, который создаст двух пользователей и выдаст им права. Почему двух? Хорошей практикой является максимальное ограничение прав для пользователя приложения (в нашем случае пользователь с именем JS). В тоже время нужен какой-то администратор, который сможет изменять эту схему. Для этого используется пользователь с именем ADMIN.
В настройках профиля вы можете заметить, что схема указана для приложения (JS), но выполняться flyway будет из-под пользователя ADMIN, т.к. пользователь приложения не обладает практически никакими правами.
Как работает Flyway? При запуске плагина он будет искать все скрипты по пути db/migration.
По соглашению файлы должны именоваться следующим образом.
Имя содержит:
- префикс: Настраиваемый, по умолчанию:
V
для версионных миграций,R
для повторяющихся - версия: (Только для версионируемых миграций) Можно прописывать сколько угодно цифр
- разделитель: Настраиваемый, по умолчанию:
__
(два подчеркивания) - описание: слова через подчеркивание
- суффикс: Настраиваемый, по умолчанию:
.sql
При миграции данных скрипты будут выполняться последовательно.
6. Использование плагина Flyway для maven
Прежде чем запускать плагин необходимо собрать проект.
Обратите внимание, что при сборке подтянулись настройки из профиля.
Выполним команду flyway:clean.
В консоль будет выведена информация:
Проверим подключение к БД и что внутри (как подключиться к базе Oracle из IDEA описано в отдельной статье):
Теперь самое интересное. Запустим команду flyway:migrate:
Обратите внимание на вывод в консоль:
А теперь проверим нашу базу данных!
Вот так просто мы получили все данные из двух скриптов. Если до этого вы каждый раз проливали данные в базу вручную, то уверен вы перестанете так делать:).
Flyway по умолчанию проверяет чексуммы и версии в базе. Если вы хоть чуть-чуть измените один из sql файлов, то будет выдана ошибка. Эти данные находятся в таблице schema_version.
Может быть интересно
Databene Benerator — автоматическая генерация тестовых данных