JavaRush /Java блог /Java-проекты /Добавляем все, что связано с БД. (Часть 1) - "Java-проект...
Roman Beekeeper
35 уровень

Добавляем все, что связано с БД. (Часть 1) - "Java-проект от А до Я"

Статья из группы Java-проекты
Шаг за шагом мы идем к нашей цели. "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 слой, статистику для бота, пишем и обновляем тесты и документацию.

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

Комментарии (12)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Anonymous #2856674 Уровень 18
28 марта 2024
Отличный туториал, как раз дает то, что не дают видосики и курсы с ютуба - посмотреть, как идет реальный процесс разработки, а не создание какой нибудь легкой фигни с многочисленными упрощениями по типу - докер не используем, бд - не используем Роман, спасибо! ЗЫ как я понял, если в тестовом профиле задаем имя бота и токен, то необязательно иметь их в applicaton-test.properies и наоборот, если они там есть (корректные, само собой), то и не надо их задавать в тестовом профиле ибо они просто перезатираются
Anton Shustikov Уровень 47
17 декабря 2022
У кого среда ругается на flyaway. Обновите базу до 8 версии и добавьте следующую зависимость <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-mysql</artifactId> </dependency>
Kirill Уровень 32
7 ноября 2022
Роман, огромная благодарность за Ваш труд! Идет туговато иногда, но мне кажется сильно сказывается то, что времени с момента написания статьи прошло прилично, у программ повыходили новые версии, практически в каждой лекции из-за этого приходится что-нибудь допридумывать, чтобы результат получился как у вас (одно из последних, при запуске main была ошибка, в логах нашел что flyway более не подерживает версию MySQL5.7). Т.к. нет реального опыта разработки, мало чего понятно, но могу сказать, что очень даже увлекательно. А когда ничего не получается и думаешь, что нах это все не нужно, но потом собираешься (у других же получилось как-то!) и спустя несколько часов поисков и переустанавливаний Docker, MySQL и кучу других программ все таки получается требуемый результат - испытываешь настоящую радость :)
Serhio Gonsales Уровень 35
6 июля 2022
Нифига flyway не подключается. БД разворачивается в докере, бот работает там же, flyway шлет меня на... 😡 UPD чудо-чудное и диво-дивное... забил я уже на это fly way, прикручиваю все штуки дальше, делаю-делаю, как ВДРУГ при очередном запуске все ломается с ошибкой: нет папки migration. Мистика) Создал все папки-файлы. Миграции подтянулись, таблица с версиями в БД появилась, капец магия 🧙‍♂️
28 июня 2022
Если у кого-нибудь тоже будет ошибка Access "MySQL: Access denied for user 'dev_jrtb_db_user'@'localhost' (using password: YES)", может помочь смена порта в application-test.properties и docker-compose-test.yml. А еще была ошибка совместимости FlyWay, поставил версию MySQL 8.0.15 (в примере 5.7) docker-compose-test.yml

version: '3.1'

services:
  jrtb-db:
    image: mysql:8.0.15
    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>
      - '3307:3306'
    expose:
      # Opens port 3307 on the container
      - '3307'
application-test.properties

# MySQL configurations:
spring.datasource.url=jdbc:mysql://localhost:3307/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 configuration:
bot.username=test
bot.token=5407239262:AAH8DGxGcOQmeogeQe8pkw_I_TZNR2OY46s // Токен не действителен ; )
Anonymous #2354938 Уровень 12
4 мая 2022
Видимо на этой статье я и закончу(. Docker жэсть, ничего не понятно + делаю на windows.
Илья Уровень 41
23 мая 2021
Роман, большое спасибо за эти труды, для новичков они бесценны! Всё взлетело! Миграцию подцепило. Бот запустился. Только пришлось смапить MySQL на соседний порт 3307, так как на компьютере локально уже имеется MySQL сервер и порт занят. Тогда docker-compose выдает ошибку.