{"id":330562,"date":"2022-03-12T15:00:26","date_gmt":"2022-03-12T15:00:26","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=330562"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=330562","title":{"rendered":"<span>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c-\u0431\u043e\u0442\u0430 (Spring Boot, Kafka, PostgreSQL), \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0432\u0430\u044f<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<div class=\"persona\" persona=\"true\"><img decoding=\"async\" persona=\"true\" class=\"image persona__image\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8c0\/49e\/961\/8c049e961b4edfcde48ca540202c318a.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/8c0\/49e\/961\/8c049e961b4edfcde48ca540202c318a.png\"\/><\/p>\n<h5 class=\"persona__heading\" persona=\"true\">\u0418\u0432\u0430\u043d\u043e\u0432 \u041c\u0430\u043a\u0441\u0438\u043c<\/h5>\n<p persona=\"true\" class=\"persona__text\">\u041c\u043b\u0430\u0434\u0448\u0438\u0439 Java \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442<\/p>\n<\/div>\n<h3>\u0420\u0435\u0446\u0435\u043f\u0442 \u043f\u043e \u043f\u0440\u0438\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u0438\u044e \u0441\u0432\u043e\u0435\u0433\u043e \u00abTelegram-\u0424\u0440\u0430\u043d\u043a\u0435\u043d\u0448\u0442\u0435\u0439\u043d\u0430\u00bb<\/h3>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/024\/061\/a5f\/024061a5f40268acd7dc05f6bad2857f.png\" alt=\"\u0414\u0430\u0436\u0435 \u0447\u0435\u043b\u043e\u0432\u0435\u043a \u0441\u0440\u0435\u0434\u043d\u0438\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0435\u0439, \u0443\u043f\u043e\u0440\u043d\u043e \u0437\u0430\u043d\u0438\u043c\u0430\u044f\u0441\u044c \u043e\u0434\u043d\u0438\u043c \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043e\u043c, \u043d\u0435\u043f\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0434\u043e\u0441\u0442\u0438\u0433\u043d\u0435\u0442 \u0432\u00a0\u043d\u0435\u043c \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0445 \u043f\u043e\u0437\u043d\u0430\u043d\u0438\u0439. - \u00ab\u0424\u0440\u0430\u043d\u043a\u0435\u043d\u0448\u0442\u0435\u0439\u043d\u00bb \u041c\u044d\u0440\u0438 \u0428\u0435\u043b\u043b\u0438\" title=\"\u0414\u0430\u0436\u0435 \u0447\u0435\u043b\u043e\u0432\u0435\u043a \u0441\u0440\u0435\u0434\u043d\u0438\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0435\u0439, \u0443\u043f\u043e\u0440\u043d\u043e \u0437\u0430\u043d\u0438\u043c\u0430\u044f\u0441\u044c \u043e\u0434\u043d\u0438\u043c \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043e\u043c, \u043d\u0435\u043f\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0434\u043e\u0441\u0442\u0438\u0433\u043d\u0435\u0442 \u0432\u00a0\u043d\u0435\u043c \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0445 \u043f\u043e\u0437\u043d\u0430\u043d\u0438\u0439. - \u00ab\u0424\u0440\u0430\u043d\u043a\u0435\u043d\u0448\u0442\u0435\u0439\u043d\u00bb \u041c\u044d\u0440\u0438 \u0428\u0435\u043b\u043b\u0438\" width=\"964\" height=\"725\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/024\/061\/a5f\/024061a5f40268acd7dc05f6bad2857f.png\"\/><figcaption>\u0414\u0430\u0436\u0435 \u0447\u0435\u043b\u043e\u0432\u0435\u043a \u0441\u0440\u0435\u0434\u043d\u0438\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0435\u0439, \u0443\u043f\u043e\u0440\u043d\u043e \u0437\u0430\u043d\u0438\u043c\u0430\u044f\u0441\u044c \u043e\u0434\u043d\u0438\u043c \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043e\u043c, \u043d\u0435\u043f\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0434\u043e\u0441\u0442\u0438\u0433\u043d\u0435\u0442 \u0432\u00a0\u043d\u0435\u043c \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0445 \u043f\u043e\u0437\u043d\u0430\u043d\u0438\u0439. &#8212; \u00ab\u0424\u0440\u0430\u043d\u043a\u0435\u043d\u0448\u0442\u0435\u0439\u043d\u00bb \u041c\u044d\u0440\u0438 \u0428\u0435\u043b\u043b\u0438<\/figcaption><\/figure>\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442, \u0434\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f, \u0441\u0432\u043e\u0435\u0433\u043e \u0440\u043e\u0434\u0430 \u043c\u043e\u0435\u0439 \u043f\u0435\u0440\u0432\u043e\u0439, \u043d\u043e \u0432\u0441\u0435 \u0436\u0435 \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u044e\u0441\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0430\u043c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0431\u043e\u0442\u0430, \u043f\u0440\u0438\u043a\u0440\u0443\u0442\u0438\u0432 \u043a \u043d\u0435\u043c\u0443 \u0432\u0441\u0435 \u043e\u0431\u0435\u0449\u0430\u043d\u043d\u044b\u0435 \u0432\u044b\u0448\u0435 \u0441\u0432\u0438\u0441\u0442\u0435\u043b\u043a\u0438-\u0442\u0430\u0440\u0430\u0445\u0442\u0435\u043b\u043a\u0438.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u0438 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 2 \u0447\u0430\u0441\u0442\u0438, \u043f\u0435\u0440\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c &#8212; \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0431\u043e\u0442\u0430 \u0441 \u043e\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u043b\u043e\u0433\u043e\u0432 (Kafka Producer) \u0438 \u0437\u0430\u043f\u0438\u0441\u044c\u044e \u0438\u0445 \u0432 \u0411\u0414, <a href=\"https:\/\/habr.com\/ru\/sandbox\/165371\/\" rel=\"noopener noreferrer nofollow\">\u0432\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c<\/a> &#8212; \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0432\u0441\u0435\u0445 \u043b\u043e\u0433\u043e\u0432 (Kafka Consumer).<\/p>\n<h2>\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b:<\/h2>\n<ol>\n<li>\n<p><a href=\"https:\/\/core.telegram.org\/bots#6-botfather\" rel=\"noopener noreferrer nofollow\">\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0431\u043e\u0442\u0430<\/a><\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Spring Boot \u043f\u0440\u043e\u0435\u043a\u0442, \u043f\u0440\u043e\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043e\u0440 \u0432 <a href=\"https:\/\/www.jetbrains.com\/ru-ru\/idea\/\" rel=\"noopener noreferrer nofollow\"><strong>IntelliJ IDEA<\/strong><\/a>, \u043b\u0438\u0431\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <a href=\"https:\/\/start.spring.io\/\" rel=\"noopener noreferrer nofollow\">Spring Initializr<\/a>. (\u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0441\u0431\u043e\u0440\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f <a href=\"https:\/\/gradle.org\/install\/\" rel=\"noopener noreferrer nofollow\">Gradle<\/a>)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/post\/496182\/\" rel=\"noopener noreferrer nofollow\">Kafka<\/a> (\u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0442\u043e\u043f\u0438\u043a\u043e\u0432 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <a href=\"https:\/\/www.conduktor.io\/download\" rel=\"noopener noreferrer nofollow\">Conductor<\/a>)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/www.postgresql.org\/download\/\" rel=\"noopener noreferrer nofollow\">PostgreSQL<\/a> (\u0434\u043b\u044f \u043a\u043e\u043c\u0444\u043e\u0440\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <a href=\"https:\/\/dbeaver.io\/download\/\" rel=\"noopener noreferrer nofollow\">DBeaver<\/a>)<\/p>\n<\/li>\n<\/ol>\n<details class=\"spoiler\">\n<summary>\u0415\u0441\u043b\u0438 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0443\u0442 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0441 \u0432\u043e\u0441\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041f\u0440\u043e\u0448\u0443 \u043f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043a\u043e\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u0432\u043e\u0437\u043d\u0438\u043a\u0448\u0438\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u043d\u0430 \u0432\u0441\u044f\u043a\u0438\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 &#8212; \u0432\u043e\u0442 \u043c\u043e\u0439 <a href=\"https:\/\/github.com\/RushianHaker\/secretary-bot\" rel=\"noopener noreferrer nofollow\">git<\/a><\/p>\n<\/div>\n<\/details>\n<h2>\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0441 \u043d\u0430\u0440\u0435\u0437\u043a\u0438:<\/h2>\n<p>\u041f\u0435\u0440\u0432\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c build.grable \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438<\/p>\n<details class=\"spoiler\">\n<summary>build.grable<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\">buildscript {     repositories {         mavenCentral()     } }  plugins {     id 'org.springframework.boot' version '2.4.2'     id 'io.spring.dependency-management' version '1.0.11.RELEASE'     id 'java' }  apply from: 'build-test.gradle'  group 'com.sercetary.bot' sourceCompatibility = '14'  configurations {     compileOnly {         extendsFrom annotationProcessor     } }  repositories {     mavenCentral() }  configurations.all {     exclude module: 'slf4j-log4j12' }  dependencies {     implementation 'org.springframework.boot:spring-boot-starter-web:2.5.6'     implementation 'org.springframework.boot:spring-boot-starter-jdbc:2.5.6'     implementation 'org.springframework.data:spring-data-commons:2.6.0'     implementation 'org.springframework.kafka:spring-kafka:2.7.6'     implementation 'org.postgresql:postgresql:42.3.1'     implementation 'com.h2database:h2:1.4.200'      implementation group: 'org.telegram', name: 'telegrambots-abilities', version: '5.3.0'     implementation group: 'org.telegram', name: 'telegrambots', version: '5.3.0'      compile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.7.29'     compileOnly 'org.projectlombok:lombok:1.18.22'     annotationProcessor 'org.projectlombok:lombok:1.18.22' }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u0440\u0430\u0437\u0443 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b Kafka \u043e\u043f\u0438\u0448\u0435\u043c application.yml, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430\u0448\u0435\u0433\u043e kafka producer<\/p>\n<details class=\"spoiler\">\n<summary>application.yml<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\">server:   port: 9000 spring:   kafka:     producer:       bootstrap-servers: localhost:9092       key-serializer: org.apache.kafka.common.serialization.StringSerializer       value-serializer: org.apache.kafka.common.serialization.StringSerializer<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 application.properties<\/p>\n<details class=\"spoiler\">\n<summary>application.properties<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># HTTP port for incoming requests server.port=8081  app.http.bot=change-me telegram-bot.name=change-me telegram-bot.token=change-me  # Bot db app.db.bot-db.url=jdbc:postgresql:\/\/localhost:5432\/change-me app.db.bot-db.driver=org.postgresql.Driver app.db.bot-db.user=change-me app.db.bot-db.password=change-me app.db.bot-db.pool-size=10  # logging logging.level.root=INFO logging.level.org.springframework.web=DEBUG logging.level.ru.centerinform.webhook=TRACE logging.file.name=change-me <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0425\u043e\u0440\u043e\u0448\u043e, \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u0435\u0433\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/75f\/734\/e5a\/75f734e5aff1197747ee530acc438f1a.png\" alt=\"\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\" title=\"\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\" width=\"354\" height=\"693\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/75f\/734\/e5a\/75f734e5aff1197747ee530acc438f1a.png\"\/><figcaption>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/figcaption><\/figure>\n<p>\u041f\u0430\u043a\u0435\u0442\u044b:<\/p>\n<ul>\n<li>\n<p>config &#8212; \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0431\u0438\u043d\u043e\u0432 \u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>controller &#8212; \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<\/li>\n<li>\n<p>dto &#8212; \u0445\u0440\u0430\u043d\u0438\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043c\u043e\u0434\u0435\u043b\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0411\u0414<\/p>\n<\/li>\n<li>\n<p>exceptions &#8212; \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u043f\u0430\u043a\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a<\/p>\n<\/li>\n<li>\n<p>repository &#8212; \u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0411\u0414<\/p>\n<\/li>\n<li>\n<p>service &#8212; \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0431\u0438\u0437\u043d\u0435\u0441 \u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<\/ul>\n<h2>\u0421\u0435\u0439\u0447\u0430\u0441 \u043c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0438\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b \u0438 \u043c\u0430\u0440\u0438\u043d\u0443\u0435\u043c:<\/h2>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0431\u0438\u043d\u043e\u0432:<\/h4>\n<p>&#8212; \u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f <a href=\"https:\/\/habr.com\/ru\/post\/334448\/\" rel=\"noopener noreferrer nofollow\">\u0431\u0438\u043d\u043e\u0432<\/a> \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 config, \u0442\u0443\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 TelegramBotsApi \u0438 ObjectMapper<\/p>\n<details class=\"spoiler\">\n<summary>AppConfig<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Configuration public class AppConfig {      @Bean     ObjectMapper customObjectMapper() {         return new ObjectMapper();     }      @Bean     TelegramBotsApi telegramBotsApi() throws TelegramApiException{         return new TelegramBotsApi(DefaultBotSession.class);     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u0412\u043d\u0443\u0442\u0440\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 DbConfig, \u0435\u0441\u0442\u044c \u043a\u043b\u0430\u0441\u0441 SpringDataJdbcProperties, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 SpringDataJdbc<\/p>\n<details class=\"spoiler\">\n<summary>DbConfig<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Configuration public class DbConfig extends DefaultDbConfig {      @Bean     @Qualifier(\"bot-db\")     @ConfigurationProperties(prefix = \"app.db.bot-db\")     SpringDataJdbcProperties gitlabJdbcProperties() {         return new SpringDataJdbcProperties();     }      @Bean     @Qualifier(\"bot-db\")     public DataSource gitlabDataSource(@Qualifier(\"bot-db\") SpringDataJdbcProperties properties) {         return hikariDataSource(\"db\", properties);     }      @Bean     @Qualifier(\"bot-db\")     JdbcTemplate gitlabJdbcTemplate(@Qualifier(\"bot-db\") DataSource dataSource) {         return new JdbcTemplate(dataSource);     }      @Data     @NoArgsConstructor     public static class SpringDataJdbcProperties {          \/\/ constants         private static final String H2_DATABASE_DRIVER = \"org.h2.Driver\";          \/**          * JDBC URL property          *\/         String url;         \/**          * JDBC driver class name property          *\/         String driver;         \/**          * JDBC username property          *\/         String user;         \/**          * JDBC password property          *\/         String password;         \/**          * Hikari \/ Vertica maxPoolSize property          *\/         String poolSize;         \/**          * Minimum pool size          *\/         int minPoolSize = 4;         \/**          * Maximum pool size          *\/         int maxPoolSize = 10;         \/**          * This property controls the maximum amount of time (in milliseconds) that a connection is allowed to          * sit idle in the pool. A value of 0 means that idle connections are never removed from the pool.          *\/         long idleTimeout;         \/**          * This property controls the maximum lifetime of a connection in the pool. When a connection          * reaches this timeout, even if recently used, it will be retired from the pool.          * An in-use connection will never be retired, only when it is idle will it be removed          *\/         long maxLifetime;         \/**          * Bulk insert size          *\/         Integer bulkSize;           \/**          * All-args constructor for {@link SpringDataJdbcProperties#toString()} (logging)          *          * @param url JDBC driver class name property          * @param driver JDBC driver class name property          * @param user JDBC username property          * @param password JDBC password property          * @param poolSize Hikari \/ Vertica maxPoolSize property          * @param bulkSize bulk insert size          *\/         public SpringDataJdbcProperties(                 String url, String driver, String user, String password, String poolSize, Integer bulkSize) {             this.url = url;             this.driver = driver;             this.user = user;             this.password = password;             this.poolSize = poolSize;             this.bulkSize = bulkSize;         }           \/**          * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0438\u0441\u0442\u0438\u043d\u0443, \u0435\u0441\u043b\u0438 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 in-memory H2 database          *          * @return \u0438\u0441\u0442\u0438\u043d\u0430, \u0435\u0441\u043b\u0438 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 in-memory H2 database          *\/         public boolean isH2Database() {             return driver.equals(H2_DATABASE_DRIVER);         }          \/**          * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON          *          * @return \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON          *\/         @Override         public String toString() {             var props = new SpringDataJdbcProperties(                     url, driver, user, ((password == null) || password.isEmpty()) ? \"\" : \"*****\", poolSize, bulkSize);             return Json.encode(props);         }      }  }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u0438\u043d\u043e\u0432<\/p>\n<details class=\"spoiler\">\n<summary>DefaultDbConfig<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Slf4j class DefaultDbConfig {      protected DataSource hikariDataSource(String tag, DbConfig.SpringDataJdbcProperties properties) {         log.info(\"[{}] \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0411\u0414: [{}]\", tag, properties.toString());          HikariDataSource ds = new HikariDataSource();         ds.setJdbcUrl(properties.getUrl());         ds.setDriverClassName(properties.getDriver());         ds.setUsername(properties.getUser());         ds.setPassword(properties.getPassword());         ds.setMaximumPoolSize(Integer.parseInt(properties.getPoolSize()));         return ds;     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u041f\u043e\u0441\u043b\u0435 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0443\u0442\u0438\u043b\u0438\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/p>\n<details class=\"spoiler\">\n<summary>Json<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">public class Json {     static final ObjectMapper mapper = new ObjectMapper();      \/**      * Encode instance as JSON      *      * @param obj instance      * @return JSON      *\/     public static String encode(Object obj) {         try {             return mapper.writeValueAsString(obj);         } catch (JsonProcessingException e) {             return obj.toString();         }     }      public static &lt;T> T decode(String json, Class&lt;T> clazz) throws JsonProcessingException {         return mapper.readValue(json, clazz);     }<\/code><\/pre>\n<\/div>\n<\/details>\n<h4>\u0414\u0430\u043b\u0435\u0435 \u043c\u044b \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0438\u0437 \u0432\u043d\u0435<\/h4>\n<p> &#8212; \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0438\u0437 \u0411\u0414<\/p>\n<details class=\"spoiler\">\n<summary>UsersController<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Slf4j @RestController @RequestMapping(\"${app.http.bot\") @RequiredArgsConstructor @SuppressWarnings(\"unused\") public class UsersController {      private final UserService userService;      \/**      * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441 \u043d\u0438\u043c\u0438 \u043f\u043b\u0430\u043d\u0430\u043c\u0438      *\/     @RequestMapping(path = \"\/users_idea\", method = RequestMethod.GET)     public List&lt;User> getIdeaList() {         log.debug(\"Method - getIdeaList was called\");         return userService.getUserList();     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<h4>\u041f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043c\u043e\u0434\u0435\u043b\u0438<\/h4>\n<p>&#8212; \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f User, \u0430 \u0442\u0430\u043a \u0436\u0435 \u0435\u0433\u043e \u043c\u0430\u043f\u043f\u0435\u0440 UserMapper, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0411\u0414 \u0438 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043f\u043e\u043b\u0435\u0439 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435<\/p>\n<details class=\"spoiler\">\n<summary>User<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Data @RequiredArgsConstructor public class User {     \/**      * user's id      *\/     @JsonProperty(\"id\")     private final int id;     \/**      * user's name      *\/     @JsonProperty(\"name\")     private final String name;     \/**      * description      *\/     @JsonProperty(\"description\")     private final String description;      private String startWord = \"\";      @Override     public String toString() { return startWord + description; } }<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>UserMapper<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Slf4j public class UserMapper implements RowMapper&lt;User> {      @Override     public User mapRow(ResultSet rs, int rowNum) throws SQLException {         var entity = new User(                 rs.getInt(\"id\"),                 rs.getString(\"user_name\"),                 rs.getString(\"description\")                 );         log.trace(\"mapRow(): entity = [{}]\", entity);         return entity;     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<h4>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 exception<\/h4>\n<details class=\"spoiler\">\n<summary>\u0414\u043b\u044f \u0447\u0435\u0433\u043e \u043e\u043d\u0438 \u043d\u0443\u0436\u043d\u044b<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0418\u0445 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043e\u0448\u0438\u0431\u043e\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0442\u0438 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u0431\u043e\u0442 \u043d\u0435 \u0441\u043b\u043e\u043c\u0430\u043b\u0441\u044f \u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043b \u0441\u0432\u043e\u044e \u0440\u0430\u0431\u043e\u0442\u0443.<\/p>\n<\/div>\n<\/details>\n<p>&#8212; BaseException &#8212; \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 RuntimeException, \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 2 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 &#8212; \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0438 \u0442\u0435\u043b\u043e \u043e\u0448\u0438\u0431\u043a\u0438<\/p>\n<details class=\"spoiler\">\n<summary>BaseException<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Slf4j public class BaseException extends RuntimeException{      public BaseException(String msg, Throwable t) {         super(msg, t);         log.error(msg, t);     }      public BaseException(String msg) {         super(msg);         log.error(msg);     }  }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; NotFoundException &#8212; \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0432\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u043e\u0442\u0432\u0435\u0442 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d, \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 BaseException<\/p>\n<details class=\"spoiler\">\n<summary>NotFoundException<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@ResponseStatus(HttpStatus.NOT_FOUND) public class NotFoundException extends BaseException {      private final static String MESSAGE = \"Not Found\";      public NotFoundException(Throwable t) {         super(MESSAGE, t);     }      public NotFoundException() {         super(MESSAGE);     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; DbException &#8212; \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0435\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441 \u0411\u0414, \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 RuntimeException<\/p>\n<details class=\"spoiler\">\n<summary>DbException<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public class DbException extends RuntimeException {      private static final String MESSAGE = \"\u041e\u0448\u0438\u0431\u043a\u0430 \u0411\u0414\";      public DbException(String message) {         super(message);     }      public DbException(Throwable cause) {         super(MESSAGE, cause);     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<h4>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0411\u0414, \u0441\u043e\u0437\u0434\u0430\u0435\u043c repository<\/h4>\n<p>&#8212; \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b, \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0437\u0430\u043f\u0438\u0441\u044f\u043c\u0438 \u0432 \u0411\u0414 <\/p>\n<details class=\"spoiler\">\n<summary>IUserRepository<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">public interface IUserRepository {      \/**      * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043f\u043e id      *      * @return \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     User getById(int id);      \/**      * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043f\u0438\u0441\u0435\u0439      *      * @return \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     List&lt;User> getUserList();      \/**      * \u0412\u0441\u0442\u0430\u0432\u043a\u0430 \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438      *      * @param entity \u043d\u043e\u0432\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     void insert(User entity);      \/**      * \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0438      *      * @param entity \u0443\u0434\u0430\u043b\u044f\u0435\u043c\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     void delete(User entity); }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430<\/p>\n<details class=\"spoiler\">\n<summary>UserRepository<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Slf4j @Repository public class UserRepository implements IUserRepository {      \/\/ constants     private static final String SQL_SELECT_BY_NAME = \"\" +             \"SELECT id, user_name, description FROM user_table WHERE id=?\";     private static final String SQL_SELECT_LIST = \"\" +             \"SELECT id, user_name, description FROM user_table\";     private static final String SQL_INSERT = \"\" +             \"INSERT INTO user_table (user_name, description) VALUES (?, ?)\";     private static final String SQL_DELETE = \"\" +             \"DELETE FROM user_table WHERE id = ?\";      protected final static UserMapper USER_MAPPER = new UserMapper();      \/\/ beans     protected final JdbcTemplate template;       \/**      * Req-args constructor for Spring DI      *\/     public UserRepository(@Qualifier(\"bot-db\") JdbcTemplate template) {         this.template = template;     }      \/**      * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043f\u043e id      *      * @return \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     @Override     public User getById(int id) throws DbException {         try {             return DataAccessUtils.singleResult(                     template.query(SQL_SELECT_BY_NAME, USER_MAPPER, id));         } catch (DataAccessException exception) {             throw new DbException(exception);         }     }      \/**      * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043f\u0438\u0441\u0435\u0439      *      * @return \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     @Override     public List&lt;User> getUserList() throws DbException {         try {             return template.query(SQL_SELECT_LIST, USER_MAPPER);         } catch (DataAccessException exception) {             throw new DbException(exception);         }     }      \/**      * \u0412\u0441\u0442\u0430\u0432\u043a\u0430 \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438      *      * @param entity \u043d\u043e\u0432\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     @Override     public void insert(User entity) throws DbException {         try {             \/\/ \u0412 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432\u0441\u0435 \u043f\u043e\u043b\u044f \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u043a\u0440\u043e\u043c\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430, \u0442.\u043a. \u043e\u043d serial \u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u043e\u043c             var result = template.update(SQL_INSERT,                     entity.getName(),                     entity.getDescription());             if (result != 1) log.trace(\"UserRepository.update() with {} rows inserted\", entity);             log.info(\"insert({}) result={}\", entity, result);         } catch (DataAccessException exception) {             throw new DbException(exception);         }     }      \/**      * \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0438      *      * @param entity \u0443\u0434\u0430\u043b\u044f\u0435\u043c\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     @Override     public void delete(User entity) throws DbException {         try {             var result = template.update(SQL_DELETE, entity.getId());             if (result != 1) log.trace(\"UserRepository.delete() with {} rows inserted\", entity);             log.info(\"delete({}) result={}\", entity, result);         } catch (DataAccessException exception) {             throw new DbException(exception);         }     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u0414\u0430\u043b\u0435\u0435 \u0443 \u043d\u0430\u0441 \u0438\u0434\u0435\u0442 \u043b\u043e\u0433\u0438\u043a\u0430 \u0431\u043e\u0442\u0430, \u0442\u0443\u0442 \u0432\u0441\u0435 \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u043e, \u0432 \u043e\u0442\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u043d\u043e\u043c <strong>onUpdateReceived<\/strong> \u043c\u0435\u0442\u043e\u0434\u0435 \u043e\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f <strong>TelegramLongPollingBot<\/strong> \u043c\u044b \u043f\u0438\u0448\u0435\u043c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0447\u0430\u0442\u0430 \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c, \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c <a href=\"https:\/\/core.telegram.org\/api\/obtaining_api_id\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>, \u0442\u0430\u043a \u0436\u0435 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0435\u0441\u0442\u044c \u0432\u044b\u0437\u043e\u0432 \u043d\u0430\u0448\u0435\u0433\u043e <strong>producer<\/strong> \u0438 \u0437\u0430\u043f\u0438\u0441\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0411\u0414<\/p>\n<details class=\"spoiler\">\n<summary>TelegramBot<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Slf4j @Getter @Component public class TelegramBot extends TelegramLongPollingBot {      private Message requestMessage = new Message();     private final SendMessage response = new SendMessage();     private final Producer producerService;     private final UserService userService;      private final String botUsername;     private final String botToken;      public TelegramBot(             TelegramBotsApi telegramBotsApi,             @Value(\"${telegram-bot.name}\") String botUsername,             @Value(\"${telegram-bot.token}\") String botToken,             Producer producerService, UserService userService) throws TelegramApiException {         this.botUsername = botUsername;         this.botToken = botToken;         this.producerService = producerService;         this.userService = userService;          telegramBotsApi.registerBot(this);     }      \/**      * \u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 \u043c\u0435\u0442\u043e\u0434 GetUpdates.      *      * @param request \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435      *\/     @SneakyThrows     @Override     public void onUpdateReceived(Update request) {         requestMessage = request.getMessage();         response.setChatId(requestMessage.getChatId().toString());          var entity = new User(                 0, requestMessage.getChat().getUserName(),                 requestMessage.getText());          if (request.hasMessage() &amp;&amp; requestMessage.hasText())             log.info(\"Working onUpdateReceived, request text[{}]\", request.getMessage().getText());          if (requestMessage.getText().equals(\"\/start\"))             defaultMsg(response, \"\u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u043f\u043e\u043a\u0430\u0437\u0430 \u0441\u043f\u0438\u0441\u043a\u0430 \u043c\u044b\u0441\u043b\u0435\u0439: \\n \" + \"\/idea - \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043c\u044b\u0441\u043b\u0438\");         else if (requestMessage.getText().equals(\"\/idea\"))             onIdea(response);         else             defaultMsg(response, \"\u042f \u0437\u0430\u043f\u0438\u0441\u0430\u043b \u0432\u0430\u0448\u0443 \u043c\u044b\u0441\u043b\u044c :) \\n \");          log.info(\"Working, text[{}]\", requestMessage.getText());          if (requestMessage.getText().startsWith(\"\/\")) {             entity.setStartWord(\"\u043a\u043e\u043c\u0430\u043d\u0434\u0430: \");             producerService.sendMessage( entity);         } else {             entity.setStartWord(\"\u043c\u044b\u0441\u043b\u044c: \");             producerService.sendMessage( entity);             userService.insert(entity);         }     }      \/**      * \u041c\u0435\u0442\u043e\u0434 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u043c \u043c\u044b\u0441\u043b\u0435\u0439 - \u043f\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \"\/idea\"      *      * @param response - \u043c\u0435\u0442\u043e\u0434 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f      *\/     private void onIdea(SendMessage response) throws TelegramApiException {         if (userService.getUserList().isEmpty()) {             defaultMsg(response, \"\u0412 \u0441\u043f\u0438\u0441\u043a\u0435 \u043d\u0435\u0442 \u043c\u044b\u0441\u043b\u0435\u0439. \\n\");         } else {             defaultMsg(response, \"\u0412\u043e\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0430\u0448\u0438\u0445 \u043c\u044b\u0441\u043b\u0435\u0439: \\n\");             for (User txt : userService.getUserList()) {                 response.setText(txt.toString());                 execute(response);             }         }     }      \/**      * \u0428\u0430\u0431\u043e\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e      *      * @param response - \u043c\u0435\u0442\u043e\u0434 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f      * @param msg - \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435      *\/     private void defaultMsg(SendMessage response, String msg) throws TelegramApiException {         response.setText(msg);         execute(response);     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0424\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u043a\u043e\u0434\u0430 \u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0432 Kafka \u0438 \u0437\u0430\u043f\u0438\u0441\u044c\u044e \u0432 \u0411\u0414<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">        if (requestMessage.getText().startsWith(\"\/\")) {             entity.setStartWord(\"\u043a\u043e\u043c\u0430\u043d\u0434\u0430: \");             producerService.sendMessage( entity);         } else {             entity.setStartWord(\"\u043c\u044b\u0441\u043b\u044c: \");             producerService.sendMessage( entity);             userService.insert(entity);         }<\/code><\/pre>\n<\/div>\n<\/details>\n<h4>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0431\u0438\u0437\u043d\u0435\u0441 \u043b\u043e\u0433\u0438\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/h4>\n<p>&#8212; BaseService &#8212; \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<details class=\"spoiler\">\n<summary>BaseService<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">public class BaseService {      \/**      * \u041e\u0431\u0451\u0440\u0442\u043a\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430      *      * @param result \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442      * @return \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442      * @throws NotFoundException \u0435\u0441\u043b\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 null      *\/     public &lt;T> T wrapResult(T result) {         if(result == null)             throw new NotFoundException();         return result;     }      \/**      * \u041e\u0431\u0451\u0440\u0442\u043a\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430      *      * @param result \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442      * @return \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442      * @throws NotFoundException \u0435\u0441\u043b\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 null \u0438\u043b\u0438 \u043f\u0443\u0441\u0442\u043e\u0439      *\/     public &lt;T> List&lt;T> wrapResults(List&lt;T> result) {         if(result == null || result.size() == 0)             throw new NotFoundException();         return result;     }  }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u041a\u043b\u0430\u0441\u0441 UserService \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u043d\u0430\u0448\u0438\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u043c IUserRepository \u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0432 \u0441\u0435\u0431\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0437\u0430\u043f\u0438\u0441\u044f\u043c\u0438 \u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445 \u0432 \u0411\u0414<\/p>\n<details class=\"spoiler\">\n<summary>UserService<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Service @Slf4j @RequiredArgsConstructor public class UserService extends BaseService {      \/\/beans     protected final IUserRepository repo;      \/**      * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043f\u0438\u0441\u0435\u0439      *      * @return \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043f\u0438\u0441\u0435\u0439      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     public List&lt;User> getUserList() {         log.trace(\"#### getUserList() - working\");         return wrapResults(repo.getUserList());     }      \/**      * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043f\u043e id      *      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     public User getById(int id) {         log.trace(\"#### getById() [id={}]\", id);         return wrapResult(repo.getById(id));     }      \/**      * \u0412\u0441\u0442\u0430\u0432\u043a\u0430 \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438      *      * @param entity \u043d\u043e\u0432\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     public void insert(User entity) {         log.trace(\"#### insert() [entity={}]\", entity);         repo.insert(entity);     }      \/**      * \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0438      *      * @param entity \u0443\u0434\u0430\u043b\u044f\u0435\u043c\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c      * @throws DbException \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0411\u0414      *\/     public void delete(User entity) {         log.trace(\"#### delete() [entity={}]\", entity);         repo.delete(entity);     }  }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u041a\u043b\u0430\u0441\u0441 Producer, \u043a\u0430\u043a \u0440\u0430\u0437 \u0442\u043e\u0442 \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0448\u043b\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432 \u0442\u043e\u043f\u0438\u043a users, \u0430 \u0442\u0430\u043a \u0436\u0435 \u0437\u0434\u0435\u0441\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u0444\u043e\u0440\u043c\u0430\u0442 \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043d \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442<\/p>\n<details class=\"spoiler\">\n<summary>Producer<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Service @Slf4j public class Producer {      private static final String TOPIC = \"users\";     protected final IUserRepository repo;      @Autowired     private KafkaTemplate&lt;String, String> kafkaTemplate;      public Producer(IUserRepository repo) {         this.repo = repo;     }      public void sendMessage(User user) {         if (user.getName() == null || user.getDescription().isEmpty()) log.info(\"#### Empty name\/description message\");         log.info(\"#### Producing message [user={}]\", user);         kafkaTemplate.send(TOPIC, \"Writing in log -> \" + user);     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<h4>\u0412 \u043a\u043e\u043d\u0446\u0435 \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0432\u0441\u0435 \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0435<\/h4>\n<details class=\"spoiler\">\n<summary>WebHookApp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Slf4j @SpringBootApplication public class WebHookApp {     public static void main(String[] args) {         SpringApplication.run(WebHookApp.class, args);     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0437\u0430\u043c\u0430\u0440\u0438\u043d\u043e\u0432\u0430\u043b\u0438 \u0432\u0441\u0435 \u0438\u043d\u0433\u0440\u0438\u0434\u0438\u0435\u043d\u0442\u044b \u0438 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b\u0438 \u0431\u043b\u044e\u0434\u043e \u043a \u0437\u0430\u043f\u0435\u043a\u0430\u043d\u0438\u044e:<\/h2>\n<p>&#8212; \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c, \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u0430 \u043b\u0438 Kafka<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/544\/243\/e5a\/544243e5a3f499a48b5226a71b95636f.png\" alt=\"\u0437\u0430\u043f\u0443\u0441\u043a \u043f\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u0435 - sudo su systemctl start kafka\" title=\"\u0437\u0430\u043f\u0443\u0441\u043a \u043f\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u0435 - sudo su systemctl start kafka\" width=\"729\" height=\"252\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/544\/243\/e5a\/544243e5a3f499a48b5226a71b95636f.png\"\/><figcaption>\u0437\u0430\u043f\u0443\u0441\u043a \u043f\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u0435 &#8212; sudo su systemctl start kafka<\/figcaption><\/figure>\n<p>&#8212; \u041f\u043e\u0441\u043b\u0435, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c Conductor \u0438 \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0440\u0430\u0431\u043e\u0442\u0435\u0442 \u0431\u0440\u043e\u043a\u0435\u0440 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439, \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0442\u0443\u0442 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0442\u043e\u043f\u0438\u043a users, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0443\u0442 \u043b\u0435\u0442\u0435\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043d\u0430\u0448\u0438\u043c producer<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f77\/787\/9aa\/f777879aad640f1fbcc378ba08c8eb1b.png\" alt=\"\u0417\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440\" title=\"\u0417\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440\" width=\"1379\" height=\"745\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f77\/787\/9aa\/f777879aad640f1fbcc378ba08c8eb1b.png\"\/><figcaption>\u0417\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440<\/figcaption><\/figure>\n<p>&#8212; \u0414\u0430\u043b\u0435\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c DBeaver \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c 2 \u0442\u0430\u0431\u043b\u0438\u0446\u044b (log \u0438 user_table), \u0432\u043e\u0442 \u0441\u0445\u0435\u043c\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446: <\/p>\n<pre><code class=\"sql\">CREATE TABLE public.log ( id serial4 NOT NULL, message varchar(500) NOT NULL, date_time date NOT NULL, topic varchar(100) NOT NULL, CONSTRAINT log_pkey PRIMARY KEY (id) );<\/code><\/pre>\n<pre><code class=\"sql\">CREATE TABLE public.user_table ( id serial4 NOT NULL, user_name varchar(100) NOT NULL, description varchar(500) NULL, CONSTRAINT user_table_pkey PRIMARY KEY (id) );<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ce5\/f86\/990\/ce5f8699013d657c6c0d71528dbc1325.png\" alt=\"\u0421\u0445\u0435\u043c\u0430 \u0411\u0414 public\" title=\"\u0421\u0445\u0435\u043c\u0430 \u0411\u0414 public\" width=\"1843\" height=\"1045\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ce5\/f86\/990\/ce5f8699013d657c6c0d71528dbc1325.png\"\/><figcaption>\u0421\u0445\u0435\u043c\u0430 \u0411\u0414 public<\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a00\/9a1\/e31\/a009a1e31faeebf410f8a7fe8c4d1e16.png\" alt=\"\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 log\" title=\"\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 log\" width=\"1502\" height=\"918\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a00\/9a1\/e31\/a009a1e31faeebf410f8a7fe8c4d1e16.png\"\/><figcaption>\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 log<\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/157\/fb1\/7ba\/157fb17ba71ee7cb677bb92981b988d6.png\" alt=\"\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 user_table\" title=\"\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 user_table\" width=\"1497\" height=\"884\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/157\/fb1\/7ba\/157fb17ba71ee7cb677bb92981b988d6.png\"\/><figcaption>\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 user_table<\/figcaption><\/figure>\n<h2>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0431\u043b\u044e\u0434\u043e \u0437\u0430\u043f\u0435\u043a\u043b\u043e\u0441\u044c \u0438 \u0433\u043e\u0442\u043e\u0432\u043e \u043a \u043f\u043e\u0434\u0430\u0447\u0435:<\/h2>\n<p>&#8212; \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0432\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0438 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/147\/bb1\/e20\/147bb1e20aae4d7917c0738559f1c417.png\" alt=\"Spring logs\" title=\"Spring logs\" width=\"1845\" height=\"1017\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/147\/bb1\/e20\/147bb1e20aae4d7917c0738559f1c417.png\"\/><figcaption>Spring logs<\/figcaption><\/figure>\n<p>&#8212; \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c \u0438 \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043d\u0430 \u0432\u043a\u0443\u0441 \u043d\u0430\u0448\u0435\u0433\u043e &#171;\u0424\u0440\u0430\u043d\u043a\u0435\u043d\u0448\u0442\u0435\u0439\u043d\u0430&#187;<\/p>\n<ul>\n<li>\n<p>\u041f\u0438\u0448\u0435\u043c &#8212; \/start  \u0438 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0442\u0435\u0441\u0442 &#8230; \u042f \u0432 \u0448\u043e\u043a\u0435, \u043e\u043d\u043e \u0436\u0438\u0432\u043e\u0435 !<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/bb8\/b50\/294\/bb8b50294a0c8c63543ada506483a047.png\" alt=\"\u041e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441 \u0431\u043e\u0442\u043e\u043c \u0432 \u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c\" title=\"\u041e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441 \u0431\u043e\u0442\u043e\u043c \u0432 \u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c\" width=\"1074\" height=\"1024\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bb8\/b50\/294\/bb8b50294a0c8c63543ada506483a047.png\"\/><figcaption>\u041e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441 \u0431\u043e\u0442\u043e\u043c \u0432 \u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c<\/figcaption><\/figure>\n<p>&#8212; \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0442\u043e \u0436\u0435 \u043d\u0430\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u043b Spring \u0432 \u043b\u043e\u0433\u0430\u0445 \u0438 \u0437\u0430\u043f\u0438\u0441\u0430\u043b\u0438\u0441\u044c \u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 Kafka \u0438 \u0411\u0414 ?<\/p>\n<details class=\"spoiler\">\n<summary>\u041b\u043e\u0433\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0431\u043e\u0442\u0430, \u043e\u0448\u0438\u0431\u043e\u043a \u043d\u0435 \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0435\u0442\u0441\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\">  .   ____          _            __ _ _  \/\\\\ \/ ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\ ( ( )\\___ | '_ | '_| | '_ \\\/ _` | \\ \\ \\ \\  \\\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )   '  |____| .__|_| |_|_| |_\\__, | \/ \/ \/ \/  =========|_|==============|___\/=\/_\/_\/_\/  :: Spring Boot ::                (v2.4.2) 2022-01-15 16:46:19.248  INFO 412498 --- [           main] com.secretary.bot.WebHookApp             : The following profiles are active: bot 2022-01-15 16:46:19.291  WARN 412498 --- [kground-preinit] o.s.h.c.j.Jackson2ObjectMapperBuilder    : For Jackson Kotlin classes support please add \"com.fasterxml.jackson.module:jackson-module-kotlin\" to the classpath 2022-01-15 16:46:19.882  INFO 412498 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8081 (http) 2022-01-15 16:46:19.887  INFO 412498 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat] 2022-01-15 16:46:19.887  INFO 412498 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat\/9.0.41] 2022-01-15 16:46:19.956  INFO 412498 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[\/]       : Initializing Spring embedded WebApplicationContext 2022-01-15 16:46:19.957  INFO 412498 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 678 ms 2022-01-15 16:46:20.013  INFO 412498 --- [           main] c.secretary.bot.config.DefaultDbConfig   : [db] \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0411\u0414: [{\"url\":\"jdbc:postgresql:\/\/localhost:5432\/postgres\",\"driver\":\"org.postgresql.Driver\",\"user\":\"*****\",\"password\":\"*****\",\"poolSize\":\"10\",\"minPoolSize\":4,\"maxPoolSize\":10,\"idleTimeout\":0,\"maxLifetime\":0,\"bulkSize\":null,\"h2Database\":false}] 2022-01-15 16:46:20.565  INFO 412498 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor' 2022-01-15 16:46:20.574 DEBUG 412498 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 1 ResponseBodyAdvice 2022-01-15 16:46:20.598 DEBUG 412498 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : 3 mappings in 'requestMappingHandlerMapping' 2022-01-15 16:46:20.619 DEBUG 412498 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Patterns [\/webjars\/**, \/**] in 'resourceHandlerMapping' 2022-01-15 16:46:20.627 DEBUG 412498 --- [           main] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice 2022-01-15 16:46:20.702  INFO 412498 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8081 (http) with context path '' 2022-01-15 16:46:20.709  INFO 412498 --- [           main] com.secretary.bot.WebHookApp             : Started WebHookApp in 1.65 seconds (JVM running for 1.962) SSS2022-01-15 16:52:33.916  INFO 412498 --- [legram Executor] o.a.k.clients.producer.ProducerConfig    : ProducerConfig values:  acks = 1 batch.size = 16384 bootstrap.servers = [localhost:9092] buffer.memory = 33554432 client.dns.lookup = use_all_dns_ips client.id = producer-1 compression.type = none connections.max.idle.ms = 540000 delivery.timeout.ms = 120000 enable.idempotence = false interceptor.classes = [] internal.auto.downgrade.txn.commit = true key.serializer = class org.apache.kafka.common.serialization.StringSerializer linger.ms = 0 max.block.ms = 60000 max.in.flight.requests.per.connection = 5 max.request.size = 1048576 metadata.max.age.ms = 300000 metadata.max.idle.ms = 300000 metric.reporters = [] metrics.num.samples = 2 metrics.recording.level = INFO metrics.sample.window.ms = 30000 partitioner.class = class org.apache.kafka.clients.producer.internals.DefaultPartitioner receive.buffer.bytes = 32768 reconnect.backoff.max.ms = 1000 reconnect.backoff.ms = 50 request.timeout.ms = 30000 retries = 2147483647 retry.backoff.ms = 100 sasl.client.callback.handler.class = null sasl.jaas.config = null sasl.kerberos.kinit.cmd = \/usr\/bin\/kinit sasl.kerberos.min.time.before.relogin = 60000 sasl.kerberos.service.name = null sasl.kerberos.ticket.renew.jitter = 0.05 sasl.kerberos.ticket.renew.window.factor = 0.8 sasl.login.callback.handler.class = null sasl.login.class = null sasl.login.refresh.buffer.seconds = 300 sasl.login.refresh.min.period.seconds = 60 sasl.login.refresh.window.factor = 0.8 sasl.login.refresh.window.jitter = 0.05 sasl.mechanism = GSSAPI security.protocol = PLAINTEXT security.providers = null send.buffer.bytes = 131072 ssl.cipher.suites = null ssl.enabled.protocols = [TLSv1.2, TLSv1.3] ssl.endpoint.identification.algorithm = https ssl.engine.factory.class = null ssl.key.password = null ssl.keymanager.algorithm = SunX509 ssl.keystore.location = null ssl.keystore.password = null ssl.keystore.type = JKS ssl.protocol = TLSv1.3 ssl.provider = null ssl.secure.random.implementation = null ssl.trustmanager.algorithm = PKIX ssl.truststore.location = null ssl.truststore.password = null ssl.truststore.type = JKS transaction.timeout.ms = 60000 transactional.id = null value.serializer = class org.apache.kafka.common.serialization.StringSerializer  2022-01-15 16:52:33.947  INFO 412498 --- [legram Executor] o.a.kafka.common.utils.AppInfoParser     : Kafka version: 2.6.0 2022-01-15 16:52:33.948  INFO 412498 --- [legram Executor] o.a.kafka.common.utils.AppInfoParser     : Kafka commitId: 62abe01bee039651 2022-01-15 16:52:33.948  INFO 412498 --- [legram Executor] o.a.kafka.common.utils.AppInfoParser     : Kafka startTimeMs: 1642254753947 2022-01-15 16:52:34.056  INFO 412498 --- [ad | producer-1] org.apache.kafka.clients.Metadata        : [Producer clientId=producer-1] Cluster ID: faKjxP6CTvGFeeVKJw 2022-01-15 16:54:01.115  INFO 412498 --- [legram Executor] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting... 2022-01-15 16:54:01.188  INFO 412498 --- [legram Executor] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed. <\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u041a\u0430\u043a \u043c\u044b \u0432\u0438\u0434\u0438\u043c, \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0411\u043e\u0442\u0443 \u043f\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c \u0432 \u0411\u0414<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/dde\/38f\/1f7\/dde38f1f7a55af7ce3ac7bb782bac34e.png\" alt=\"\u0417\u0430\u043f\u0438\u0441\u0438 \u0432 \u0411\u0414\" title=\"\u0417\u0430\u043f\u0438\u0441\u0438 \u0432 \u0411\u0414\" width=\"1502\" height=\"310\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dde\/38f\/1f7\/dde38f1f7a55af7ce3ac7bb782bac34e.png\"\/><figcaption>\u0417\u0430\u043f\u0438\u0441\u0438 \u0432 \u0411\u0414<\/figcaption><\/figure>\n<p>&#8212; \u041e\u0442\u043a\u0440\u044b\u0432 \u043a\u043e\u043d\u0434\u0443\u043a\u0442\u043e\u0440, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0443 topics, \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 \u043d\u0430\u0448 \u0442\u043e\u043f\u0438\u043a users<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/719\/3df\/6b5\/7193df6b5aec4ed9c630730399277921.png\" alt=\"\u0412\u043a\u043b\u0430\u0434\u043a\u0430 topics\" title=\"\u0412\u043a\u043b\u0430\u0434\u043a\u0430 topics\" width=\"1377\" height=\"745\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/719\/3df\/6b5\/7193df6b5aec4ed9c630730399277921.png\"\/><figcaption>\u0412\u043a\u043b\u0430\u0434\u043a\u0430 topics<\/figcaption><\/figure>\n<p>&#8212; \u0414\u0430\u043b\u0435\u0435 \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0442\u043e\u043f\u0438\u043a\u0430 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 CONSUME DATA<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e0b\/3f9\/a92\/e0b3f9a926feee20e359140ebf7066b4.png\" alt=\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0442\u043e\u043f\u0438\u043a\u0435 users\" title=\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0442\u043e\u043f\u0438\u043a\u0435 users\" width=\"1386\" height=\"748\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e0b\/3f9\/a92\/e0b3f9a926feee20e359140ebf7066b4.png\"\/><figcaption>\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0442\u043e\u043f\u0438\u043a\u0435 users<\/figcaption><\/figure>\n<p>&#8212; \u0412 \u043e\u0442\u043a\u0440\u044b\u0432\u0448\u0435\u043c\u0441\u044f \u043e\u043a\u043d\u0435, \u0441\u0442\u0430\u0432\u0438\u043c \u0442\u0430\u043a\u0438\u0435 \u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 (\u0441\u0430\u043c\u0430\u044f \u0432\u0430\u0436\u043d\u0430\u044f \u0438\u0437 \u043d\u0438\u0445 \u044d\u0442\u043e Start From &#8212; \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u0441 \u043a\u0430\u043a\u043e\u0433\u043e \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432 Kafka, \u043d\u0430\u0448\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 &#8212; \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b\u0435 \u0440\u0430\u043d\u0435\u0435)<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5cb\/23f\/5ed\/5cb23f5ed3ec2686ce370f8d3a5481de.png\" alt=\"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439\" title=\"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439\" width=\"995\" height=\"681\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5cb\/23f\/5ed\/5cb23f5ed3ec2686ce370f8d3a5481de.png\"\/><figcaption>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439<\/figcaption><\/figure>\n<p>&#8212; \u0412\u043e\u0442 \u0438 \u0432\u0441\u0435, \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0443\u0431\u0435\u0434\u0438\u043b\u0438\u0441\u044c, \u0447\u0442\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0431\u043b\u0430\u0433\u043e\u043f\u043e\u043b\u0443\u0447\u043d\u043e \u043f\u0440\u0438\u043b\u0435\u0442\u0435\u043b\u0438 \u0432 Kafka, \u0437\u0430\u043f\u0438\u0441\u0430\u043b\u0438\u0441\u044c \u0432 \u0411\u0414 \u0438 \u043d\u0435 \u0432\u044b\u0437\u0432\u0430\u043b\u0438 \u043e\u0448\u0438\u0431\u043e\u043a \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d54\/a27\/edd\/d54a27eddab92677fdeedb5e9e316672.png\" alt=\"\u041f\u0440\u0438\u043b\u0435\u0442\u0435\u0432\u0448\u0438\u0435 \u0432 Kafka \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \" title=\"\u041f\u0440\u0438\u043b\u0435\u0442\u0435\u0432\u0448\u0438\u0435 \u0432 Kafka \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \" width=\"994\" height=\"682\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d54\/a27\/edd\/d54a27eddab92677fdeedb5e9e316672.png\"\/><figcaption>\u041f\u0440\u0438\u043b\u0435\u0442\u0435\u0432\u0448\u0438\u0435 \u0432 Kafka \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f <\/figcaption><\/figure>\n<blockquote>\n<p>\u041d\u0443 \u0447\u0442\u043e \u0436\u0435, \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0432\u0441\u0435\u043c \u0441\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u0440\u0435\u043c\u044f, \u043f\u043e\u0442\u0440\u0430\u0447\u0435\u043d\u043d\u043e\u0435 \u043d\u0430 \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438, \u0436\u0434\u0443 \u0432\u0430\u0441 \u0432\u043e <a href=\"https:\/\/habr.com\/ru\/sandbox\/165371\/\" rel=\"noopener noreferrer nofollow\"><strong>\u0432\u0442\u043e\u0440\u043e\u0439 \u0447\u0430\u0441\u0442\u0438<\/strong><\/a> \u044d\u0442\u043e\u0433\u043e \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430, \u0433\u0434\u0435 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Consumer Kafka, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u0435\u0442\u0430\u044e\u0449\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f.<\/p>\n<\/blockquote>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/655329\/\"> https:\/\/habr.com\/ru\/post\/655329\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<div class=\"persona\" persona=\"true\">\n<h5 class=\"persona__heading\" persona=\"true\">\u0418\u0432\u0430\u043d\u043e\u0432 \u041c\u0430\u043a\u0441\u0438\u043c<\/h5>\n<p persona=\"true\" class=\"persona__text\">\u041c\u043b\u0430\u0434\u0448\u0438\u0439 Java \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442<\/p>\n<\/div>\n<h3>\u0420\u0435\u0446\u0435\u043f\u0442 \u043f\u043e \u043f\u0440\u0438\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u0438\u044e \u0441\u0432\u043e\u0435\u0433\u043e \u00abTelegram-\u0424\u0440\u0430\u043d\u043a\u0435\u043d\u0448\u0442\u0435\u0439\u043d\u0430\u00bb<\/h3>\n<figure class=\"bordered full-width\"><figcaption>\u0414\u0430\u0436\u0435 \u0447\u0435\u043b\u043e\u0432\u0435\u043a \u0441\u0440\u0435\u0434\u043d\u0438\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0435\u0439, \u0443\u043f\u043e\u0440\u043d\u043e \u0437\u0430\u043d\u0438\u043c\u0430\u044f\u0441\u044c \u043e\u0434\u043d\u0438\u043c \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043e\u043c, \u043d\u0435\u043f\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0434\u043e\u0441\u0442\u0438\u0433\u043d\u0435\u0442 \u0432\u00a0\u043d\u0435\u043c \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0445 \u043f\u043e\u0437\u043d\u0430\u043d\u0438\u0439. &#8212; \u00ab\u0424\u0440\u0430\u043d\u043a\u0435\u043d\u0448\u0442\u0435\u0439\u043d\u00bb \u041c\u044d\u0440\u0438 \u0428\u0435\u043b\u043b\u0438<\/figcaption><\/figure>\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442, \u0434\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f, \u0441\u0432\u043e\u0435\u0433\u043e \u0440\u043e\u0434\u0430 \u043c\u043e\u0435\u0439 \u043f\u0435\u0440\u0432\u043e\u0439, \u043d\u043e \u0432\u0441\u0435 \u0436\u0435 \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u044e\u0441\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0430\u043c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0431\u043e\u0442\u0430, \u043f\u0440\u0438\u043a\u0440\u0443\u0442\u0438\u0432 \u043a \u043d\u0435\u043c\u0443 \u0432\u0441\u0435 \u043e\u0431\u0435\u0449\u0430\u043d\u043d\u044b\u0435 \u0432\u044b\u0448\u0435 \u0441\u0432\u0438\u0441\u0442\u0435\u043b\u043a\u0438-\u0442\u0430\u0440\u0430\u0445\u0442\u0435\u043b\u043a\u0438.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u0438 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 2 \u0447\u0430\u0441\u0442\u0438, \u043f\u0435\u0440\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c &#8212; \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0431\u043e\u0442\u0430 \u0441 \u043e\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u043b\u043e\u0433\u043e\u0432 (Kafka Producer) \u0438 \u0437\u0430\u043f\u0438\u0441\u044c\u044e \u0438\u0445 \u0432 \u0411\u0414, <a href=\"https:\/\/habr.com\/ru\/sandbox\/165371\/\" rel=\"noopener noreferrer nofollow\">\u0432\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c<\/a> &#8212; \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0432\u0441\u0435\u0445 \u043b\u043e\u0433\u043e\u0432 (Kafka Consumer).<\/p>\n<h2>\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b:<\/h2>\n<ol>\n<li>\n<p><a href=\"https:\/\/core.telegram.org\/bots#6-botfather\" rel=\"noopener noreferrer nofollow\">\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0431\u043e\u0442\u0430<\/a><\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Spring Boot \u043f\u0440\u043e\u0435\u043a\u0442, \u043f\u0440\u043e\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043e\u0440 \u0432 <a href=\"https:\/\/www.jetbrains.com\/ru-ru\/idea\/\" rel=\"noopener noreferrer nofollow\"><strong>IntelliJ IDEA<\/strong><\/a>, \u043b\u0438\u0431\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <a href=\"https:\/\/start.spring.io\/\" rel=\"noopener noreferrer nofollow\">Spring Initializr<\/a>. (\u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0441\u0431\u043e\u0440\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f <a href=\"https:\/\/gradle.org\/install\/\" rel=\"noopener noreferrer nofollow\">Gradle<\/a>)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/post\/496182\/\" rel=\"noopener noreferrer nofollow\">Kafka<\/a> (\u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0442\u043e\u043f\u0438\u043a\u043e\u0432 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <a href=\"https:\/\/www.conduktor.io\/download\" rel=\"noopener noreferrer nofollow\">Conductor<\/a>)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/www.postgresql.org\/download\/\" rel=\"noopener noreferrer nofollow\">PostgreSQL<\/a> (\u0434\u043b\u044f \u043a\u043e\u043c\u0444\u043e\u0440\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <a href=\"https:\/\/dbeaver.io\/download\/\" rel=\"noopener noreferrer nofollow\">DBeaver<\/a>)<\/p>\n<\/li>\n<\/ol>\n<details class=\"spoiler\">\n<summary>\u0415\u0441\u043b\u0438 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0443\u0442 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0441 \u0432\u043e\u0441\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041f\u0440\u043e\u0448\u0443 \u043f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043a\u043e\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u0432\u043e\u0437\u043d\u0438\u043a\u0448\u0438\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u043d\u0430 \u0432\u0441\u044f\u043a\u0438\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 &#8212; \u0432\u043e\u0442 \u043c\u043e\u0439 <a href=\"https:\/\/github.com\/RushianHaker\/secretary-bot\" rel=\"noopener noreferrer nofollow\">git<\/a><\/p>\n<\/div>\n<\/details>\n<h2>\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0441 \u043d\u0430\u0440\u0435\u0437\u043a\u0438:<\/h2>\n<p>\u041f\u0435\u0440\u0432\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c build.grable \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438<\/p>\n<details class=\"spoiler\">\n<summary>build.grable<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\">buildscript {     repositories {         mavenCentral()     } }  plugins {     id 'org.springframework.boot' version '2.4.2'     id 'io.spring.dependency-management' version '1.0.11.RELEASE'     id 'java' }  apply from: 'build-test.gradle'  group 'com.sercetary.bot' sourceCompatibility = '14'  configurations {     compileOnly {         extendsFrom annotationProcessor     } }  repositories {     mavenCentral() }  configurations.all {     exclude module: 'slf4j-log4j12' }  dependencies {     implementation 'org.springframework.boot:spring-boot-starter-web:2.5.6'     implementation 'org.springframework.boot:spring-boot-starter-jdbc:2.5.6'     implementation 'org.springframework.data:spring-data-commons:2.6.0'     implementation 'org.springframework.kafka:spring-kafka:2.7.6'     implementation 'org.postgresql:postgresql:42.3.1'     implementation 'com.h2database:h2:1.4.200'      implementation group: 'org.telegram', name: 'telegrambots-abilities', version: '5.3.0'     implementation group: 'org.telegram', name: 'telegrambots', version: '5.3.0'      compile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.7.29'     compileOnly 'org.projectlombok:lombok:1.18.22'     annotationProcessor 'org.projectlombok:lombok:1.18.22' }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u0440\u0430\u0437\u0443 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b Kafka \u043e\u043f\u0438\u0448\u0435\u043c application.yml, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430\u0448\u0435\u0433\u043e kafka producer<\/p>\n<details class=\"spoiler\">\n<summary>application.yml<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\">server:   port: 9000 spring:   kafka:     producer:       bootstrap-servers: localhost:9092       key-serializer: org.apache.kafka.common.serialization.StringSerializer       value-serializer: org.apache.kafka.common.serialization.StringSerializer<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 application.properties<\/p>\n<details class=\"spoiler\">\n<summary>application.properties<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># HTTP port for incoming requests server.port=8081  app.http.bot=change-me telegram-bot.name=change-me telegram-bot.token=change-me  # Bot db app.db.bot-db.url=jdbc:postgresql:\/\/localhost:5432\/change-me app.db.bot-db.driver=org.postgresql.Driver app.db.bot-db.user=change-me app.db.bot-db.password=change-me app.db.bot-db.pool-size=10  # logging logging.level.root=INFO logging.level.org.springframework.web=DEBUG logging.level.ru.centerinform.webhook=TRACE logging.file.name=change-me <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0425\u043e\u0440\u043e\u0448\u043e, \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u0435\u0433\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:<\/p>\n<figure class=\"\"><figcaption>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/figcaption><\/figure>\n<p>\u041f\u0430\u043a\u0435\u0442\u044b:<\/p>\n<ul>\n<li>\n<p>config &#8212; \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0431\u0438\u043d\u043e\u0432 \u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>controller &#8212; \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<\/li>\n<li>\n<p>dto &#8212; \u0445\u0440\u0430\u043d\u0438\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043c\u043e\u0434\u0435\u043b\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0411\u0414<\/p>\n<\/li>\n<li>\n<p>exceptions &#8212; \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u043f\u0430\u043a\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a<\/p>\n<\/li>\n<li>\n<p>repository &#8212; \u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0411\u0414<\/p>\n<\/li>\n<li>\n<p>service &#8212; \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0431\u0438\u0437\u043d\u0435\u0441 \u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<\/ul>\n<h2>\u0421\u0435\u0439\u0447\u0430\u0441 \u043c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0438\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b \u0438 \u043c\u0430\u0440\u0438\u043d\u0443\u0435\u043c:<\/h2>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0431\u0438\u043d\u043e\u0432:<\/h4>\n<p>&#8212; \u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f <a href=\"https:\/\/habr.com\/ru\/post\/334448\/\" rel=\"noopener noreferrer nofollow\">\u0431\u0438\u043d\u043e\u0432<\/a> \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 config, \u0442\u0443\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 TelegramBotsApi \u0438 ObjectMapper<\/p>\n<details class=\"spoiler\">\n<summary>AppConfig<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Configuration public class AppConfig {      @Bean     ObjectMapper customObjectMapper() {         return new ObjectMapper();     }      @Bean     TelegramBotsApi telegramBotsApi() throws TelegramApiException{         return new TelegramBotsApi(DefaultBotSession.class);     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u0412\u043d\u0443\u0442\u0440\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 DbConfig, \u0435\u0441\u0442\u044c \u043a\u043b\u0430\u0441\u0441 SpringDataJdbcProperties, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 SpringDataJdbc<\/p>\n<details class=\"spoiler\">\n<summary>DbConfig<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Configuration public class DbConfig extends DefaultDbConfig {      @Bean     @Qualifier(\"bot-db\")     @ConfigurationProperties(prefix = \"app.db.bot-db\")     SpringDataJdbcProperties gitlabJdbcProperties() {         return new SpringDataJdbcProperties();     }      @Bean     @Qualifier(\"bot-db\")     public DataSource gitlabDataSource(@Qualifier(\"bot-db\") SpringDataJdbcProperties properties) {         return hikariDataSource(\"db\", properties);     }      @Bean     @Qualifier(\"bot-db\")     JdbcTemplate gitlabJdbcTemplate(@Qualifier(\"bot-db\") DataSource dataSource) {         return new JdbcTemplate(dataSource);     }      @Data     @NoArgsConstructor     public static class SpringDataJdbcProperties {          \/\/ constants         private static final String H2_DATABASE_DRIVER = \"org.h2.Driver\";          \/**          * JDBC URL property          *\/         String url;         \/**          * JDBC driver class name property          *\/         String driver;         \/**          * JDBC username property          *\/         String user;         \/**          * JDBC password property          *\/         String password;         \/**          * Hikari \/ Vertica maxPoolSize property          *\/         String poolSize;         \/**          * Minimum pool size          *\/         int minPoolSize = 4;         \/**          * Maximum pool size          *\/         int maxPoolSize = 10;         \/**          * This property controls the maximum amount of time (in milliseconds) that a connection is allowed to          * sit idle in the pool. A value of 0 means that idle connections are never removed from the pool.          *\/         long idleTimeout;         \/**          * This property controls the maximum lifetime of a connection in the pool. When a connection          * reaches this timeout, even if recently used, it will be retired from the pool.          * An in-use connection will never be retired, only when it is idle will it be removed          *\/         long maxLifetime;         \/**          * Bulk insert size          *\/         Integer bulkSize;           \/**          * All-args constructor for {@link SpringDataJdbcProperties#toString()} (logging)          *          * @param url JDBC driver class name property          * @param driver JDBC driver class name property          * @param user JDBC username property          * @param password JDBC password property          * @param poolSize Hikari \/ Vertica maxPoolSize property          * @param bulkSize bulk insert size          *\/         public SpringDataJdbcProperties(                 String url, String driver, String user, String password, String poolSize, Integer bulkSize) {             this.url = url;             this.driver = driver;             this.user = user;             this.password = password;             this.poolSize = poolSize;             this.bulkSize = bulkSize;         }           \/**          * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0438\u0441\u0442\u0438\u043d\u0443, \u0435\u0441\u043b\u0438 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 in-memory H2 database          *          * @return \u0438\u0441\u0442\u0438\u043d\u0430, \u0435\u0441\u043b\u0438 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 in-memory H2 database          *\/         public boolean isH2Database() {             return driver.equals(H2_DATABASE_DRIVER);         }          \/**          * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON          *          * @return \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON          *\/         @Override         public String toString() {             var props = new SpringDataJdbcProperties(                     url, driver, user, ((password == null) || password.isEmpty()) ? \"\" : \"*****\", poolSize, bulkSize);             return Json.encode(props);         }      }  }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u0438\u043d\u043e\u0432<\/p>\n<details class=\"spoiler\">\n<summary>DefaultDbConfig<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Slf4j class DefaultDbConfig {      protected DataSource hikariDataSource(String tag, DbConfig.SpringDataJdbcProperties properties) {         log.info(\"[{}] \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0411\u0414: [{}]\", tag, properties.toString());          HikariDataSource ds = new HikariDataSource();         ds.setJdbcUrl(properties.getUrl());         ds.setDriverClassName(properties.getDriver());         ds.setUsername(properties.getUser());         ds.setPassword(properties.getPassword());         ds.setMaximumPoolSize(Integer.parseInt(properties.getPoolSize()));         return ds;     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>&#8212; \u041f\u043e\u0441\u043b\u0435 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0443\u0442\u0438\u043b\u0438\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/p>\n<details class=\"spoiler\">\n<summary>Json<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">public class Json {     static final ObjectMapper mapper = new ObjectMapper();      \/**      * Encode instance as JSON      *      * @param obj instance      * @return JSON      *\/     public static String encode(Object obj) {         try {             return mapper.writeValueAsString(obj);         } catch (JsonProcessingException e) {             return obj.toString();         }     }      public static &lt;T> T decode(String json, Class&lt;T> clazz) throws JsonProcessingException {         return mapper.readValue(json, clazz);     }<\/code><\/pre>\n<\/div>\n<\/details>\n<h4>\u0414\u0430\u043b\u0435\u0435 \u043c\u044b \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0438\u0437 \u0432\u043d\u0435<\/h4>\n<p> &#8212; \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0438\u0437 \u0411\u0414<\/p>\n<details class=\"spoiler\">\n<summary>UsersController<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Slf4j @RestController @RequestMapping(\"${app.http.bot\") @RequiredArgsConstructor @SuppressWarnings(\"unused\") public class UsersController {      private final UserService userService;      \/**      * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441 \u043d\u0438\u043c\u0438 \u043f\u043b\u0430\u043d\u0430\u043c\u0438      *\/     @RequestMapping(path = \"\/users_idea\", method = RequestMethod.GET)     public List&lt;User> getIdeaList() {         log.debug(\"Method - getIdeaList was called\");         return userService.getUserList();     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<h4>\u041f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043c\u043e\u0434\u0435\u043b\u0438<\/h4>\n<p>&#8212; \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f User, \u0430 \u0442\u0430\u043a \u0436\u0435 \u0435\u0433\u043e \u043c\u0430\u043f\u043f\u0435\u0440 UserMapper, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0411\u0414 \u0438 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043f\u043e\u043b\u0435\u0439 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435<\/p>\n<details class=\"spoiler\">\n<summary>User<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@Data @RequiredArgsConstructor public class <\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-330562","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/330562","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=330562"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/330562\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=330562"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=330562"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=330562"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}