Привет, в этом посте я покажу вам, как легко вы можете объединить мощь Gradle с Flyway и JOOQ. В конце у вас будет рецепт сборки, который автоматически обновляет модели при каждом обновлении базы данных.
Эта проблема
При разработке приложений с доступом к базе данных мы обычно сталкиваемся с проблемой: нам нужно адаптировать наш код к изменениям в схеме базы данных или наоборот.
Поскольку мы стараемся не повторять что-то, отличной идеей будет то, что мы можем создать одно из обоих из другого. Например, генерация доступа к базе данных и перенос объектов из фактической схемы базы данных. Поскольку база данных должна быть источником правды для данных схемы, пример выглядит как хороший путь. Но об этом позже.
Другая проблема заключается в соединении с базой данных для генерации классов из схемы. Создание схемы на лету до того, как класс начнет работать, похоже на привлекательную идею. Это освободило бы разработчика от необходимости иметь соединение с базой данных, когда он восстанавливает классы доступа. Это также было бы удобно в том смысле, что он может легко загрузить окружение на своей локальной машине.
Давайте посмотрим, как мы можем приступить к этим требованиям / задачам.
Инструменты
Решением для второй задачи может быть Flyway , поскольку это умный инструмент для управления и выполнения обновлений схемы для многих реляционных баз данных. Цикл разработки с Flyway (очень кратко) описывается следующим образом: определите изменение схемы в файле SQL, загрузите приложение, и flyway позаботится об этом.
Нет, нам просто нужен генератор от данных схемы до объектов доступа / передачи данных. Я нахожу здесь JOOQ очень удобным, так как это библиотека, которая предоставляет оболочку для доступа к реляционной базе данных через мощный DSL, где SQL-код виден в вашем коде и не скрыт за каким-либо OR-mapper.
JOOQ приходит вместе с необходимым генератором кода, так что нам не нужно писать и изменять неприятный и иногда громоздкий код снова и снова.
Теперь, когда мы сделали выбор технологий / библиотек, давайте перейдем к
Gradle интеграция
Я представлю новое задание Gradle generateJOOQ. Он позаботится о переносе / создании схемы с помощью Flyway и генерации кода с помощью JOOQ и должен выполняться перед каждым запуском compileJava .
Начнем с необходимых конфигураций и исходных наборов. Классы будут сгенерированы в сгенерированный исходный набор, который требует наличия следующего фрагмента в файле сборки:
|
01
02
03
04
05
06
07
08
09
10
|
configurations { compile.extendsFrom generatedCompile} sourceSets { generated main { compileClasspath += generated.output }} |
Следующим шагом теперь является информирование Gradle о Flyway и JOOQ путем добавления необходимых зависимостей в блок buildscript нашего build.gradle.
|
01
02
03
04
05
06
07
08
09
10
11
|
buildscript { repositories { mavenCentral() } dependencies { classpath 'org.jooq:jooq-codegen:3.6.1' classpath 'com.h2database:h2:1.4.188' classpath 'org.flywaydb:flyway-core:3.2.1' }} |
Для этого примера выбрана база данных H2, но вы можете использовать любой драйвер базы данных, который можно использовать вместе с Flyway и JOOQ.
Теперь мы должны предоставить конфигурацию для JOOQ, чтобы он мог генерировать классы в нужную папку и путь к классам
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
def writer = new StringWriter()new groovy.xml.MarkupBuilder(writer) jdbc() { driver('org.h2.Driver') url("jdbc:h2:file:${project.projectDir}/build/generator/${project.name}") user('sa') password('') } generator() { database() { } generate() { } target() { packageName('com.coderskitchen.example') directory('src/generated/java') } }} |
В качестве следующего шага мы можем создать задачу generateJOOQ :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
import org.flywaydb.core.Flyway task generateJOOQ() { doLast { def Flyway flyway = new Flyway(); flyway.setDataSource("jdbc:h2:file:${project.projectDir}/build/generator/${project.name}", "sa", null); flyway.setLocations("filesystem:${project.projectDir}/src/main/resources/db/migration") flyway.migrate(); org.jooq.util.GenerationTool.generate( javax.xml.bind.JAXB.unmarshal(new StringReader(writer.toString()), org.jooq.util.jaxb.Configuration.class) ) }} |
Как видите, файлы миграции взяты из src / main / resources / db /igration . Это имеет то преимущество, что, если нам нравится / нужно, может доставлять файлы SQL переноса в нашем приложении, так что мы можем переносить базу данных непосредственно в рабочую / промежуточную среду. Это очень полезно, если среда не находится под нашим контролем, например, когда приложение работает на компьютерах внешних клиентов.
Зависимости между задачами Gradle будут следующие: compileJava -> compileGeneratedJava -> generateJOOQ && clean :
|
1
2
3
|
compileGeneratedJava.dependsOn cleancompileGeneratedJava.dependsOn generateJOOQcompileJava.dependsOn compileGeneratedJava |
Наконец, мы должны добавить зависимости JOOQ к зависимостям в сгенерированную конфигурацию Compile, иначе наш проект не будет построен:
|
1
2
3
4
5
|
dependencies { generatedCompile 'org.jooq:jooq:3.6.1', 'org.jooq:jooq-meta:3.6.1', 'org.jooq:jooq-codegen:3.6.1'} |
Весь минимальный файл сборки выглядит так
|
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
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
|
buildscript { repositories { mavenCentral() } dependencies { classpath 'org.jooq:jooq-codegen:3.6.1' classpath 'com.h2database:h2:1.4.188' classpath 'org.flywaydb:flyway-core:3.2.1' }} import org.flywaydb.core.Flyway group 'com.coderskitchen'version '1.0.0' apply plugin: 'java'apply plugin: 'idea' sourceCompatibility = 1.8 repositories { mavenCentral()} configurations { compile.extendsFrom generatedCompile} sourceSets { generated main { compileClasspath += generated.output }} dependencies { generatedCompile 'org.jooq:jooq:3.6.1', 'org.jooq:jooq-meta:3.6.1', 'org.jooq:jooq-codegen:3.6.1' testCompile group: 'junit', name: 'junit', version: '4.11'} def writer = new StringWriter()new groovy.xml.MarkupBuilder(writer) jdbc() { driver('org.h2.Driver') url("jdbc:h2:file:${project.projectDir}/build/generator/${project.name}") user('sa') password('') } generator() { database() { } generate() { } target() { packageName('com.coderskitchen.example') directory('src/generated/java') } }} task generateJOOQ() { doLast { def Flyway flyway = new Flyway(); flyway.setDataSource("jdbc:h2:file:${project.projectDir}/build/generator/${project.name}", "sa", null); flyway.setLocations("filesystem:${project.projectDir}/src/main/resources/db/migration") flyway.migrate(); org.jooq.util.GenerationTool.generate( javax.xml.bind.JAXB.unmarshal(new StringReader(writer.toString()), org.jooq.util.jaxb.Configuration.class) ) }} compileGeneratedJava.dependsOn cleancompileGeneratedJava.dependsOn generateJOOQcompileJava.dependsOn compileGeneratedJava |
Наконец у нас есть все вместе, чтобы попробовать это. Все? С точки зрения настройки, это правильно, просто отсутствует какой-то пример.
Бегущий пример
Пример о простом календаре парусных гонок, написанном в две итерации
- первый будет содержать таблицу с датами гонки со всей информацией о месте
- вторая итерация перенесет эту схему в более нормализованную, где место отделено от даты
Я опубликовал пример на github ( https://github.com/coders-kitchen/gradle-jooq-flyway-example ) и у меня есть два тега, соответствующих упомянутым итерациям.
До свидания и хорошей недели
| Ссылка: | Объединяя возможности Gradle, Flyway и JOOQ для беспроблемной разработки с базами данных от нашего партнера JCG Питера Даума в блоге Coders Kitchen . |