在Heroku上使用Spring Boot + JPA + Flyway来创建LINE Bot
总结
使用Spring Boot、JPA、 Flyway和Heroku创建一个LINE Bot,从数据库中获取回复消息。
由于这只是一个简单的示例代码,所以请宽容地对待细节方面的问题。
此外,关于以下内容,我不会进行解释,请参考链接。
LINE Channelの作成
https://developers.line.biz/ja/docs/messaging-api/getting-started/
herokuでアプリを作成
https://signup.heroku.com/
JPA (Java Persistence API) => Java標準のORM
https://www.vogella.com/tutorials/JavaPersistenceAPI/article.html
Flyway => Migrationツール
相关
-
- line-bot-sdk-javaを使ってSpring bootでLINE Botを作る
- LINE BOT APIのreplyでたまにInvalid reply tokenが発生する
前提 – Qian ti
使用Line提供的样例代码。
https://github.com/line/line-bot-sdk-java.git
环境
-
- Java 11.02
- Spring Boot 2.4.9 (line-bot-sdk-javaで使われているversion)
重要的文件
当您在LINE上向机器人发送消息时,系统会根据您发送的文字内容从数据库表中提取相应的回复消息并作出回应。回复条件如下:
建筑设计
-
- heroku
- PostgreSQL 13.3 (on heroku)
目录结构
在示例代码中,仅列出了本次要编辑和新增的文件。
.
├ ─ ─ Procfile
└ ─ ─ sample-spring-boot-kitchensink
├ ─ ─ build.gradle
└ ─ ─ src
└ ─ ─ main
├ ─ ─ java
│ └ ─ ─ com
│ └ ─ ─ example
│ └ ─ ─ bot
│ └ ─ ─ spring
│ ├ ─ ─ KitchenSinkController.java
│ ├ ─ ─ models
│ │ └ ─ ─ TextMessageModel.java
│ ├ ─ ─ repositories
│ │ └ ─ ─ TextMessageRepository.java
│ └ ─ ─ services
│ └ ─ ─ TextMessageService.java
└ ─ ─ resources
├ ─ ─ application.yml
└ ─ ─ db
└ ─ ─ migration
└ ─ ─ V1_0__create_table_text_messages.sql
可以删除以下三个目录,因为我们将使用样本代码中的sample-spring-boot-kitchensink。
-
- sample-manage-audience
-
- sample-spring-boot-echo
- sample-spring-boot-echo-kotlin
数据库表格
创建一个名为text_messages的表用于存放回复消息。由于会使用Flyway进行迁移,因此不需要手动创建。
line-bot-sample::DATABASE=> \d text_messages;
Table "public.text_messages"
Column | Type | Collation | Nullable | Default
---------+------------------------+-----------+----------+-------------------------------------------
id | integer | | not null | nextval('text_messages_id_seq'::regclass)
message | character varying(255) | | not null |
Indexes:
"text_messages_pkey" PRIMARY KEY, btree (id)
GitHub
完成品在这里
https://github.com/Esfahan/line-bot-java-sample
制作步骤
获取示例代码
使用Line提供的示例代码。
$ git clone https://github.com/line/line-bot-sdk-java.git
使JPA和flyway可用。
将以下内容添加到 sample-spring-boot-kitchensink/build.gradle 文件中。
dependencies {
implementation project(':line-bot-spring-boot')
implementation "org.springframework.boot:spring-boot-starter-web"
implementation 'com.google.guava:guava'
+ implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
+ implementation 'org.flywaydb:flyway-core'
+ runtimeOnly 'org.postgresql:postgresql'
}
创建 application.yml 文件
创建一个新的sample-spring-boot-kitchensink/src/main/resources/application.yml文件。
有关环境变量值的说明将在后面提到。
line.bot:
channel-token: ${CHANNEL_TOKEN}
channel-secret: ${CHANNEL_SECRET}
handler.path: /callback
spring:
jpa:
database: POSTGRESQL
open-in-view: True
datasource:
url: ${DATABASE_JDBC_URL}
username: ${DATABASE_USER}
password: ${DATABASE_PASSWORD}
driver-class-name: org.postgresql.Driver
connectionProperties: useUnicode=true;characterEncoding=utf-8;
flyway:
locations: classpath:/db/migration
baseline-on-migrate: true
其他属性请参考以下链接:
https://spring.pleiades.io/spring-boot/docs/current/reference/html/application-properties.html
创建Migration文件 Migration
以下是关于迁移文件命名规则的参考链接:https://garafu.blogspot.com/2020/06/how-to-use-flyway.html。
这次将以以下名称创建:
V1_0__create_table_text_messages.sql
当按照 application.yml 中 flyway.locations 的定义,在 resources/db/migration/ 目录下放置 sql 文件,就会执行迁移操作。因此,需要创建一个目录并创建 sql 文件。
$ mkdir -p sample-spring-boot-kitchensink/src/main/resources/db/migration/
$ vi sample-spring-boot-kitchensink/src/main/resources/db/migration/V1_0__create_table_text_messages.sql
CREATE TABLE text_messages(
id serial PRIMARY KEY,
message varchar(255) NOT NULL
);
创建目录
创建models/,repositories/,services/目录以放置各个源代码。
$ mkdir sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/{models,repositories,services}
创建Model
在 `sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/models/TextMessageModel.java` 创建一个新文件。
package com.example.bot.spring.models;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="text_messages")
public class TextMessageModel {
@Id
private Integer id;
public String message;
}
创建存储库
创建以下文件:sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/repositories/TextMessageRepository.java。
package com.example.bot.spring.repositories;
import com.example.bot.spring.models.TextMessageModel;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TextMessageRepository extends JpaRepository<TextMessageModel, Integer> {
}
创建服务
在中国,本土化的内容重新表达如下:
新建以下文件:
sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/services/TextMessageService.java
package com.example.bot.spring.services;
import com.example.bot.spring.repositories.TextMessageRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class TextMessageService {
private String replyTextMessage;
private boolean replyFlg;
@Autowired
TextMessageRepository textMessageRepository;
public void fetchReplyTextMessage (String message) {
log.info("fetchReplyTextMessage is called: {}", message);
Integer id = replyId(message);
if (id > 0) {
this.replyTextMessage = textMessageRepository.findById(id).get().message;
this.replyFlg = true;
} else {
this.replyFlg = false;
}
}
public String getReplyMessage() {
return this.replyTextMessage;
}
public boolean getReplyFlg() {
return this.replyFlg;
}
private Integer replyId(String message) {
switch (message) {
case "ハロー": {
return 1;
}
case "hello": {
return 2;
}
default: {
return 0;
}
}
}
}
控制器
编辑下面的文件:
sample-spring-boot-kitchensink/src/main/java/com/example/bot/spring/KitchenSinkController.java
为了简化理解处理内容,我们将handleTextMessageEvent方法重写为使用一个方法来处理全部内容。
+import com.example.bot.spring.services.TextMessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
public class KitchenSinkController {
@Autowired
private LineBlobClient lineBlobClient;
+ @Autowired
+ private TextMessageService textMessageService;
+
@EventMapping
public void handleTextMessageEvent(MessageEvent<TextMessageContent> event) throws Exception {
TextMessageContent message = event.getMessage();
- handleTextContent(event.getReplyToken(), event, message);
+
+ log.info("TextMessageContent is called");
+
+ textMessageService.fetchReplyTextMessage(message.getText());
+
+ if (textMessageService.getReplyFlg()) {
+ log.info("replyFlg is true");
+
+ String replyText = textMessageService.getReplyMessage();
+ List<Message> replyMessage = singletonList(new TextMessage(replyText));
+ String replyToken = event.getReplyToken();
+
+ log.info("Returns echo message {}: {}", replyToken, replyText);
+
+ boolean notificationDisabled = false;
+ try {
+ BotApiResponse apiResponse = lineMessagingClient
+ .replyMessage(new ReplyMessage(replyToken, replyMessage, notificationDisabled))
+ .get();
+ log.info("Sent messages: {}", apiResponse);
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
修改Procfile。
为了使用sample-spring-boot-kitchensink,需修改Procfile如下所示。
-web: java $JAVA_OPTS -jar sample-spring-boot-echo/build/libs/sample-spring-boot-echo-*.jar --server.port=$PORT
+web: java $JAVA_OPTS -jar sample-spring-boot-kitchensink/build/libs/sample-spring-boot-kitchensink-*.jar --server.port=$PORT
.gitignore 文件
如果忽略了.jar文件,就无法部署./gradle/wrapper/gradle-wrapper.jar,并且会出现以下错误,因此需要进行编辑。
错误:无法找到或加载主类org.gradle.wrapper.GradleWrapperMain。
# Package Files #
-*.jar
+#*.jar
*.war
*.ear
在Heroku上设置环境变量。
在Heroku上设置以下环境变量。
部署
将应用程序部署到Heroku。
$ git remote add heroku https://git.heroku.com/${HEROKU_APP_NAME}.git
$ git add --all
$ git commit -m 'Added a sample codeset for Qiita'
$ git push heroku master
确认移民的结果
当将程序部署到Heroku时,将会在PostgreSQL中创建以下表。
对于在本地使用Intellij等开发工具的情况,只需要在IDE中运行程序,就会在连接的数据库中创建表。
$ heroku pg:psql postgresql-abcdefg-12345 --app line-bot-sample
line-bot-sample::DATABASE=> \d
List of relations
Schema | Name | Type | Owner
--------+-----------------------+----------+----------------
public | flyway_schema_history | table | sample-user
public | text_messages | table | sample-user
public | text_messages_id_seq | sequence | sample-user
(3 rows)
插入
将以下内容插入到数据库中。这将作为回复消息。
line-bot-sample::DATABASE=> insert into text_messages (message) values ('ハローワールド');
INSERT 0 1
line-bot-sample::DATABASE=> insert into text_messages (message) values ('Hello world!!');
INSERT 0 1
line-bot-sample::DATABASE=> select * from text_messages;
id | message
----+----------------
1 | ハローワールド
2 | Hello world!!
(2 rows)
在LINE上設置Webhook URL。
在LINE开发者的Message API的Webhook设置中,注册heroku的URL。
Webhook URL: https://{HEROKU_APP_NAME}.herokuapp.com/callback
Webhook 使用: 设为启用
如果要编写测试代码
在下面创建一个名为test的目录,并在该目录下创建测试代码。
$ mkdir -p sample-spring-boot-kitchensink/src/test/java/com/example/bot/spring/
请在汉语中提供以下内容的同义表述:
借鉴
以下是中文的版本:
https://tosi-tech.net/2019/04/flyway-with-spring-boot/ 可以参考此链接了解如何在Spring Boot中使用Flyway进行数据库迁移。
https://tomokazu-kozuma.com/how-to-migrate-mysql-using-flyway/ 你可以参考此链接了解如何使用Flyway进行MySQL数据库迁移的方法。
相关
- LINE BOT APIのreplyでたまにInvalid reply tokenが発生する