JavaRush /Java блогы /Random-KK /Біз дерекқорға қатысты барлық нәрсені қосамыз. (2 бөлім) ...
Roman Beekeeper
Деңгей

Біз дерекқорға қатысты барлық нәрсені қосамыз. (2 бөлім) - «Java жобасы А-дан Я-ға дейін»

Топта жарияланған
Бәріңе сәлем. Еске сала кетейін: бірінші бөлімде біз Flyway қостық. жалғастырайық.

Docker-compose.yml дерекқорын қосу

Келесі кезең негізгі docker-compose.yml ішінде дерекқормен жұмысты орнату болып табылады. Дерекқорды docker-compose файлына қосамыз:
version: '3.1'

services:
 jrtb-bot:
   depends_on:
     - jrtb-db
   build:
     context: .
   environment:
     BOT_NAME: ${BOT_NAME}
     BOT_TOKEN: ${BOT_TOKEN}
     BOT_DB_USERNAME: ${BOT_DB_USERNAME}
     BOT_DB_PASSWORD: ${BOT_DB_PASSWORD}
   restart: always
 jrtb-db:
   image: mysql:5.7
   restart: always
   environment:
     MYSQL_USER: ${BOT_DB_USERNAME}
     MYSQL_PASSWORD: ${BOT_DB_PASSWORD}
     MYSQL_DATABASE: 'jrtb_db'
     MYSQL_ROOT_PASSWORD: 'root'
   ports:
     - '3306:3306'
   expose:
     - '3306'
Мен бұл жолды біздің қосымшамызға да қостым:
depends_on:
 - jrtb-db
Бұл қолданбаны іске қоспас бұрын дерекқордың іске қосылуын күтетінімізді білдіреді. Әрі қарай, дерекқормен жұмыс істеу үшін қажет тағы екі айнымалының қосылғанын байқай аласыз:
${BOT_DB_USERNAME}
${BOT_DB_PASSWORD}
Біз оларды docker-compose жүйесінде телеграмма боты сияқты ортаның айнымалы мәндері арқылы аламыз. Мен мұны бізде дерекқордың пайдаланушы аты мен оның құпиясөзінің мәндерін орнататын бір ғана орын болуы үшін жасадым. Біз оларды қолданбамыздың докер кескініне және дерекқорымыздың докер контейнеріне жібереміз. Әрі қарай SpringBoot-ты дерекқор үшін айнымалы мәндерді қабылдауға үйрету үшін Dockerfile файлын жаңартуымыз керек.
FROM adoptopenjdk/openjdk11:ubi
ARG JAR_FILE=target/*.jar
ENV BOT_NAME=test.javarush_community_bot
ENV BOT_TOKEN=1375780501:AAE4A6Rz0BSnIGzeu896OjQnjzsMEG6_uso
ENV BOT_DB_USERNAME=jrtb_db_user
ENV BOT_DB_PASSWORD=jrtb_db_password
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Dspring.datasource.password=${BOT_DB_PASSWORD}", "-Dbot.username=${BOT_NAME}", "-Dbot.token=${BOT_TOKEN}", "-Dspring.datasource.username=${BOT_DB_USERNAME}", "-jar", "app.jar"]
Енді біз Docker файлына дерекқор айнымалыларын қосамыз:
ENV BOT_DB_USERNAME=jrtb_db_user
ENV BOT_DB_PASSWORD=jrtb_db_password
Айнымалы мәндер әртүрлі болады. Біз Dockerfile файлына өтетіндер әдепкі мәндерді талап етеді, сондықтан мен кейбірін енгіздім. Біз соңғы жолды екі элементпен кеңейтеміз, оның көмегімен біз DB пайдаланушы аты мен паролін қолданбаны іске қосуға жібереміз:
"-Dspring.datasource.password=${BOT_DB_PASSWORD}", "-Dbot.username=${BOT_NAME}"
Dockerfile ішіндегі соңғы жол (ENTRYPOINT-тан басталады) ораусыз болуы керек. Егер сіз аударым жасасаңыз, бұл code жұмыс істемейді. Соңғы қадам дерекқорға айнымалы мәндерді беру үшін start.sh файлын жаңарту болып табылады.
#!/bin/bash

# Pull new changes
git pull

# Prepare Jar
mvn clean
mvn package

# Ensure, that docker-compose stopped
docker-compose stop

# Add environment variables
export BOT_NAME=$1
export BOT_TOKEN=$2
export BOT_DB_USERNAME='prod_jrtb_db_user'
export BOT_DB_PASSWORD='Pap9L9VVUkNYj99GCUCC3mJkb'

# Start new deployment
docker-compose up --build -d
Docker-compose іске қоспас бұрын, біз ортаның айнымалы мәндерін қалай қосу керектігін білеміз. Мұны істеу үшін сізге var_name=var_value экспортын орындау қажет. Сондықтан біз тек екі жолды қосамыз:
export BOT_DB_USERNAME='prod_jrtb_db_user'
export BOT_DB_PASSWORD='Pap9L9VVUkNYj99GCUCC3mJkb'
Бұл жерде біз дерекқордың пайдаланушы аты мен құпия сөзін орнаттық. Әрине, бұл айнымалы мәндерді bash сценарийін іске қосу кезінде жіберуге болады, мысалы, біз боттың аты мен таңбалауышы үшін жасаймыз. Бірақ бұл маған қажет емес сияқты. Дерекқорға нақты қол жеткізу үшін дерекқор орналастырылатын serverдің IP мекенжайын білуіңіз керек және сұрауға рұқсат етілген IP мекенжайларының тізімінде болуыңыз керек. Маған келсек, бұл қазірдің өзінде жеткілікті. Іргетасы қаланды: енді әзірлеушіге түсінікті нәрселерді жасауға болады - code жазу. Бұған дейін біз DevOps инженерлерімен айналысатынбыз - қоршаған ортаны орнату.

Репозиторий қабатын қосу

Әдетте қолданба үш қабаттан тұрады:
  1. Контроллерлер қолданбаға кіру нүктелері болып табылады.
  2. Қызметтер - бұл бизнес логикасы жұмыс істейтін жерде. Бізде бұл жартылай бар: SendMessageService бизнес логикасының айқын өкілі болып табылады.
  3. Репозиторийлер – мәліметтер қорымен жұмыс істеу орны. Біздің жағдайда бұл телеграмма боты.
Енді біз үшінші қабатты - репозиторийлерді қосамыз. Мұнда біз көктемгі экожүйенің жобасын қолданамыз - Spring Data. Бұл туралы сіз Хабредегі осы мақаладан оқи аласыз . Біз бірнеше тармақтарды біліп, түсінуіміз керек:
  1. Біз JDBC-мен жұмыс істеудің қажеті жоқ: біз жоғарырақ абстракциялармен тікелей жұмыс істейміз. Яғни, дерекқордағы кестелерге сәйкес келетін POJO-ларды сақтаңыз. Біз мұндай сыныптарды нысан деп атаймыз , өйткені олар Java Persistence API-де ресми түрде аталады (бұл ORM арқылы дерекқормен жұмыс істеуге арналған интерфейстердің жалпы жиынтығы, яғни JDBC-мен жұмыс істеудің абстракциясы). Бізде дерекқорда сақтайтын нысан класы болады және олар дәл бізге қажет кестеге жазылады. Деректер базасында іздеу кезінде біз бірдей нысандарды аламыз.
  2. Spring Data интерфейстер жинағын пайдалануды ұсынады: JpaRepository , CrudRepository , т.б... Басқа интерфейстер бар: толық тізімді мына жерден табуға болады . Сұлулығы сол, олардың әдістерін іске асырмай-ақ қолдануға болады(!). Сонымен қатар, интерфейсте жаңа әдістерді жазуға болатын белгілі бір үлгі бар және олар автоматты түрде жүзеге асырылады.
  3. Көктем біздің дамуымызды мүмкіндігінше жеңілдетеді. Ол үшін біз өз интерфейсімізді жасап, жоғарыда сипатталғандардан мұра алуымыз керек. Көктем осы интерфейсті пайдалану керек екенін білуі үшін Репозиторий annotationсын қосыңыз.
  4. Егер бізге жоқ мәліметтер қорымен жұмыс істеу әдісін жазу қажет болса, онда бұл да проблема емес - біз оны жазамыз. Мен онда не және қалай істеу керектігін көрсетемін.
Бұл мақалада біз TelegramUser жолын толық қосумен жұмыс істейміз және осы бөлікті мысал ретінде көрсетеміз. Қалғанын басқа тапсырмалар бойынша кеңейтеміз. Яғни, /start пәрменін орындаған кезде пайдаланушымыздың деректер қорына active = true деп жазамыз. Бұл пайдаланушының ботты пайдаланып жатқанын білдіреді. Егер пайдаланушы дерекқорда бұрыннан бар болса, біз белсенді = шын өрісін жаңартамыз. /stop пәрменін орындау кезінде біз пайдаланушыны жоймаймыз, тек белсенді өрісті false мәніне жаңартамыз, осылайша пайдаланушы ботты қайта пайдаланғысы келсе, оны іске қосып, тоқтаған жерінен жалғастыра алады. Тестілеу кезінде бірдеңе болып жатқанын көру үшін біз /stat пәрменін жасаймыз: ол белсенді пайдаланушылардың санын көрсетеді. Біз репозиторий пакетін бот, команда, қызмет пакеттерінің жанынан жасаймыз . Бұл пакетте біз басқа бір нысанды жасаймыз . Нысан пакетінде TelegramUser сыныбын жасаймыз:
package com.github.javarushcommunity.jrtb.repository.entity;

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;

/**
* Telegram User entity.
*/
@Data
@Entity
@Table(name = "tg_user")
public class TelegramUser {

   @Id
   @Column(name = "chat_id")
   private String chatId;

   @Column(name = "active")
   private boolean active;
}
Мұнда javax.persistence бумасындағы барлық annotationлар бар екенін көруге болады. Бұл барлық ORM енгізулері үшін пайдаланылатын жалпы annotationлар. Әдепкі бойынша Spring Data Jpa күту режимін пайдаланады, дегенмен басқа іске асыруларды пайдалануға болады. Міне, біз пайдаланатын annotationлар тізімі:
  • Entity – бұл дерекқормен жұмыс істеуге арналған нысан екенін көрсетеді;
  • Кесте – мұнда кестенің атын анықтаймыз;
  • Id – annotation кестеде қай өріс Бастапқы кілт болатынын айтады;
  • Баған – кестеден өрістің атын анықтау.
Әрі қарай мәліметтер қорымен жұмыс істеу интерфейсін жасаймыз. Әдетте мұндай интерфейстердің атаулары EntiryNameRepository үлгісінің көмегімен жазылады. Бізде TelegramuserRepository болады:
package com.github.javarushcommunity.jrtb.repository;

import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
* {@link Repository} for handling with {@link TelegramUser} entity.
*/
@Repository
public interface TelegramUserRepository extends JpaRepository<TelegramUser, String> {
   List<TelegramUser> findAllByActiveTrue();
}
Мұнда сіз еш жерде қолданbyteын findAllByActiveTrue() әдісін қалай қосқанымды көре аласыз . Бірақ бұл оның жұмыс істеуіне кедергі болмайды. Spring Data белсенді өрісі = true tg_user кестесінен барлық жазбаларды алу қажет екенін түсінеді . Біз TelegramUser нысанымен жұмыс істеу қызметін қосамыз (біз SOLID-тен тәуелділік инversionсын басқа субъектілердің қызметтері басқа нысанның репозиторийімен тікелей байланыса алмайтын контекстте қолданамыз - тек сол нысанның қызметі арқылы). Біз пакетте TelegramUserService қызметін жасаймыз, оның әзірге бірнеше әдістері болады: пайдаланушыны сақтау, пайдаланушыны оның идентификаторы бойынша алу және белсенді пайдаланушылар тізімін көрсету. Алдымен TelegramUserService интерфейсін жасаймыз:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

/**
* {@link Service} for handling {@link TelegramUser} entity.
*/
public interface TelegramUserService {

   /**
    * Save provided {@link TelegramUser} entity.
    *
    * @param  telegramUser provided telegram user.
    */
   void save(TelegramUser telegramUser);

   /**
    * Retrieve all active {@link TelegramUser}.
    *
    * @return the collection of the active {@link TelegramUser} objects.
    */
   List<TelegramUser> retrieveAllActiveUsers();

   /**
    * Find {@link TelegramUser} by chatId.
    *
    * @param chatId provided Chat ID
    * @return {@link TelegramUser} with provided chat ID or null otherwise.
    */
   Optional<TelegramUser> findByChatId(String chatId);
}
Және, шын мәнінде, TelegramUserServiceImpl іске асыру:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.repository.TelegramUserRepository;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

/**
* Implementation of {@link TelegramUserService}.
*/
@Service
public class TelegramUserServiceImpl implements TelegramUserService {

   private final TelegramUserRepository telegramUserRepository;

   @Autowired
   public TelegramUserServiceImpl(TelegramUserRepository telegramUserRepository) {
       this.telegramUserRepository = telegramUserRepository;
   }

   @Override
   public void save(TelegramUser telegramUser) {
       telegramUserRepository.save(telegramUser);
   }

   @Override
   public List<TelegramUser> retrieveAllActiveUsers() {
       return telegramUserRepository.findAllByActiveTrue();
   }

   @Override
   public Optional<TelegramUser> findByChatId(String chatId) {
       return telegramUserRepository.findById(chatId);
   }
}
Мұнда біз Autowired annotationсын және конструкторда TelegramuserRepository нысанының тәуелділік инъекциясын (сынып данасын енгізу) қолданатынымызды атап өткен жөн . Сіз мұны айнымалы үшін жасай аласыз, бірақ бұл Spring Framework тобы бізге ұсынатын тәсіл.

Боттың статистикасын қосу

Содан кейін /start және /stop пәрмендерін жаңарту қажет. /start пәрмені пайдаланылғанда, жаңа пайдаланушыны дерекқорға сақтап, оны белсенді = шын мәніне орнату керек. Ал /тоқтаған кезде, пайдаланушы деректерін жаңартыңыз: белсенді = жалған орнатыңыз. StartCommand сыныбын түзетейік :
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.telegram.telegrambots.meta.api.objects.Update;

/**
* Start {@link Command}.
*/
public class StartCommand implements Command {

   private final SendBotMessageService sendBotMessageService;
   private final TelegramUserService telegramUserService;

   public final static String START_MESSAGE = "Привет. Я Javarush Telegram Bot. Я помогу тебе быть в курсе последних " +
           "статей тех авторов, котрые тебе интересны. Я еще маленький и только учусь.";

   public StartCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
       this.sendBotMessageService = sendBotMessageService;
       this.telegramUserService = telegramUserService;
   }

   @Override
   public void execute(Update update) {
       String chatId = update.getMessage().getChatId().toString();

       telegramUserService.findByChatId(chatId).ifPresentOrElse(
               user -> {
                   user.setActive(true);
                   telegramUserService.save(user);
               },
               () -> {
                   TelegramUser telegramUser = new TelegramUser();
                   telegramUser.setActive(true);
                   telegramUser.setChatId(chatId);
                   telegramUserService.save(telegramUser);
               });

       sendBotMessageService.sendMessage(chatId, START_MESSAGE);
   }
}
Мұнда біз сонымен қатар TelegramuserService нысанын конструкторға береміз, оның көмегімен біз жаңа пайдаланушыны сақтаймыз. Одан әрі Java тіліндегі Optional мүмкіндіктерін пайдалана отырып, келесі логика жұмыс істейді: егер дерекқорда пайдаланушы болса, біз оны жай ғана белсенді етеміз, егер жоқ болса, біз жаңа белсендіні жасаймыз. Тоқтату командасы:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.telegram.telegrambots.meta.api.objects.Update;

import java.util.Optional;

/**
* Stop {@link Command}.
*/
public class StopCommand implements Command {

   private final SendBotMessageService sendBotMessageService;
   private final TelegramUserService telegramUserService;

   public static final String STOP_MESSAGE = "Деактивировал все ваши подписки \uD83D\uDE1F.";

   public StopCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
       this.sendBotMessageService = sendBotMessageService;
       this.telegramUserService = telegramUserService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), STOP_MESSAGE);
       telegramUserService.findByChatId(update.getMessage().getChatId().toString())
               .ifPresent(it -> {
                   it.setActive(false);
                   telegramUserService.save(it);
               });
   }
}
Біз TelegramServiceTest-ті StopCommand-ға дәл осылай өткіземіз. Қосымша логика мынада: егер бізде осындай чат идентификаторы бар пайдаланушы болса, біз оны өшіреміз, яғни белсенді = жалған мәнін орнатамыз. Мұны өз көзіңізбен қалай көруге болады? Бот статистикасын көрсететін жаңа /stat пәрменін жасайық. Бұл кезеңде бұл барлық пайдаланушыларға қолжетімді қарапайым статистика болады. Болашақта біз оны шектеп, тек әкімшілерге рұқсат береміз. Статистикада бір жазба болады: белсенді бот пайдаланушыларының саны. Ол үшін CommandName ішіне STAT("/stat") мәнін қосыңыз . Содан кейін StatCommand сыныбын жасаңыз :
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.telegram.telegrambots.meta.api.objects.Update;

/**
* Statistics {@link Command}.
*/
public class StatCommand implements Command {

   private final TelegramUserService telegramUserService;
   private final SendBotMessageService sendBotMessageService;

   public final static String STAT_MESSAGE = "Javarush Telegram Bot использует %s человек.";

   @Autowired
   public StatCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
       this.sendBotMessageService = sendBotMessageService;
       this.telegramUserService = telegramUserService;
   }

   @Override
   public void execute(Update update) {
       int activeUserCount = telegramUserService.retrieveAllActiveUsers().size();
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), String.format(STAT_MESSAGE, activeUserCount));
   }
}
Мұнда бәрі қарапайым: біз retrieveAllActiveUsers әдісін қолданатын барлық белсенді пайдаланушылардың тізімін аламыз және жинақтың өлшемін аламыз. Бізге енді өсетін сыныптарды жаңарту керек: CommandContainer және JavarushTelegramBot , олар бізге қажет жаңа қызметті тасымалдауды үйренуі үшін. Командалық контейнер:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import com.google.common.collect.ImmutableMap;

import static com.github.javarushcommunity.jrtb.command.CommandName.*;

/**
* Container of the {@link Command}s, which are using for handling telegram commands.
*/
public class CommandContainer {

   private final ImmutableMap<String, Command> commandMap;
   private final Command unknownCommand;

   public CommandContainer(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {

       commandMap = ImmutableMap.<String, Command>builder()
               .put(START.getCommandName(), new StartCommand(sendBotMessageService, telegramUserService))
               .put(STOP.getCommandName(), new StopCommand(sendBotMessageService, telegramUserService))
               .put(HELP.getCommandName(), new HelpCommand(sendBotMessageService))
               .put(NO.getCommandName(), new NoCommand(sendBotMessageService))
               .put(STAT.getCommandName(), new StatCommand(sendBotMessageService, telegramUserService))
               .build();

       unknownCommand = new UnknownCommand(sendBotMessageService);
   }

   public Command retrieveCommand(String commandIdentifier) {
       return commandMap.getOrDefault(commandIdentifier, unknownCommand);
   }

}
Мұнда біз картаға жаңа пәрмен қостық және оны TelegramUserService конструкторы арқылы өткіздік. Бірақ боттың өзінде тек конструктор өзгереді:
@Autowired
public JavarushTelegramBot(TelegramUserService telegramUserService) {
   this.commandContainer = new CommandContainer(new SendBotMessageServiceImpl(this), telegramUserService);
}
Енді біз Autowired annotationсын қоса отырып, TelegramUserService қызметін аргумент ретінде береміз. Бұл біз оны Қолданба мәтінмәнінен алатынымызды білдіреді. Сипаттамада жаңа статистика пәрмені пайда болуы үшін біз HelpCommand сыныбын да жаңартамыз .

Қолмен тестілеу

Дерекқорды docker-compose-test.yml және JavarushTelegramBotApplication класындағы негізгі әдісті іске қосайық. Содан кейін біз командалар жинағын жазамыз:
  • /stat - егер дерекқор бос болса, бұл ботты пайдаланатын адам нөлдік болады деп күтеміз;
  • /start - ботты іске қосу;
  • /stat - енді біз ботты 1 адам пайдаланады деп күтеміз;
  • /stop – ботты тоқтату;
  • /stat - біз оны қайтадан 0 адам пайдаланады деп күтеміз.
«А-дан Я-ға Java жобасы»: Дерекқорға қатысты барлық нәрсені қосу.  2 - 2 бөлімЕгер нәтиже сіз үшін бірдей болса, функция дұрыс жұмыс істеді және бот дұрыс жұмыс істейді деп айта аламыз. Егер бірдеңе дұрыс болмаса, маңызды емес: біз отладка режимінде негізгі әдісті қайта іске қосамыз және қатенің не екенін табу үшін бүкіл жолды анық өтеміз.

Біз сынақтарды жазамыз және жаңартамыз

Біз конструкторларды өзгерткендіктен, сынақ сыныптарын да жаңартуымыз керек. AbstractCommandTest сыныбында бізге тағы бір өрісті - TelegramUserService сыныбын қосу керек , ол үш пәрмен үшін қажет:
protected TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
Әрі қарай, CommandContainer ішіндегі init() әдісін жаңартайық :
@BeforeEach
public void init() {
   SendBotMessageService sendBotMessageService = Mockito.mock(SendBotMessageService.class);
   TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
   commandContainer = new CommandContainer(sendBotMessageService, telegramUserService);
}
StartCommand ішінде getCommand() әдісін жаңарту қажет :
@Override
Command getCommand() {
   return new StartCommand(sendBotMessageService, telegramUserService);
}
Сондай-ақ StopCommand ішінде:
@Override
Command getCommand() {
   return new StopCommand(sendBotMessageService, telegramUserService);
}
Әрі қарай, жаңа сынақтарды қарастырайық. StatCommand үшін әдеттегі сынақты жасайық :
package com.github.javarushcommunity.jrtb.command;

import static com.github.javarushcommunity.jrtb.command.CommandName.STAT;
import static com.github.javarushcommunity.jrtb.command.StatCommand.STAT_MESSAGE;

public class StatCommandTest extends AbstractCommandTest {
   @Override
   String getCommandName() {
       return STAT.getCommandName();
   }

   @Override
   String getCommandMessage() {
       return String.format(STAT_MESSAGE, 0);
   }

   @Override
   Command getCommand() {
       return new StatCommand(sendBotMessageService, telegramUserService);
   }
}
Бұл қарапайым. Енді мәліметтер қорымен жұмысты қалай тексеретініміз туралы сөйлесейік. Бұрын біз тек бірлік сынақтарымен айналыстық. Біріктіру сынағы қолданбаның бірнеше бөліктері арасындағы интеграцияны тексереді. Мысалы, қолданбалар мен мәліметтер базасы. Мұнда бәрі күрделірек болады, өйткені тестілеу үшін бізге орналастырылған деректер базасы қажет. Сондықтан, біз сынақтарды жергілікті түрде іске қосқан кезде, бізде docker-compose-test.yml арқылы жұмыс істейтін дерекқор болуы керек. Бұл сынақты орындау үшін SpringBoot қолданбасын толығымен іске қосу керек. Сынақ сыныбында қолданбаны іске қосатын SpringBootTest annotationсы бар. Бірақ бұл тәсіл біз үшін жұмыс істемейді, өйткені қолданба іске қосылған кезде телеграмма боты да іске қосылады. Бірақ бұл жерде қайшылық бар. Сынақтар біздің құрылғыда жергілікті түрде де, GitHub әрекеттері арқылы жалпыға бірдей орындалады. Бүкіл қолданбаны іске қосқан кезде сынақтар өтуі үшін біз оларды телеграмма ботында жарамды деректермен іске қосуымыз керек: яғни оның аты мен белгісі бойынша... Сондықтан бізде екі нұсқа бар:
  1. Сондықтан боттың аты мен токенін көпшілікке жария етіңіз және бәрі жақсы болады, оны ешкім пайдаланып, бізге кедергі жасамайды деп үміттеніңіз.
  2. Басқа жолды ойлап көріңіз.
Мен екінші нұсқаны таңдадым. SpringBoot тестілеуінде DataJpaTest annotationсы бар , ол дерекқорды тестілеу кезінде біз тек қажетті сыныптарды ғана қолданамыз және басқаларды жалғыз қалдырамыз. Бірақ бұл бізге қолайлы, өйткені телеграмма боты мүлде іске қосылмайды. Бұл оған жарамды атау мен таңбалауыш берудің қажеті жоқ дегенді білдіреді!))) Біз сынақтан өтеміз, онда Spring Data бізге енгізетін әдістер біз күткендей жұмыс істейтінін тексереміз. Сынақ профилін пайдалануды көрсету үшін @ActiveProfiles("test") annotationсын қолданатынымызды ескеру маңызды . Бұл біздің дерекқордың дұрыс сипаттарын санау үшін бізге қажет нәрсе. Тесттерімізді іске қоспас бұрын дерекқорды дайындаған дұрыс болар еді. Бұл мәселе бойынша мұндай тәсіл бар: сынаққа Sql annotationсын қосыңыз және оны сынақты бастамас бұрын іске қосу қажет сценарий атауларының жинағын тапсырыңыз:
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/telegram_users.sql"})
Біз үшін олар ./src/test/resources/ + annotationда көрсетілген жолдың бойында орналасады. Міне, олар қалай көрінеді:
clearDbs.sql:
DELETE FROM tg_user;

telegram_users.sql:
INSERT INTO tg_user VALUES ("123456789", 1);
INSERT INTO tg_user VALUES ("123456788", 1);
INSERT INTO tg_user VALUES ("123456787", 1);
INSERT INTO tg_user VALUES ("123456786", 1);
INSERT INTO tg_user VALUES ("123456785", 1);
INSERT INTO tg_user VALUES ("123456784", 0);
INSERT INTO tg_user VALUES ("123456782", 0);
INSERT INTO tg_user VALUES ("123456781", 0);
Нәтижесінде TelegramUserRepositoryIT тесті осылай болады (көріп отырғаныңыздай, интеграциялық тестілеудің атауы басқаша болады - біз Тест емес, АТ қосамыз):
package com.github.javarushcommunity.jrtb.repository;

import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;

import java.util.List;
import java.util.Optional;

import static org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace.NONE;

/**
* Integration-level testing for {@link TelegramUserRepository}.
*/
@ActiveProfiles("test")
@DataJpaTest
@AutoConfigureTestDatabase(replace = NONE)
public class TelegramUserRepositoryIT {

   @Autowired
   private TelegramUserRepository telegramUserRepository;

   @Sql(scripts = {"/sql/clearDbs.sql", "/sql/telegram_users.sql"})
   @Test
   public void shouldProperlyFindAllActiveUsers() {
       //when
       List<TelegramUser> users = telegramUserRepository.findAllByActiveTrue();

       //then
       Assertions.assertEquals(5, users.size());
   }

   @Sql(scripts = {"/sql/clearDbs.sql"})
   @Test
   public void shouldProperlySaveTelegramUser() {
       //given
       TelegramUser telegramUser = new TelegramUser();
       telegramUser.setChatId("1234567890");
       telegramUser.setActive(false);
       telegramUserRepository.save(telegramUser);

       //when
       Optional<TelegramUser> saved = telegramUserRepository.findById(telegramUser.getChatId());

       //then
       Assertions.assertTrue(saved.isPresent());
       Assertions.assertEquals(telegramUser, saved.get());
   }
}
Біз сынақтарды жаздық, бірақ сұрақ туындайды: GitHub сайтында CI процесінің іске қосылуымен не болады? Оның дерекқоры болмайды. Әзірге шынымен қызыл құрылым болады. Бұл әрекетті орындау үшін бізде GitHub әрекеттері бар, оларда құрастыруды іске қосуды конфигурациялай аламыз. Сынақтарды іске қоспас бұрын, қажетті параметрлері бар дерекқорды іске қосуды қосу керек. Белгілі болғандай, Интернетте мысалдар көп емес, сондықтан мен оны бір жерде сақтауды ұсынамын. .github/workflows/maven.yml файлын жаңартайық:
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven

name: Java CI with Maven

on:
 push:
   branches: [ main ]
 pull_request:
   branches: [ main ]

jobs:
 build:
   runs-on: ubuntu-latest
   steps:
   - uses: actions/checkout@v2
   - name: Set up MySQL
     uses: mirromutth/mysql-action@v1.1
     with:
       mysql version: '5.7'
       mysql database: 'dev_jrtb_db'
       mysql root password: 'root'
       mysql user: 'dev_jrtb_db_user'
       mysql password: 'dev_jrtb_db_password'
   - name: Set up JDK 1.11
     uses: actions/setup-java@v1
     with:
       java-version: 1.11
   - name: Build with Maven
     run: mvn -B package --file pom.xml
Енді MySQL орнатудың жаңа блогы бар . Онда біз өзімізге қажет айнымалы мәндерді бір уақытта анықтай отырып, CI процесіне MySQL қосамыз. Енді біз қалағанның бәрін қостық. Соңғы кезең - өзгерістерді итермелеу және құрылыстың өтіп, жасыл болатынын көру.

Құжаттаманы жаңарту

Жоба нұсқасын pom.xml ішіндегі 0.3.0-SNAPSHOT-тан 0.4.0-SNAPSHOT-қа дейін жаңартайық, сонымен қатар RELEASE_NOTES-ке қосамыз:
## 0.4.0-SNAPSHOT

*   JRTB-1: added repository layer.
Осының бәрінен кейін біз міндеттеме, итеру және тарту сұрауын жасаймыз. Ең бастысы, біздің құрылысымыз жасыл!«А-дан Я-ға Java жобасы»: Дерекқорға қатысты барлық нәрсені қосу.  2 - 3 бөлім

Пайдалы сілтемелер:

Барлық өзгертулерді осы жерден жасалған тарту сұрауында көруге болады . Оқығаныңыз үшін барлығына рахмет.

Сериядағы барлық материалдардың тізімі осы мақаланың басында.

Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION