JavaRush /Blog Java /Random-MS /Kami menambah semua yang berkaitan dengan pangkalan data....

Kami menambah semua yang berkaitan dengan pangkalan data. (Bahagian 2) - "Projek Java dari A hingga Z"

Diterbitkan dalam kumpulan
Hai semua. Biar saya ingatkan anda: pada bahagian pertama kami menambah Flyway. Jom sambung.

Menambah pangkalan data pada docker-compose.yml

Peringkat seterusnya ialah menyediakan kerja dengan pangkalan data dalam docker-compose.yml utama. Mari tambah pangkalan data ke fail 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'
Saya juga menambah baris ini pada aplikasi kami:
depends_on:
 - jrtb-db
Ini bermakna kita menunggu pangkalan data bermula sebelum memulakan aplikasi. Seterusnya, anda boleh melihat penambahan dua lagi pembolehubah yang kami perlukan untuk bekerja dengan pangkalan data:
${BOT_DB_USERNAME}
${BOT_DB_PASSWORD}
Kami akan memasukkannya ke dalam docker-compose dengan cara yang sama seperti untuk bot telegram - melalui pembolehubah persekitaran. Saya melakukan ini supaya kita hanya mempunyai satu tempat di mana kita menetapkan nilai nama pengguna pangkalan data dan kata laluannya. Kami menghantarnya ke imej docker aplikasi kami dan ke bekas docker pangkalan data kami. Seterusnya kami perlu mengemas kini Dockerfile untuk mengajar SpringBoot kami untuk menerima pembolehubah untuk pangkalan data.
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"]
Sekarang kami menambah pembolehubah pangkalan data ke Dockerfile:
ENV BOT_DB_USERNAME=jrtb_db_user
ENV BOT_DB_PASSWORD=jrtb_db_password
Nilai pembolehubah akan berbeza. Yang kami akan masukkan ke dalam Dockerfile, bagaimanapun, memerlukan nilai lalai, jadi saya memasukkan beberapa. Kami mengembangkan baris terakhir dengan dua elemen, dengan bantuannya kami akan menghantar nama pengguna dan kata laluan DB ke pelancaran aplikasi:
"-Dspring.datasource.password=${BOT_DB_PASSWORD}", "-Dbot.username=${BOT_NAME}"
Baris terakhir dalam Dockerfile (yang bermula dengan ENTRYPOINT) mestilah tanpa pembalut. Jika anda membuat pemindahan, kod ini tidak akan berfungsi. Langkah terakhir ialah mengemas kini fail start.sh untuk menghantar pembolehubah ke pangkalan data.
#!/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
Kami sudah tahu cara menambah pembolehubah persekitaran sebelum menjalankan docker-compose. Untuk melakukan ini, anda hanya perlu melaksanakan export var_name=var_value.. Oleh itu, kami hanya menambah dua baris:
export BOT_DB_USERNAME='prod_jrtb_db_user'
export BOT_DB_PASSWORD='Pap9L9VVUkNYj99GCUCC3mJkb'
Di sinilah kami menetapkan nama pengguna dan kata laluan pangkalan data. Sudah tentu, adalah mungkin untuk melepasi pembolehubah ini semasa menjalankan skrip bash, seperti yang kita lakukan untuk nama dan token bot. Tetapi nampaknya saya ini tidak perlu. Untuk benar-benar mengakses pangkalan data, anda perlu mengetahui IP pelayan di mana pangkalan data akan digunakan, dan berada dalam senarai alamat IP yang dibenarkan untuk permintaan itu. Bagi saya, ini sudah cukup. Asas telah diletakkan: kini anda boleh melakukan perkara yang lebih mudah difahami untuk pembangun - tulis kod. Sebelum itu, kami melakukan perkara yang dilakukan oleh jurutera DevOps - menyediakan persekitaran.

Menambah lapisan Repositori

Biasanya aplikasi mempunyai tiga lapisan:
  1. Pengawal adalah pintu masuk ke dalam aplikasi.
  2. Perkhidmatan adalah tempat logik perniagaan berfungsi. Kami sudah mempunyai ini sebahagiannya: SendMessageService ialah wakil eksplisit bagi logik perniagaan.
  3. Repositori ialah tempat untuk bekerja dengan pangkalan data. Dalam kes kami, ini adalah bot telegram.
Sekarang kita akan menambah lapisan ketiga - repositori. Di sini kita akan menggunakan projek daripada ekosistem Spring - Spring Data. Anda boleh membaca tentang apa itu dalam artikel ini tentang Habré . Kita perlu mengetahui dan memahami beberapa perkara:
  1. Kami tidak perlu bekerja dengan JDBC: kami akan bekerja secara langsung dengan abstraksi yang lebih tinggi. Iaitu, simpan POJO yang sepadan dengan jadual dalam pangkalan data. Kami akan memanggil entiti kelas sedemikian , kerana ia secara rasmi dipanggil dalam Java Persistence API (ini adalah set antara muka biasa untuk bekerja dengan pangkalan data melalui ORM, iaitu abstraksi daripada bekerja dengan JDBC). Kami akan mempunyai kelas entiti yang kami akan simpan dalam pangkalan data, dan mereka akan ditulis tepat pada jadual yang kami perlukan. Kami akan menerima objek yang sama apabila mencari dalam pangkalan data.
  2. Spring Data menawarkan untuk menggunakan set antara muka mereka: JpaRepository , CrudRepository , dll... Terdapat antara muka lain: senarai penuh boleh didapati di sini . Keindahannya ialah anda boleh menggunakan kaedah mereka tanpa melaksanakannya(!). Selain itu, terdapat templat tertentu yang anda boleh menulis kaedah baharu dalam antara muka, dan ia akan dilaksanakan secara automatik.
  3. Musim bunga memudahkan pembangunan kami seberapa banyak yang boleh. Untuk melakukan ini, kita perlu mencipta antara muka kita sendiri dan mewarisi daripada yang diterangkan di atas. Dan supaya Spring tahu bahawa ia perlu menggunakan antara muka ini, tambah anotasi Repositori.
  4. Jika kita perlu menulis kaedah untuk bekerja dengan pangkalan data yang tidak wujud, maka ini juga tidak menjadi masalah - kita akan menulisnya. Saya akan menunjukkan kepada anda apa dan bagaimana untuk dilakukan di sana.
Dalam artikel ini, kami akan bekerja dengan menambah sepanjang laluan TelegramUser dan menunjukkan bahagian ini sebagai contoh. Kami akan mengembangkan selebihnya pada tugas lain. Iaitu, apabila kami melaksanakan perintah /start, kami akan menulis active = true kepada pangkalan data pengguna kami. Ini bermakna pengguna menggunakan bot. Jika pengguna sudah berada dalam pangkalan data, kami akan mengemas kini medan aktif = benar. Apabila melaksanakan perintah /stop, kami tidak akan memadamkan pengguna, tetapi hanya akan mengemas kini medan aktif kepada palsu, supaya jika pengguna ingin menggunakan bot itu semula, dia boleh memulakannya dan menyambung dari tempat dia berhenti. Dan supaya apabila menguji kami dapat melihat bahawa sesuatu sedang berlaku, kami akan mencipta arahan /stat: ia akan memaparkan bilangan pengguna aktif. Kami mencipta pakej repositori di sebelah bot, arahan, pakej perkhidmatan. Dalam pakej ini kami mencipta satu lagi entiti . Dalam pakej entiti kami mencipta kelas 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;
}
Di sini anda boleh melihat bahawa kami mempunyai semua anotasi daripada pakej javax.persistence. Ini ialah anotasi biasa yang digunakan untuk semua pelaksanaan ORM. Secara lalai, Spring Data Jpa menggunakan Hibernate, walaupun pelaksanaan lain boleh digunakan. Berikut ialah senarai anotasi yang kami gunakan:
  • Entiti - menunjukkan bahawa ini adalah entiti untuk bekerja dengan pangkalan data;
  • Jadual - di sini kita menentukan nama jadual;
  • Id - anotasi menyatakan medan mana yang akan menjadi Kunci Utama dalam jadual;
  • Lajur - tentukan nama medan daripada jadual.
Seterusnya, kami mencipta antara muka untuk bekerja dengan pangkalan data. Biasanya, nama antara muka sedemikian ditulis menggunakan templat - EntiryNameRepository. Kami akan mempunyai 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();
}
Di sini anda boleh melihat bagaimana saya menambah kaedah findAllByActiveTrue() , yang saya tidak laksanakan di mana-mana sahaja. Tetapi itu tidak akan menghalangnya daripada bekerja. Spring Data akan memahami bahawa ia perlu mendapatkan semua rekod daripada tg_user table yang medan aktifnya = true . Kami menambah perkhidmatan untuk bekerja dengan entiti TelegramUser (kami menggunakan penyongsangan kebergantungan daripada SOLID dalam konteks bahawa perkhidmatan entiti lain tidak boleh berkomunikasi secara langsung dengan repositori entiti lain - hanya melalui perkhidmatan entiti itu). Kami mencipta perkhidmatan TelegramUserService dalam pakej, yang buat masa ini akan mempunyai beberapa kaedah: simpan pengguna, dapatkan pengguna dengan IDnya dan paparkan senarai pengguna aktif. Mula-mula kita buat antara muka 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);
}
Dan, sebenarnya, pelaksanaan 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);
   }
}
Di sini perlu diperhatikan bahawa kami menggunakan suntikan kebergantungan (memperkenalkan contoh kelas) objek TelegramuserRepository menggunakan anotasi Autowired , dan pada pembina. Anda boleh melakukan ini untuk pembolehubah, tetapi ini adalah pendekatan yang disyorkan oleh pasukan Rangka Kerja Spring kepada kami.

Menambah statistik untuk bot

Seterusnya anda perlu mengemas kini arahan /start dan /stop. Apabila arahan /start digunakan, anda perlu menyimpan pengguna baharu dalam pangkalan data dan menetapkannya kepada active = true. Dan apabila terdapat /stop, kemas kini data pengguna: set active = false. Mari kita betulkan kelas 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);
   }
}
Di sini kami juga menghantar objek TelegramuserService kepada pembina, yang dengannya kami akan menyelamatkan pengguna baharu. Selanjutnya, menggunakan keseronokan Pilihan di Jawa, logik berikut berfungsi: jika kita mempunyai pengguna dalam pangkalan data, kita hanya menjadikannya aktif, jika tidak, kita mencipta yang aktif baharu. StopCommand:
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);
               });
   }
}
Kami lulus TelegramServiceTest ke StopCommand dengan cara yang sama. Logik tambahan ialah ini: jika kami mempunyai pengguna dengan ID sembang sedemikian, kami menyahaktifkannya, iaitu, kami menetapkan aktif = palsu. Bagaimana anda boleh melihat ini dengan mata anda sendiri? Mari buat arahan /stat baharu, yang akan memaparkan statistik bot. Pada peringkat ini, ini akan menjadi statistik mudah yang tersedia untuk semua pengguna. Pada masa hadapan, kami akan mengehadkannya dan membuat akses hanya untuk pentadbir. Akan ada satu entri dalam statistik: bilangan pengguna bot aktif. Untuk melakukan ini, tambah nilai STAT("/stat") ke CommandName. Seterusnya, buat kelas 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));
   }
}
Semuanya mudah di sini: kami mendapat senarai semua pengguna aktif menggunakan kaedah retrieveAllActiveUsers dan mendapatkan saiz koleksi. Kami juga kini perlu mengemas kini kelas menaik: CommandContainer dan JavarushTelegramBot supaya mereka belajar untuk menghantar perkhidmatan baharu yang kami perlukan. CommandContainer:
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);
   }

}
Di sini kami menambah arahan baharu pada peta dan menyampaikannya melalui pembina TelegramUserService. Tetapi dalam bot itu sendiri, hanya pembina akan berubah:
@Autowired
public JavarushTelegramBot(TelegramUserService telegramUserService) {
   this.commandContainer = new CommandContainer(new SendBotMessageServiceImpl(this), telegramUserService);
}
Sekarang kita lulus TelegramUserService sebagai hujah, menambah anotasi Autowired. Ini bermakna kami akan menerimanya daripada Konteks Aplikasi. Kami juga akan mengemas kini kelas HelpCommand supaya perintah statistik baharu muncul dalam huraian.

Ujian manual

Mari lancarkan pangkalan data daripada docker-compose-test.yml dan kaedah utama dalam kelas JavarushTelegramBotApplication. Seterusnya kita menulis satu set arahan:
  • /stat - kami menjangkakan bahawa jika pangkalan data kosong, akan ada sifar orang yang menggunakan bot ini;
  • /mula - mulakan bot;
  • /stat - kini kami menjangkakan bahawa bot akan digunakan oleh 1 orang;
  • /stop - hentikan bot;
  • /stat - kami menjangkakan bahawa sekali lagi akan ada 0 orang yang menggunakannya.
"Projek Java dari A hingga Z": Menambah semua yang berkaitan dengan pangkalan data.  Bahagian 2 - 2Jika keputusan adalah sama untuk anda, kami boleh mengatakan bahawa fungsi berfungsi dengan betul dan bot berfungsi dengan betul. Jika berlaku masalah, tidak mengapa: kami memulakan semula kaedah utama dalam mod nyahpepijat dan dengan jelas melalui keseluruhan laluan untuk mencari ralat itu.

Kami menulis dan mengemas kini ujian

Memandangkan kami menukar pembina, kami juga perlu mengemas kini kelas ujian. Dalam kelas AbstractCommandTest , kita perlu menambah satu lagi medan - kelas TelegramUserService , yang diperlukan untuk tiga arahan:
protected TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
Seterusnya, mari kemas kini kaedah init() dalam CommandContainer :
@BeforeEach
public void init() {
   SendBotMessageService sendBotMessageService = Mockito.mock(SendBotMessageService.class);
   TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
   commandContainer = new CommandContainer(sendBotMessageService, telegramUserService);
}
Dalam StartCommand anda perlu mengemas kini kaedah getCommand() :
@Override
Command getCommand() {
   return new StartCommand(sendBotMessageService, telegramUserService);
}
Juga dalam StopCommand:
@Override
Command getCommand() {
   return new StopCommand(sendBotMessageService, telegramUserService);
}
Seterusnya, mari kita lihat ujian baharu. Mari buat ujian tipikal untuk 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);
   }
}
Ini adalah mudah. Sekarang mari kita bercakap tentang bagaimana kita akan menguji bekerja dengan pangkalan data. Apa yang kami lakukan sebelum ini adalah ujian unit. Ujian integrasi menguji integrasi antara berbilang bahagian aplikasi. Contohnya, aplikasi dan pangkalan data. Di sini semuanya akan menjadi lebih rumit, kerana untuk ujian kita memerlukan pangkalan data yang digunakan. Oleh itu, apabila kami menjalankan ujian kami secara tempatan, kami mesti mempunyai pangkalan data yang dijalankan dari docker-compose-test.yml. Untuk menjalankan ujian ini, anda perlu menjalankan keseluruhan aplikasi SpringBoot. Kelas ujian mempunyai anotasi SpringBootTest yang akan memulakan aplikasi. Tetapi pendekatan ini tidak akan berfungsi untuk kami, kerana apabila aplikasi dilancarkan, bot telegram juga akan dilancarkan. Tetapi terdapat percanggahan di sini. Ujian akan dijalankan secara tempatan pada mesin kami dan secara terbuka melalui Tindakan GitHub. Agar ujian lulus dengan pelancaran keseluruhan aplikasi, kami mesti menjalankannya dengan data yang sah pada bot telegram: iaitu, dengan nama dan tokennya... Oleh itu, kami mempunyai dua pilihan:
  1. Jadi umumkan nama dan token bot itu dan harap semuanya akan baik-baik saja, tiada siapa yang akan menggunakannya dan mengganggu kami.
  2. Datang dengan cara lain.
Saya memilih pilihan kedua. Ujian SpringBoot mempunyai anotasi DataJpaTest , yang dicipta supaya apabila menguji pangkalan data, kami hanya menggunakan kelas yang kami perlukan dan membiarkan yang lain sahaja. Tetapi ini sesuai dengan kami, kerana bot telegram tidak akan dilancarkan sama sekali. Ini bermakna tidak perlu memberikannya nama dan token yang sah!))) Kami akan mendapat ujian di mana kami akan menyemak sama ada kaedah yang Spring Data laksanakan untuk kami berfungsi seperti yang kami harapkan. Adalah penting untuk ambil perhatian di sini bahawa kami menggunakan anotasi @ActiveProfiles("test") untuk menentukan penggunaan profil ujian. Dan inilah yang kami perlukan supaya kami boleh mengira sifat yang betul untuk pangkalan data kami. Adalah baik untuk menyediakan pangkalan data sebelum menjalankan ujian kami. Terdapat pendekatan sedemikian untuk perkara ini: tambah anotasi Sql pada ujian dan berikan koleksi nama skrip yang perlu dijalankan sebelum memulakan ujian:
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/telegram_users.sql"})
Bagi kami, ia akan terletak di sepanjang laluan ./src/test/resources/ + laluan yang dinyatakan dalam anotasi. Inilah rupa mereka:
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);
Inilah rupa ujian TelegramUserRepositoryIT kami sebagai hasilnya (seperti yang anda lihat, nama untuk ujian integrasi akan berbeza - kami menambah IT, bukan Ujian):
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());
   }
}
Kami menulis ujian, tetapi persoalan timbul: apakah yang akan berlaku dengan pelancaran proses CI kami di GitHub? Ia tidak akan mempunyai pangkalan data. Buat masa ini hanya akan ada binaan merah. Untuk melakukan ini, kami mempunyai tindakan GitHub, di mana kami boleh mengkonfigurasi pelancaran binaan kami. Sebelum menjalankan ujian, anda perlu menambah pelancaran pangkalan data dengan tetapan yang diperlukan. Ternyata, tidak banyak contoh di Internet, jadi saya menasihati anda untuk menyimpannya di suatu tempat. Mari kemas kini fail .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
Kini terdapat blok Sediakan MySQL baharu . Di dalamnya kami menambah MySQL pada proses CI kami, pada masa yang sama menentukan pembolehubah yang kami perlukan. Sekarang kami telah menambah semua yang kami mahu. Peringkat terakhir ialah menolak perubahan dan melihat bahawa binaan akan lulus dan menjadi hijau.

Mengemas kini dokumentasi

Mari kemas kini versi projek daripada 0.3.0-SNAPSHOT kepada 0.4.0-SNAPSHOT dalam pom.xml dan juga tambah pada RELEASE_NOTES:
## 0.4.0-SNAPSHOT

*   JRTB-1: added repository layer.
Selepas semua ini, kami membuat permintaan komit, tolak dan tarik. Dan yang paling penting, binaan kami adalah hijau!"Projek Java dari A hingga Z": Menambah semua yang berkaitan dengan pangkalan data.  Bahagian 2 - 3

Pautan berguna:

Semua perubahan boleh dilihat di sini dalam permintaan tarik yang dibuat . Terima kasih semua kerana membaca.

Senarai semua bahan dalam siri ini adalah pada permulaan artikel ini.

Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION