Шаг за шагом мы идем к нашей цели. "Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 1Сегодня нам нужно решить выполнить много задач:
  1. Добавить Flyway. Здесь же настроим развертывание БД для проекта.
  2. Добавить схему БД.
  3. Добавить слой репозитория.
  4. Добавить команду для статистики бота.
  5. Написать тесты, в конце концов.
Поэтому садитесь поудобнее: будет лонгрид. Мы уже научились деплоить наше приложение с помощью docker и docker-compose. Это было нелегко, и тех, у кого это получилось сделать, поздравляю. Если будут какие-то вопросы по реализации — можете посмотреть на код в этом пулл-реквесте.

Добавляем Flyway

Чтобы добавить Flyway, нужно иметь базу данных, которую мы добавим как еще один service в docker-compose. В прошлый раз я говорил, что в docker-compose есть секция services, которая как раз и отвечает за добавление и управление некой инфраструктурой — то есть, набором приложений/баз данных/мессенджеров и тд.
Я уже говорил о том, что такое Flyway и как его добавить в SpringBoot приложение. Советую освежить информацию в памяти перед прочтением этой статьи. Также можно почитать README.md файл проекта с демонстрацией статьи о Flyway + SpringBoot.
Первое, что мы делаем — обновляем нашу main ветку в проекте (git pull) и создаем новую с названием нашей задачи — STEP_5_JRTB-1 (git checkout -b STEP_5_JRTB-1). Напомню: мы переименовали ветки, чтобы отсортировать последовательность выполнения задач в проекте можно было только по названию. Это позволит быстро переходить от одного шага к другому. Теперь можно спокойно работать. Нам нужно добавить три зависимости:

<dependency>
  <groupId>org.flywaydb</groupId>
  <artifactId>flyway-core</artifactId>
</dependency>

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Первая зависимость отвечает за добавление в проект FlyWay, вторая добавляет нам возможность подключаться к MySQL бд. Третья — SpringBoot starter, который запускает всю магию использования БД в Spring. И еще добавим ломбок, чтобы не писать геттеры и сеттеры:

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
</dependency>
Теперь нам нужна база данных для разработки. Мы будем создавать ее пока что (пока я лучше не придумал) как отдельный docker-compose файл и отдельный файл для application.properties. Есть такая вещь в разработке (и в Spring экосистеме в частности) как профили. В зависимости от профиля запускаются разные настройки для работы. Думаю, в будущем мы объединим docker-compose в один файл и сделаем так, чтобы для разработки запускалась только одна база данных на тех настройках, которые нам нужны. На данный момент сделаем так:
  1. Создадим файл в корне проекта docker-compose-test.yml, в котором будет запускаться только база данных с собственными настройками.
  2. Создадим файл application-test.properties с настройками, которые соответствуют настройкам в docker-compose-test.yml файле.
Для работы с MySQL я взял официальный образ из DockerHub:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 2Быстро пролистав, нашел нужные переменные: MYSQL_DATABASE — имя БД MYSQL_USER — имя пользователя БД MYSQL_PASSWORD — пароль пользователя БД И то, как их нужно правильно записать:

jrtb-db:
   image: mysql
   restart: always
   container_name: dev-jrtb-db
   ports:
     - "3306:3306"
   environment:
     MYSQL_DATABASE: "dev_jrtb_db"
     MYSQL_USER: root
     MYSQL_PASSWORD: root
После этого создаю в корне проекта docker-compose-test.yml:

version: '3.1'

services:
 jrtb-db:
   image: mysql
   restart: always
   container_name: dev-jrtb-db
   ports:
     - "3306:3306"
   environment:
     MYSQL_DATABASE: "dev_jrtb_db"
     MYSQL_USER: root
     MYSQL_PASSWORD: root
Чтобы запустить именно этот файл для работы с docker-compose, нужно использовать в запросе флаг -f. Он означает, что будет предоставлено имя файла, который запускается, а не взят стандартный docker-compose.yml. Поэтому запрос будет выглядеть так: docker-compose -f docker-compose-test.yml up -d Получается, вначале мы говорим, какой взять файл (-f docker-compose-test.yml), а потом говорим, что нужно поднять сервисы и сделать это в daemon режиме (up -d) — то есть так, чтобы процесс работал вне сессии терминала, в котором эту команду запустили. Запускаем и пробуем подключиться к БД, чтобы удостовериться, что она поднялась и ею можно пользоваться. Для этого открываем знакомый нам MySQL Workbench (вот здесь мы говорили об этом) и создаем новое соединение:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 3Нам нужно заполнить следующие поля:
  • Connection name — имя соединения — как вариант, DEV_JRTB_DB;
  • Hostname — здесь адрес, на котором развернута БД. В нашем случае все останется, как и было: 127.0.0.1, ну или localhost;
  • username — здесь будет root, как у нас записано в docker-compose;
  • password — также root.
После этого нужно проверить, будет ли соединение. Для этого нажимаем Test Connection:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 4В итоге получаем: Failed to Connect to MySQL… Почему? Заходим в терминал и смотрим, что произошло с докер-контейнером. Напишем в терминале:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 5Видим, что терминал создан 59 минут назад, а перезапуск был 55 секунд... почему? Непонятно, нужно смотреть логи. Заходим в терминале в корень проекта и запустить следующую команду: docker-compose -f docker-compose-test.yml logs
Получаем:

dev-jrtb-db | 2021-02-28 10:14:28+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:28+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' dev-jrtb-db | 2021-02-28 10:14:28+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:28+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified dev-jrtb-db | You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD dev-jrtb-db | 2021-02-28 10:14:29+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:29+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' dev-jrtb-db | 2021-02-28 10:14:29+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:29+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified dev-jrtb-db | You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD dev-jrtb-db | 2021-02-28 10:14:29+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:29+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' dev-jrtb-db | 2021-02-28 10:14:29+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:29+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified dev-jrtb-db | You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD dev-jrtb-db | 2021-02-28 10:14:30+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:30+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' dev-jrtb-db | 2021-02-28 10:14:30+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:30+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified dev-jrtb-db | You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD dev-jrtb-db | 2021-02-28 10:14:32+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:32+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' dev-jrtb-db | 2021-02-28 10:14:32+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:32+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified dev-jrtb-db | You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD dev-jrtb-db | 2021-02-28 10:14:34+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:34+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' dev-jrtb-db | 2021-02-28 10:14:34+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:34+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified dev-jrtb-db | You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD dev-jrtb-db | 2021-02-28 10:14:37+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:37+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' dev-jrtb-db | 2021-02-28 10:14:37+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:37+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified dev-jrtb-db | You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD dev-jrtb-db | 2021-02-28 10:14:44+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:44+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' dev-jrtb-db | 2021-02-28 10:14:44+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:44+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified dev-jrtb-db | You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD dev-jrtb-db | 2021-02-28 10:14:58+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:58+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' dev-jrtb-db | 2021-02-28 10:14:58+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 10:14:58+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified dev-jrtb-db | You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD

Теперь понятно, что БД так и не запустилась с нашими настройками. Ошибка одна и та же, поэтому вычленим ее из листа:
2021-02-28 11:03:37+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified dev-jrtb-db | You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD
То есть ему нужно определить пароль для root пользователя — главного пользователя. Хорошо, сейчас мы обновим наш файл:

version: '3.1'

services:
 jrtb-db:
   image: mysql
   restart: always
   container_name: dev-jrtb-db
   ports:
     - "3306:3306"
   environment:
     MYSQL_DATABASE: "dev_jrtb_db"
     MYSQL_ROOT_PASSWORD: root
Мы убрали те переменные за пользователя и пароль и добавили только пароль для рутовского пользователя. Это значит, что теперь для пользователя root будет пароль root. По крайней мере, мы так предполагаем. Сейчас запустим и посмотрим, что будет: docker-compose -f docker-compose-test.yml up -d И сразу посмотрим, что говорят нам логи: docker-compose -f docker-compose-test.yml logs
Смотрим:

Attaching to dev-jrtb-db dev-jrtb-db | 2021-02-28 11:20:40+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 11:20:41+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' dev-jrtb-db | 2021-02-28 11:20:41+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started. dev-jrtb-db | 2021-02-28 11:20:41+00:00 [Note] [Entrypoint]: Initializing database files dev-jrtb-db | 2021-02-28T11:20:41.083803Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.23) initializing of server in progress as process 43 dev-jrtb-db | 2021-02-28T11:20:41.088457Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. dev-jrtb-db | 2021-02-28T11:20:41.447990Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. dev-jrtb-db | 2021-02-28T11:20:42.672093Z 6 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option. dev-jrtb-db | 2021-02-28 11:20:45+00:00 [Note] [Entrypoint]: Database files initialized dev-jrtb-db | 2021-02-28 11:20:45+00:00 [Note] [Entrypoint]: Starting temporary server dev-jrtb-db | 2021-02-28T11:20:45.893664Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.23) starting as process 88 dev-jrtb-db | 2021-02-28T11:20:45.921695Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. dev-jrtb-db | 2021-02-28T11:20:46.088339Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. dev-jrtb-db | 2021-02-28T11:20:46.207499Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock dev-jrtb-db | 2021-02-28T11:20:46.369170Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed. dev-jrtb-db | 2021-02-28T11:20:46.369357Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel. dev-jrtb-db | 2021-02-28T11:20:46.371501Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory. dev-jrtb-db | 2021-02-28T11:20:46.402227Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.23' socket: '/var/run/mysqld/mysqld.sock' port: 0 MySQL Community Server - GPL. dev-jrtb-db | 2021-02-28 11:20:46+00:00 [Note] [Entrypoint]: Temporary server started. dev-jrtb-db | Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it. dev-jrtb-db | Warning: Unable to load '/usr/share/zoneinfo/leap-seconds.list' as time zone. Skipping it. dev-jrtb-db | Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it. dev-jrtb-db | Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it. dev-jrtb-db | 2021-02-28 11:20:49+00:00 [Note] [Entrypoint]: Creating database dev_jrtb_db dev-jrtb-db | dev-jrtb-db | 2021-02-28 11:20:49+00:00 [Note] [Entrypoint]: Stopping temporary server dev-jrtb-db | 2021-02-28T11:20:49.187996Z 11 [System] [MY-013172] [Server] Received SHUTDOWN from user root. Shutting down mysqld (Version: 8.0.23). dev-jrtb-db | 2021-02-28T11:20:50.925075Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.23) MySQL Community Server - GPL. dev-jrtb-db | 2021-02-28 11:20:51+00:00 [Note] [Entrypoint]: Temporary server stopped dev-jrtb-db | dev-jrtb-db | 2021-02-28 11:20:51+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up. dev-jrtb-db | dev-jrtb-db | 2021-02-28T11:20:51.420058Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.23) starting as process 1 dev-jrtb-db | 2021-02-28T11:20:51.427384Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. dev-jrtb-db | 2021-02-28T11:20:51.577244Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. dev-jrtb-db | 2021-02-28T11:20:51.677659Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock dev-jrtb-db | 2021-02-28T11:20:51.787156Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed. dev-jrtb-db | 2021-02-28T11:20:51.787325Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel. dev-jrtb-db | 2021-02-28T11:20:51.789742Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory. dev-jrtb-db | 2021-02-28T11:20:51.812100Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.23' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.

Ошибок вроде уже нет, поэтому можно смотреть, будет ли доступ. Но, к сожалению, доступа опять нет… Ничего: будем дальше разбираться!"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 6Чтобы завершить работу docker-compose и удалить все контейнеры, нужно написать: docker-compose -f docker-compose-test.yml down Здесь мы указали файл, по которому нужно сделать удаление. Что делать? Будем гуглить: может, у кого-то получилось сделать лучше. Самый простой запрос: “example of mysql in docker-compose” помогает нам найти ссылку на medium ресурс. Там приводят такой пример:

version: '3.3'
services:
 db:
   image: mysql:5.7
   restart: always
   environment:
     MYSQL_DATABASE: 'db'
     # So you don't have to use root, but you can if you like
     MYSQL_USER: 'user'
     # You can use whatever password you like
     MYSQL_PASSWORD: 'password'
     # Password for root access
     MYSQL_ROOT_PASSWORD: 'password'
   ports:
     # <Port exposed> : < MySQL Port running inside container>
     - '3306:3306'
   expose:
     # Opens port 3306 on the container
     - '3306'
     # Where our data will be persisted
   volumes:
     - my-db:/var/lib/mysql
# Names our volume
volumes:
 my-db:
Адаптируем его под наши нужды и получаем:

version: '3.1'

services:
 jrtb-db:
   image: mysql:5.7
   restart: always
   environment:
     MYSQL_DATABASE: 'dev_jrtb_db'
     # So you don't have to use root, but you can if you like
     MYSQL_USER: 'dev_jrtb_db_user'
     # You can use whatever password you like
     MYSQL_PASSWORD: 'dev_jrtb_db_password'
     # Password for root access
     MYSQL_ROOT_PASSWORD: 'root'
   ports:
     # <Port exposed> : < MySQL Port running inside container>
     - '3306:3306'
   expose:
       # Opens port 3306 on the container
       - '3306'
Запускаем этот вариант уже известной нам командой: docker-compose -f docker-compose-test.yml up -d Пытаемся еще раз получить доступ к базе данных через MySQLWorkBench:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 7Наконец-то появился доступ. Но теперь же интересно, что повлияло на это. Я грешу на эту часть:

expose:
   # Opens port 3306 on the container
   - '3306'
Проверить легко: без этого просто ничего не должно работать :D. Проверим: удалим эту часть, перезапустим docker-compose: docker-compose -f docker-compose-test.yml down docker-compose -f docker-compose-test.yml up Пытаемся зайти в БД через workbench: все зашло. Тогда единственный вариант — это версия БД. Здесь четко указана 5.7, то есть, если поменять ее на последнюю, не должно работать. Перезапустил — и вправду не работает…"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 8Видимо, какие-то проблемы у них с докер образом на последней версии. Ну ладно, не беда. Такое часто случается с бесплатным программным обеспечением. Будем использовать версию 5.7. Посмотрим, есть ли база данных, которая нам нужна — dev_jrtb_db. Для этого запустим Query в MySQL Workbench команду SHOW DATABASES:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 9Да, в выводе она присутствует. Отлично, можно двигаться дальше. Далее добавим нашу первую миграцию. На основе задач из статьи "Планирование проекта: семь раз отмерь — один раз отрежь" создадим нашу первую таблицу tg_user. Находиться все миграции будут в папке projectDir/src/main/resources/db/migration, а имя будет такое: V00001__add_tg_user_table.sql. Почему именно в той папке и такое имя — описывал в статье про Spring + flyway. Вот как будет выглядеть наша первая миграция: V00001__add_tg_user_table.sql:

-- ensure that the table with this name is removed before creating a new one.
DROP TABLE IF EXISTS tg_user;

-- Create tg_user table
CREATE TABLE tg_user (
   chat_id VARCHAR(100),
   active BOOLEAN
);
То есть, здесь просто мы создаем таблицу пользователя, у которого есть только ID-шник чата (уникальный элемент, который может быть использован для работы как ID в таблице) и его состояние — активный или неактивный. Так, в случае остановки бота информация о подписке останется, и ее можно будет использовать, если ботом опять захотят воспользоваться. Другой информации о пользователе пока что не нужно. Теперь главный вопрос: а как это проверить? Нужно запустить наше приложение. Пока что в основном docker-compose.yml файле нет связи с БД, а еще нам нужно научиться отлаживать приложение вместе с базой данных. Поэтому запустим приложение с Intellij IDEA. Для проверки этого нам нужно будет запустить наш main метод в JavarushTelegramBotApplication. Чтобы разделить работу по деплою и тестированию, мы будем использовать другой файл с пропертями — application-test.properties. С помощью этого шаблона мы даем Spring’у понять, что этот файл будет использоваться для профиля test. Когда мы не указываем профиль, используется профиль по умолчанию (проверьте это у себя в логах при запуске SpringBoot: в первых строках об этом будет написано). Вот так он будет выглядеть:

# MySQL configurations:
spring.datasource.url=jdbc:mysql://localhost:3306/dev_jrtb_db
spring.datasource.username=dev_jrtb_db_user
spring.datasource.password=dev_jrtb_db_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# TelegramBot configurations:
bot.username=tes
bot.token=1375780501:AAE4A6Rz0BSnIGzeu896OjQnjzsMEG6_uso
А основной application.properties будет выглядеть несколько иначе:

# MySQL configurations:
spring.datasource.url=jdbc:mysql://jrtb-db:3306/jrtb_db
spring.datasource.username=jrtb_db_user
spring.datasource.password=jrtb_db_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# TelegramBot configurations:
bot.username=test.javarush_community_bot
bot.token=1375780501:AAE4A6Rz0BSnIGzeu896OjQnjzsMEG6_uso
Существенная разница находится в поле spring.datasource.url: в test профиле у нас URL на localhost. То есть ожидается, что приложение будет запускаться вне докера, что правда, потому что при отладке мы будем запускать в Intellij IDEA наш проект и ожидать, что у нас будет доступ к БД с локальной машины. А вот в application.properties URL уже указан другой — jrtb-db. Это будет имя нашей базы данных в docker-compose, и таким образом наше приложение в докере сможет достучаться в базе данных. Почему? Потому они запустятся в рамках одного docker-compose и будут знать друг о друге вот таким образом. Это нужно, чтобы у нас была одна закрытая инфраструктура для проекта. Но как запустить профиль при запуске? Для этого можно настроить запуск main метода в Intellij IDEA. Для этого есть Edit Configurations: переходим в главный класс JavarushTelegramBotApplication, нажимаем на зеленую стрелочку напротив объявления main метода и выбираем Modify Run Configuration:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 10Вводим необходимые переменные и настраиваем профиль. В Intellij IDEA несколько изменился интерфейс этого конфигурационного окна, поэтому я советую всем обновить IDEA до последней версии, чтобы мы видели одинаковую картинку:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 11В поле Environment variables нам нужно добавить переменные имени и токена бота по такому шаблону: имя1_переменной=значение1;имя2_переменной=значение2. То есть ключ=значение разделены точкой с запятой. В нашем случае это будет так: bot.token=1375780501:AAHLzsf4WhcjSdPguDwtggRS1IMu5l8;bot.username=javarush_community_bot (у вас будет другое имя и токен для бота) Далее выбираем в кнопке Modify options поле Add VM options:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 12Пишем: -Dspring.profiles.active=test — это скажет SpringBoot, что нужно использовать профиль test, а значит и проперти будут взяты из application-test.properties. В итоге это будет выглядеть так:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 13Теперь запускаем нашу тестовую базу данных: docker-compose -f docker-compose-test.yml up Причем именно так, чтобы без приставки -d, чтобы в терминале сразу было видно логи, если такие понадобятся. И последний шаг — просто запустить main метод. Если вы сделаете все так же, как я описал, в итоге получите:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 14Второй строкой логов будет описание, что заработал test профиль. Заходим через MySQLWorkbench и запускаем две команды, чтобы проверить, что миграция накатилась и все работает (хотя это и видно из логов запуска приложения, но проверить определенно стоит):
  1. use dev_jrtb_db;
  2. show tables;
Результат:"Java-проект от А до Я": Добавляем все, что связано с БД. Часть 1 - 15Как мы и ожидали, получилось две таблицы. Первая — техническая для учета миграций, вторая — наша таблица для пользователей Телеграма. Все: Flyway вместе с базой данных добавлен. Теперь новые миграции будут идти как по маслу. Самое сложное — это первая настройка и запуск процесса. В продолжении — завершаем работу над этой глобальной задачей: добавляем базы данных в docker-compose.yml, добавляем Repository слой, статистику для бота, пишем и обновляем тесты и документацию.

Список всех материалов серии в начале этой статьи.