JavaRush /جاوا بلاگ /Random-SD /اسان ڊيٽابيس سان لاڳاپيل هر شيء شامل ڪندا آهيون. (حصو 2) ...

اسان ڊيٽابيس سان لاڳاپيل هر شيء شامل ڪندا آهيون. (حصو 2) - "جاوا پروجيڪٽ A کان Z تائين"

گروپ ۾ شايع ٿيل
هيلو سڀ. مون کي توهان کي ياد ڏيارڻ ڏيو: پهرين حصي ۾ اسان 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 ۾ ساڳيءَ طرح حاصل ڪنداسين جيئن ٽيليگرام بوٽ لاءِ - ماحولياتي متغيرن ذريعي. مون اهو ان ڪري ڪيو ته اسان وٽ صرف هڪ جڳهه آهي جتي اسان ڊيٽابيس جي يوزر جي نالي ۽ ان جي پاسورڊ جا قدر مقرر ڪريون ٿا. اسان انهن کي اسان جي ايپليڪيشن جي ڊاکر تصوير ۽ اسان جي ڊيٽابيس جي ڊاکر ڪنٽينر ڏانهن منتقل ڪريون ٿا. اڳيون اسان کي ڊيڪر فائل کي اپڊيٽ ڪرڻ جي ضرورت آهي اسان جي اسپرنگ بوٽ کي سيکارڻ لاءِ ڊيٽابيس لاءِ متغير قبول ڪرڻ لاءِ.
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"]
ھاڻي اسان ڊيٽابيس متغير شامل ڪندا آھيون Dockerfile ۾:
ENV BOT_DB_USERNAME=jrtb_db_user
ENV BOT_DB_PASSWORD=jrtb_db_password
متغير قدر مختلف ٿي ويندا. جن کي اسان Dockerfile ۾ داخل ڪنداسين، جڏهن ته، ڊفالٽ قدر جي ضرورت آهي، تنهنڪري مون ڪجهه داخل ڪيو. اسان آخري لائين کي ٻن عناصر سان وڌايو، جنھن جي مدد سان اسين ڊي بي يوزرنيم ۽ پاسورڊ کي ايپليڪيشن لانچ ڏانھن منتقل ڪنداسين:
"-Dspring.datasource.password=${BOT_DB_PASSWORD}", "-Dbot.username=${BOT_NAME}"
Dockerfile ۾ آخري لڪير (جيڪو ENTRYPOINT سان شروع ٿئي ٿو) بغير لپڻ جي هجڻ گهرجي. جيڪڏهن توهان منتقلي ڪريو ٿا، اهو ڪوڊ ڪم نه ڪندو. آخري قدم آهي 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
اسان اڳ ۾ ئي ڄاڻون ٿا ته ڊاڪر-ڪپوز کي هلائڻ کان اڳ ماحول جي متغير کي ڪيئن شامل ڪجي. هن کي ڪرڻ لاء، توهان کي صرف برآمد ڪرڻ جي ضرورت آهي var_name=var_value.. تنهن ڪري، اسان صرف ٻه لائين شامل ڪندا آهيون:
export BOT_DB_USERNAME='prod_jrtb_db_user'
export BOT_DB_PASSWORD='Pap9L9VVUkNYj99GCUCC3mJkb'
هي آهي جتي اسان ڊيٽابيس يوزرنيم ۽ پاسورڊ سيٽ ڪيو. يقينن، اهو ممڪن هوندو ته انهن متغيرن کي پاس ڪرڻ جڏهن بش اسڪرپٽ هلائي رهيا آهيون، جيئن اسان بوٽ جي نالي ۽ ٽوڪن لاءِ ڪندا آهيون. پر مون کي لڳي ٿو ته اهو غير ضروري آهي. اصل ۾ ڊيٽابيس تائين رسائي حاصل ڪرڻ لاء، توهان کي سرور جي IP کي ڄاڻڻ جي ضرورت آهي جنهن تي ڊيٽابيس کي ترتيب ڏنو ويندو، ۽ درخواست لاء اجازت ڏنل IP پتي جي فهرست تي هجي. جيئن ته مون لاء، اهو اڳ ۾ ئي ڪافي آهي. بنياد رکيو ويو آھي: ھاڻي توھان ڪم ڪري سگھوٿا جيڪي ڊولپر لاءِ وڌيڪ سمجھڻ وارا آھن - ڪوڊ لکو. ان کان اڳ، اسان ڪري رهيا هئاسين جيڪي DevOps انجنيئر ڪندا آهن - ماحول کي ترتيب ڏيڻ.

مخزن جي پرت کي شامل ڪرڻ

عام طور تي ايپليڪيشن ۾ ٽي تہون آھن:
  1. ڪنٽرولر ايپليڪيشن ۾ داخلا پوائنٽ آهن.
  2. خدمتون آهن جتي ڪاروباري منطق ڪم ڪري ٿي. اسان وٽ اڳ ۾ ئي جزوي طور تي هي آهي: SendMessageService ڪاروباري منطق جو هڪ واضح نمائندو آهي.
  3. ذخيرو هڪ ڊيٽابيس سان ڪم ڪرڻ لاء هڪ جڳهه آهي. اسان جي حالت ۾، هي هڪ ٽيليگرام بوٽ آهي.
هاڻي اسان هڪ ٽيون پرت شامل ڪنداسين - مخزن. هتي اسان اسپرنگ ايڪو سسٽم مان هڪ پروجيڪٽ استعمال ڪنداسين - اسپرنگ ڊيٽا. توهان پڙهي سگهو ٿا ته اهو ڇا آهي هن مضمون ۾ Habré تي . اسان کي ڪيترن ئي نقطن کي ڄاڻڻ ۽ سمجهڻ جي ضرورت آهي:
  1. اسان کي JDBC سان ڪم ڪرڻ جي ضرورت نه پوندي: اسان سڌو ڪم ڪنداسين اعلي خلاصن سان. اھو آھي، ذخيرو ڪريو POJOs جيڪي ڊيٽابيس ۾ جدولن سان ملن ٿا. اسان اهڙن طبقن کي entity سڏينداسين ، جيئن اهي سرڪاري طور تي Java Persistence API ۾ سڏيا وڃن ٿا (هي هڪ عام سيٽ آهي انٽرفيس جو هڪ ORM ذريعي ڊيٽابيس سان ڪم ڪرڻ لاءِ، يعني JDBC سان ڪم ڪرڻ تي هڪ خلاصو). اسان وٽ هڪ ادارو ڪلاس هوندو جنهن کي اسين ڊيٽابيس ۾ محفوظ ڪنداسين، ۽ اهي بلڪل انهيءَ ٽيبل تي لکيا ويندا جنهن جي اسان کي ضرورت آهي. اسان ساڳئي شيون وصول ڪنداسين جڏهن ڊيٽابيس ۾ ڳولا ڪندا.
  2. بهار جي ڊيٽا انهن جي انٽرفيس جي سيٽ کي استعمال ڪرڻ جي صلاح ڏني آهي: JpaRepository , CrudRepository , etc... ٻيا انٽرفيس آهن: هڪ مڪمل فهرست هتي ملي سگهي ٿي . حسن اهو آهي ته توهان انهن جي طريقن کي استعمال ڪري سگهو ٿا بغير انهن کي لاڳو ڪرڻ (!). ان کان علاوه، ھڪڙو خاص ٽيمپليٽ آھي جنھن کي استعمال ڪندي توھان انٽرفيس ۾ نوان طريقا لکي سگھو ٿا، ۽ اھي خودڪار طريقي سان لاڳو ڪيا ويندا.
  3. بهار اسان جي ترقي کي آسان بڻائي ٿو جيترو ٿي سگهي ٿو. هن کي ڪرڻ لاء، اسان کي پنهنجو انٽرفيس ٺاهڻ جي ضرورت آهي ۽ مٿي بيان ڪيل انهن مان ورثو. ۽ انهي ڪري ته بهار ڄاڻي ٿو ته ان کي هن انٽرفيس کي استعمال ڪرڻ جي ضرورت آهي، شامل ڪريو مخزن جي تشريح.
  4. جيڪڏهن اسان کي ڊيٽابيس سان ڪم ڪرڻ لاء هڪ طريقو لکڻ جي ضرورت آهي جيڪا موجود ناهي، پوء اهو پڻ ڪو مسئلو ناهي - اسان ان کي لکنداسين. مان توهان کي ڏيکاريندس ته اتي ڇا ۽ ڪيئن ڪجي.
هن آرٽيڪل ۾، اسان ٽيليگرام يوزر جي سڄي رستي کي شامل ڪرڻ سان ڪم ڪنداسين ۽ هن حصي کي مثال طور ڏيکارينداسين. اسان باقي ٻين ڪمن تي وسعت ڪنداسين. اهو آهي، جڏهن اسان /start ڪمانڊ تي عمل ڪريون ٿا، اسان لکنداسين active = true اسان جي صارف جي ڊيٽابيس ۾. هن جو مطلب اهو ٿيندو ته صارف هڪ بوٽ استعمال ڪري رهيو آهي. جيڪڏهن صارف اڳ ۾ ئي ڊيٽابيس ۾ آهي، اسان فيلڊ کي تازه ڪاري ڪنداسين active = true. جڏهن /اسٽاپ ڪمانڊ تي عمل ڪندي، اسان صارف کي حذف نه ڪنداسين، پر صرف فعال فيلڊ کي غلط ۾ تازه ڪاري ڪنداسين، انهي ڪري ته جيڪڏهن صارف بوٽ کي ٻيهر استعمال ڪرڻ چاهي ٿو، هو ان کي شروع ڪري سگهي ٿو ۽ وٺي سگهي ٿو جتي هن ڇڏي ڏنو. ۽ انهي ڪري ته جڏهن اسان جاچ ڪري سگهون ٿا ته ڪجهه ٿي رهيو آهي، اسان ٺاهينداسين /stat حڪم: اهو فعال استعمال ڪندڙن جو تعداد ڏيکاريندو. اسان بوٽ، ڪمانڊ، سروس پيڪيجز جي اڳيان هڪ مخزن وارو پيڪيج ٺاهيندا آهيون. هن پيڪيج ۾ اسان هڪ ٻيو ٺاهيندا آهيون - ادارو . انٽيٽي پيڪيج ۾ اسان ٺاهيندا آهيون ٽيليگرام يوزر ڪلاس:
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 پيڪيج مان. اهي عام تشريحون آهن جيڪي استعمال ڪيون وينديون آهن سڀني ORM عملن لاءِ. ڊفالٽ طور، اسپرنگ ڊيٽا Jpa استعمال ڪري ٿو Hibernate، باقي ٻيا عمل استعمال ڪري سگھجن ٿا. هتي تشريح جي هڪ فهرست آهي جيڪا اسان استعمال ڪريون ٿا:
  • ادارو - ظاهر ڪري ٿو ته هي هڪ ادارو آهي ڊيٽابيس سان ڪم ڪرڻ لاءِ؛
  • ٽيبل - هتي اسان ٽيبل جو نالو بيان ڪريون ٿا؛
  • Id - تشريح ٻڌائي ٿي ته ٽيبل ۾ ڪهڙي فيلڊ پرائمري ڪيئي هوندي؛
  • ڪالمن - ٽيبل مان فيلڊ جو نالو طئي ڪريو.
اڳيون، اسان ڊيٽابيس سان ڪم ڪرڻ لاء هڪ انٽرنيٽ ٺاهي. عام طور تي، اهڙن انٽرفيس جا نالا ٽيمپليٽ استعمال ڪندي لکيا ويندا آهن - 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();
}
هتي توهان ڏسي سگهو ٿا ته مون ڪيئن شامل ڪيو findAllByActiveTrue() طريقو ، جنهن کي مان ڪٿي به لاڳو نٿو ڪريان. پر اهو کيس ڪم ڪرڻ کان روڪي نه سگهندو. بهار جي ڊيٽا سمجهي ويندي ته ان کي tg_user ٽيبل مان سڀئي رڪارڊ حاصل ڪرڻ گهرجن جن جي فعال فيلڊ = سچو . اسان TelegramUser entity سان ڪم ڪرڻ لاءِ هڪ خدمت شامل ڪريون ٿا (اسان ان حوالي سان SOLID کان انحصار جي ڦيرڦار کي استعمال ڪريون ٿا ته ٻين ادارن جون خدمتون سڌو سنئون ڪنهن ٻئي اداري جي مخزن سان رابطو نه ٿيون ڪري سگهن - صرف ان اداري جي خدمت ذريعي). اسان ھڪڙي خدمت ٺاھيون ٿا TelegramUserService پئڪيج ۾، جنھن لاء ھاڻي ڪيترائي طريقا آھن: صارف کي بچايو، صارف کي پنھنجي ID سان حاصل ڪريو ۽ فعال استعمال ڪندڙن جي ھڪڙي فهرست ڏيکاري. پهرين اسان ٺاهيندا آهيون 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);
   }
}
هتي اهو ياد رکڻ گهرجي ته اسان TelegramuserRepository اعتراض جي انحصار انجيڪشن (هڪ طبقي جو مثال متعارف ڪرايو) استعمال ڪندا آهيون Autowired annotation استعمال ڪندي ، ۽ تعمير ڪندڙ تي. توھان ھي ڪري سگھو ٿا ھڪڙي متغير لاءِ، پر اھو اھو طريقو آھي جيڪو اسپرنگ فريم ورڪ ٽيم اسان کي تجويز ڪري ٿو.

بوٽ لاءِ انگ اکر شامل ڪرڻ

اڳيون توهان کي تازه ڪاري ڪرڻ جي ضرورت آهي /شروع ۽ / اسٽاپ حڪم. جڏهن /start حڪم استعمال ڪيو ويندو آهي، توهان کي ڊيٽابيس ۾ نئين صارف کي بچائڻ جي ضرورت آهي ۽ ان کي سيٽ ڪرڻ جي ضرورت آهي Active = true. ۽ جڏهن اتي آهي / اسٽاپ، صارف ڊيٽا کي اپڊيٽ ڪريو: سيٽ فعال = غلط. اچو ته درست ڪريون 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 اعتراض کي تعمير ڪندڙ ڏانهن منتقل ڪيو، جنهن سان اسان نئين صارف کي محفوظ ڪنداسين. وڌيڪ، جاوا ۾ اختياري جي نعمتن کي استعمال ڪندي، هيٺيون منطق ڪم ڪري ٿو: جيڪڏهن اسان وٽ ڊيٽابيس ۾ صارف آهي، اسان صرف ان کي فعال ڪريون ٿا، جيڪڏهن نه، اسان هڪ نئون فعال ٺاهيندا آهيون. اسٽاپ ڪمانڊ:
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 ڏانهن ساڳئي طريقي سان پاس ڪندا آهيون. اضافي منطق هي آهي: جيڪڏهن اسان وٽ هڪ صارف آهي جنهن سان هڪ چيٽ ID آهي، اسان ان کي غير فعال ڪريون ٿا، اهو آهي، اسان سيٽ ڪيو Active = غلط. توهان پنهنجي اکين سان اهو ڪيئن ڏسي سگهو ٿا؟ اچو ته هڪ نئون ڪمانڊ/stat ٺاهيون، جيڪو ڏيکاريندو بوٽ جا انگ اکر. ھن مرحلي تي، اھي سادي انگ اکر آھن جيڪي سڀني استعمال ڪندڙن لاءِ دستياب آھن. مستقبل ۾، اسان ان کي محدود ڪنداسين ۽ صرف منتظمين لاءِ رسائي ڪنداسين. انگن اکرن ۾ ھڪڙي داخلا ٿيندي: فعال بوٽ استعمال ڪندڙن جو تعداد. هن کي ڪرڻ لاء، ڪمانڊ نام ۾ قيمت 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);
}
ھاڻي اسان پاس ڪريون ٿا TelegramUserService ھڪ دليل جي طور تي، شامل ڪندي Autowired تشريح. ان جو مطلب اهو آهي ته اسان ان کي حاصل ڪنداسين ايپليڪيشن جي حوالي سان. اسان هيلپ ڪمانڊ ڪلاس کي به اپڊيٽ ڪنداسين ته جيئن تفصيل ۾ هڪ نئون شمارياتي ڪمانڊ ظاهر ٿئي.

دستي جاچ

اچو ته ڊيٽابيس کي شروع ڪريون docker-compose-test.yml کان ۽ بنيادي طريقو JavarushTelegramBotApplication ڪلاس ۾. اڳتي هلي اسان حڪمن جو هڪ سيٽ لکون ٿا:
  • /stat - اسان کي اميد آهي ته جيڪڏهن ڊيٽابيس خالي آهي، اتي صفر ماڻهو هوندا هي بوٽ استعمال ڪندي؛
  • /شروع - بوٽ شروع ڪريو؛
  • /stat - هاڻي اسان کي اميد آهي ته بوٽ 1 شخص طرفان استعمال ڪيو ويندو؛
  • / اسٽاپ - بوٽ کي روڪيو؛
  • /stat - اسان کي اميد آهي ته ٻيهر 0 ماڻهو ان کي استعمال ڪندي.
"جاوا پروجيڪٽ A کان Z تائين": ڊيٽابيس سان لاڳاپيل سڀ ڪجهه شامل ڪرڻ.  حصو 2 - 2جيڪڏهن نتيجو توهان لاء ساڳيو آهي، اسان اهو چئي سگهون ٿا ته ڪارڪردگي صحيح ڪم ڪيو ۽ بوٽ صحيح ڪم ڪري رهيو آهي. جيڪڏهن ڪجهه غلط ٿي وڃي، اهو مسئلو ناهي: اسان ڊيبگ موڊ ۾ مکيه طريقو ٻيهر شروع ڪيو ۽ واضح طور تي سڄي رستي ذريعي وڃون ٿا ته غلطي ڇا هئي.

اسان لکندا ۽ تازه ڪاري ٽيسٽ

جيئن ته اسان تعمير ڪندڙن کي تبديل ڪيو، اسان کي پڻ ٽيسٽ ڪلاس کي اپڊيٽ ڪرڻ جي ضرورت پوندي. AbstractCommandTest ڪلاس ۾ ، اسان کي هڪ وڌيڪ فيلڊ شامل ڪرڻ جي ضرورت آهي - TelegramUserService class ، جيڪو ٽن حڪمن لاءِ گهربل آهي:
protected TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
اڳيون، اچو ته تازه ڪاري ڪريون init() طريقو CommandContainer ۾ :
@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);
}
اسٽاپ ڪمانڊ ۾ پڻ:
@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 تشريح آهي جيڪا ايپليڪيشن کي شروع ڪندي. پر اهو طريقو اسان لاءِ ڪم نه ڪندو، ڇاڪاڻ ته جڏهن ايپليڪيشن لانچ ٿيندي، ته ٽيليگرام بوٽ به لانچ ڪندو. پر هتي هڪ تضاد آهي. ٽيسٽون هلايون وينديون مقامي طور تي اسان جي مشين تي ۽ عوامي طور تي GitHub ايڪشنز ذريعي. پوري ايپليڪيشن جي لانچ سان گڏ ٽيسٽ پاس ڪرڻ لاءِ، اسان کي لازمي طور تي ٽيليگرام بوٽ تي صحيح ڊيٽا سان هلائڻو پوندو: يعني ان جي نالي ۽ ٽوڪن سان... ان ڪري، اسان وٽ ٻه آپشن آهن:
  1. ان ڪري بوٽ جو نالو ۽ ٽوڪن پبليڪ ڪريو ۽ اميد رکو ته سڀ ڪجھ ٺيڪ ٿي ويندو، ڪو به ان کي استعمال نه ڪندو ۽ اسان سان مداخلت نه ڪندو.
  2. اچو ته ٻئي طريقي سان.
مون ٻيو اختيار چونڊيو. SpringBoot ٽيسٽنگ ۾ DataJpaTest تشريح آهي ، جيڪا ٺاهي وئي آهي ته جيئن ڊيٽابيس کي جانچڻ وقت، اسان صرف اهي ڪلاس استعمال ڪريون جيڪي اسان کي گهرجن ۽ ٻين کي اڪيلو ڇڏي ڏيو. پر اهو اسان کي مناسب آهي، ڇاڪاڻ ته ٽيليگرام بوٽ شروع نه ٿيندو. ان جو مطلب اهو آهي ته ان کي پاس ڪرڻ جي ڪا ضرورت ناهي هڪ صحيح نالو ۽ ٽوڪن!))) اسان هڪ امتحان حاصل ڪنداسين جنهن ۾ اسين جانچ ڪنداسين ته اهي طريقا جيڪي اسپرنگ ڊيٽا اسان لاءِ لاڳو ڪندا آهن اسان جي توقع مطابق ڪم ڪن ٿا. هتي اهو نوٽ ڪرڻ ضروري آهي ته اسان استعمال ڪريون ٿا @ActiveProfiles("test") تشريح ٽيسٽ پروفائل جي استعمال جي وضاحت ڪرڻ لاءِ. ۽ اھو اھو آھي جيڪو اسان کي گھربل آھي تنھنڪري اسان پنھنجي ڊيٽابيس لاءِ صحيح ملڪيتن کي ڳڻائي سگھون ٿا. اهو سٺو لڳندو ته اسان جي ٽيسٽ هلائڻ کان پهريان هڪ ڊيٽابيس تيار ڪيو وڃي. ھن معاملي لاءِ ھڪڙو طريقو آھي: ٽيسٽ لاءِ Sql تشريح شامل ڪريو ۽ ان کي اسڪرپٽ نالن جو مجموعو پاس ڪريو جيڪي ٽيسٽ شروع ڪرڻ کان اڳ ھلڻ گهرجن.
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/telegram_users.sql"})
اسان لاءِ، اهي رستي تي واقع هوندا./src/test/resources/ + تشريح ۾ بيان ڪيل رستو. هتي اهي آهن جيڪي نظر اچن ٿا:
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 ٽيسٽ جي نتيجي ۾ نظر ايندو (جيئن توھان ڏسي سگھو ٿا، انٽيگريشن ٽيسٽنگ جو نالو مختلف ھوندو - اسان IT شامل ڪندا آھيون، ٽيسٽ نه):
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 بلاڪ آھي . ان ۾ اسان MySQL کي اسان جي CI پروسيس ۾ شامل ڪريون ٿا، گڏو گڏ متغيرن جي وضاحت ڪندي جيڪي اسان کي گهربل آهن. هاڻي اسان سڀ ڪجهه شامل ڪيو آهي جيڪو اسان چاهيون ٿا. آخري مرحلو تبديلين کي دٻائڻو آهي ۽ ڏسو ته تعمير گذري ويندي ۽ سائي ٿي ويندي.

دستاويز کي اپڊيٽ ڪرڻ

اچو ته پروجيڪٽ ورزن کي 0.3.0-SNAPSHOT کان 0.4.0-SNAPSHOT تائين pom.xml ۾ تازه ڪاري ڪريون ۽ RELEASE_NOTES ۾ پڻ شامل ڪريو:
## 0.4.0-SNAPSHOT

*   JRTB-1: added repository layer.
هن سڀ کان پوء، اسان هڪ ڪمٽ، ڌڪ ۽ ڇڪڻ جي درخواست ٺاهيندا آهيون. ۽ سڀ کان اهم، اسان جي تعمير سائي آهي!"جاوا پروجيڪٽ A کان Z تائين": ڊيٽابيس سان لاڳاپيل سڀ ڪجهه شامل ڪرڻ.  حصو 2 - 3

مفيد لنڪس:

سڀ تبديليون ڏسي سگهجن ٿيون هتي ٺاهيل پل جي درخواست ۾ . پڙهڻ لاءِ سڀني جي مهرباني.

سيريز ۾ سڀني مواد جي هڪ فهرست هن مضمون جي شروعات ۾ آهي.

تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION