{"id":39794,"date":"2023-03-30T19:39:46","date_gmt":"2023-05-22T19:10:36","guid":{"rendered":"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/"},"modified":"2024-04-29T21:11:45","modified_gmt":"2024-04-29T13:11:45","slug":"%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot","status":"publish","type":"post","link":"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/","title":{"rendered":"\u5728Heroku\u4e0a\u4f7f\u7528Spring Boot + JPA + Flyway\u6765\u521b\u5efaLINE Bot"},"content":{"rendered":"<h2>\u603b\u7ed3<\/h2>\n<p>\u4f7f\u7528Spring Boot\u3001JPA\u3001 Flyway\u548cHeroku\u521b\u5efa\u4e00\u4e2aLINE Bot\uff0c\u4ece\u6570\u636e\u5e93\u4e2d\u83b7\u53d6\u56de\u590d\u6d88\u606f\u3002<br \/>\n\u7531\u4e8e\u8fd9\u53ea\u662f\u4e00\u4e2a\u7b80\u5355\u7684\u793a\u4f8b\u4ee3\u7801\uff0c\u6240\u4ee5\u8bf7\u5bbd\u5bb9\u5730\u5bf9\u5f85\u7ec6\u8282\u65b9\u9762\u7684\u95ee\u9898\u3002<br \/>\n\u6b64\u5916\uff0c\u5173\u4e8e\u4ee5\u4e0b\u5185\u5bb9\uff0c\u6211\u4e0d\u4f1a\u8fdb\u884c\u89e3\u91ca\uff0c\u8bf7\u53c2\u8003\u94fe\u63a5\u3002<\/p>\n<p>LINE Channel\u306e\u4f5c\u6210<\/p>\n<p>https:\/\/developers.line.biz\/ja\/docs\/messaging-api\/getting-started\/<\/p>\n<p>heroku\u3067\u30a2\u30d7\u30ea\u3092\u4f5c\u6210<\/p>\n<p>https:\/\/signup.heroku.com\/<\/p>\n<p>JPA (Java Persistence API) =&gt; Java\u6a19\u6e96\u306eORM<\/p>\n<p>https:\/\/www.vogella.com\/tutorials\/JavaPersistenceAPI\/article.html<\/p>\n<p>Flyway =&gt; Migration\u30c4\u30fc\u30eb<\/p>\n<blockquote class=\"wp-embedded-content\" data-secret=\"1O287F4CsQ\"><p><a href=\"https:\/\/flywaydb.org\/\">Homepage<\/a><\/p><\/blockquote>\n<p><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" title=\"&#8220;Homepage&#8221; &#8212; Flyway\" src=\"https:\/\/flywaydb.org\/embed#?secret=G5P156pwQy#?secret=1O287F4CsQ\" data-secret=\"1O287F4CsQ\" width=\"500\" height=\"282\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe><\/p>\n<h2>\u76f8\u5173<\/h2>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">line-bot-sdk-java\u3092\u4f7f\u3063\u3066Spring boot\u3067LINE Bot\u3092\u4f5c\u308b<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul class=\"post-ul\">LINE BOT API\u306ereply\u3067\u305f\u307e\u306bInvalid reply token\u304c\u767a\u751f\u3059\u308b<\/ul>\n<h2>\u524d\u63d0 &#8211; Qian ti<\/h2>\n<p>\u4f7f\u7528Line\u63d0\u4f9b\u7684\u6837\u4f8b\u4ee3\u7801\u3002<br \/>\nhttps:\/\/github.com\/line\/line-bot-sdk-java.git<\/p>\n<h2>\u73af\u5883<\/h2>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">Java 11.02<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul class=\"post-ul\">Spring Boot 2.4.9 (line-bot-sdk-java\u3067\u4f7f\u308f\u308c\u3066\u3044\u308bversion)<\/ul>\n<h2>\u91cd\u8981\u7684\u6587\u4ef6<\/h2>\n<p>\u5f53\u60a8\u5728LINE\u4e0a\u5411\u673a\u5668\u4eba\u53d1\u9001\u6d88\u606f\u65f6\uff0c\u7cfb\u7edf\u4f1a\u6839\u636e\u60a8\u53d1\u9001\u7684\u6587\u5b57\u5185\u5bb9\u4ece\u6570\u636e\u5e93\u8868\u4e2d\u63d0\u53d6\u76f8\u5e94\u7684\u56de\u590d\u6d88\u606f\u5e76\u4f5c\u51fa\u56de\u5e94\u3002\u56de\u590d\u6761\u4ef6\u5982\u4e0b\uff1a<\/p>\n<div>\n<div class=\"post-table\">\u9001\u4fe1Message\u8fd4\u4fe1Message\u30cf\u30ed\u30fc\u30cf\u30ed\u30fc\u30ef\u30fc\u30eb\u30c9helloHello world!!\u305d\u308c\u4ee5\u5916\u8fd4\u4fe1\u3057\u306a\u3044<\/div>\n<\/div>\n<h3>\u5efa\u7b51\u8bbe\u8ba1<\/h3>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">heroku<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul class=\"post-ul\">PostgreSQL 13.3 (on heroku)<\/ul>\n<h3>\u76ee\u5f55\u7ed3\u6784<\/h3>\n<p>\u5728\u793a\u4f8b\u4ee3\u7801\u4e2d\uff0c\u4ec5\u5217\u51fa\u4e86\u672c\u6b21\u8981\u7f16\u8f91\u548c\u65b0\u589e\u7684\u6587\u4ef6\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"c\">.\r\n<\/span><span class=\"go\">\u251c \u2500 \u2500  Procfile\r\n\u2514 \u2500 \u2500  sample-spring-boot-kitchensink\r\n  \u00a0\u00a0 \u251c \u2500 \u2500  build.gradle\r\n  \u00a0\u00a0 \u2514 \u2500 \u2500  src\r\n  \u00a0\u00a0     \u2514 \u2500 \u2500  main\r\n  \u00a0\u00a0         \u251c \u2500 \u2500  java\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0 \u2514 \u2500 \u2500  com\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0     \u2514 \u2500 \u2500  example\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0         \u2514 \u2500 \u2500  bot\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0             \u2514 \u2500 \u2500  spring\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0                 \u251c \u2500 \u2500  KitchenSinkController.java\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0                 \u251c \u2500 \u2500  models\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0                 \u2502 \u00a0\u00a0 \u2514 \u2500 \u2500  TextMessageModel.java\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0                 \u251c \u2500 \u2500  repositories\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0                 \u2502 \u00a0\u00a0 \u2514 \u2500 \u2500  TextMessageRepository.java\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0                 \u2514 \u2500 \u2500  services\r\n  \u00a0\u00a0         \u2502 \u00a0\u00a0                      \u2514 \u2500 \u2500  TextMessageService.java\r\n  \u00a0\u00a0         \u2514 \u2500 \u2500  resources\r\n  \u00a0\u00a0             \u251c \u2500 \u2500  application.yml\r\n  \u00a0\u00a0             \u2514 \u2500 \u2500  db\r\n  \u00a0\u00a0              \u00a0\u00a0 \u2514 \u2500 \u2500  migration\r\n  \u00a0\u00a0              \u00a0\u00a0     \u2514 \u2500 \u2500  V1_0__create_table_text_messages.sql\r\n<\/span><\/code><\/pre>\n<p>\u53ef\u4ee5\u5220\u9664\u4ee5\u4e0b\u4e09\u4e2a\u76ee\u5f55\uff0c\u56e0\u4e3a\u6211\u4eec\u5c06\u4f7f\u7528\u6837\u672c\u4ee3\u7801\u4e2d\u7684sample-spring-boot-kitchensink\u3002<\/p>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">sample-manage-audience<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">sample-spring-boot-echo<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul class=\"post-ul\">sample-spring-boot-echo-kotlin<\/ul>\n<h3>\u6570\u636e\u5e93\u8868\u683c<\/h3>\n<p>\u521b\u5efa\u4e00\u4e2a\u540d\u4e3atext_messages\u7684\u8868\u7528\u4e8e\u5b58\u653e\u56de\u590d\u6d88\u606f\u3002\u7531\u4e8e\u4f1a\u4f7f\u7528Flyway\u8fdb\u884c\u8fc1\u79fb\uff0c\u56e0\u6b64\u4e0d\u9700\u8981\u624b\u52a8\u521b\u5efa\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"n\">line<\/span><span class=\"o\">-<\/span><span class=\"n\">bot<\/span><span class=\"o\">-<\/span><span class=\"n\">sample<\/span><span class=\"p\">::<\/span><span class=\"k\">DATABASE<\/span><span class=\"o\">=&gt;<\/span> <span class=\"err\">\\<\/span><span class=\"n\">d<\/span> <span class=\"n\">text_messages<\/span><span class=\"p\">;<\/span>\r\n                                    <span class=\"k\">Table<\/span> <span class=\"nv\">\"public.text_messages\"<\/span>\r\n <span class=\"k\">Column<\/span>  <span class=\"o\">|<\/span>          <span class=\"k\">Type<\/span>          <span class=\"o\">|<\/span> <span class=\"k\">Collation<\/span> <span class=\"o\">|<\/span> <span class=\"k\">Nullable<\/span> <span class=\"o\">|<\/span>                  <span class=\"k\">Default<\/span>\r\n<span class=\"c1\">---------+------------------------+-----------+----------+-------------------------------------------<\/span>\r\n <span class=\"n\">id<\/span>      <span class=\"o\">|<\/span> <span class=\"nb\">integer<\/span>                <span class=\"o\">|<\/span>           <span class=\"o\">|<\/span> <span class=\"k\">not<\/span> <span class=\"k\">null<\/span> <span class=\"o\">|<\/span> <span class=\"n\">nextval<\/span><span class=\"p\">(<\/span><span class=\"s1\">'text_messages_id_seq'<\/span><span class=\"p\">::<\/span><span class=\"n\">regclass<\/span><span class=\"p\">)<\/span>\r\n <span class=\"n\">message<\/span> <span class=\"o\">|<\/span> <span class=\"nb\">character<\/span> <span class=\"nb\">varying<\/span><span class=\"p\">(<\/span><span class=\"mi\">255<\/span><span class=\"p\">)<\/span> <span class=\"o\">|<\/span>           <span class=\"o\">|<\/span> <span class=\"k\">not<\/span> <span class=\"k\">null<\/span> <span class=\"o\">|<\/span>\r\n<span class=\"n\">Indexes<\/span><span class=\"p\">:<\/span>\r\n    <span class=\"nv\">\"text_messages_pkey\"<\/span> <span class=\"k\">PRIMARY<\/span> <span class=\"k\">KEY<\/span><span class=\"p\">,<\/span> <span class=\"n\">btree<\/span> <span class=\"p\">(<\/span><span class=\"n\">id<\/span><span class=\"p\">)<\/span>\r\n<\/code><\/pre>\n<h3>GitHub<\/h3>\n<p>\u5b8c\u6210\u54c1\u5728\u8fd9\u91cc<br \/>\nhttps:\/\/github.com\/Esfahan\/line-bot-java-sample<\/p>\n<h2>\u5236\u4f5c\u6b65\u9aa4<\/h2>\n<h3>\u83b7\u53d6\u793a\u4f8b\u4ee3\u7801<\/h3>\n<p>\u4f7f\u7528Line\u63d0\u4f9b\u7684\u793a\u4f8b\u4ee3\u7801\u3002<\/p>\n<pre class=\"post-pre\"><code>$ git clone https:\/\/github.com\/line\/line-bot-sdk-java.git\r\n<\/code><\/pre>\n<h3>\u4f7fJPA\u548cflyway\u53ef\u7528\u3002<\/h3>\n<p>\u5c06\u4ee5\u4e0b\u5185\u5bb9\u6dfb\u52a0\u5230 sample-spring-boot-kitchensink\/build.gradle \u6587\u4ef6\u4e2d\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"p\">dependencies {\r\n<\/span>     implementation project(':line-bot-spring-boot')\r\n     implementation \"org.springframework.boot:spring-boot-starter-web\"\r\n     implementation 'com.google.guava:guava'\r\n<span class=\"gi\">+    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'\r\n+    implementation 'org.flywaydb:flyway-core'\r\n+    runtimeOnly 'org.postgresql:postgresql'\r\n<\/span> }\r\n<\/code><\/pre>\n<h3>\u521b\u5efa application.yml \u6587\u4ef6<\/h3>\n<p>\u521b\u5efa\u4e00\u4e2a\u65b0\u7684sample-spring-boot-kitchensink\/src\/main\/resources\/application.yml\u6587\u4ef6\u3002<br \/>\n\u6709\u5173\u73af\u5883\u53d8\u91cf\u503c\u7684\u8bf4\u660e\u5c06\u5728\u540e\u9762\u63d0\u5230\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"s\">line.bot<\/span><span class=\"pi\">:<\/span>\r\n  <span class=\"na\">channel-token<\/span><span class=\"pi\">:<\/span> <span class=\"s\">${CHANNEL_TOKEN}<\/span>\r\n  <span class=\"na\">channel-secret<\/span><span class=\"pi\">:<\/span> <span class=\"s\">${CHANNEL_SECRET}<\/span>\r\n  <span class=\"s\">handler.path<\/span><span class=\"pi\">:<\/span> <span class=\"s\">\/callback<\/span>\r\n\r\n<span class=\"na\">spring<\/span><span class=\"pi\">:<\/span>\r\n  <span class=\"na\">jpa<\/span><span class=\"pi\">:<\/span>\r\n    <span class=\"na\">database<\/span><span class=\"pi\">:<\/span> <span class=\"s\">POSTGRESQL<\/span>\r\n    <span class=\"na\">open-in-view<\/span><span class=\"pi\">:<\/span> <span class=\"s\">True<\/span>\r\n  <span class=\"na\">datasource<\/span><span class=\"pi\">:<\/span>\r\n    <span class=\"na\">url<\/span><span class=\"pi\">:<\/span> <span class=\"s\">${DATABASE_JDBC_URL}<\/span>\r\n    <span class=\"na\">username<\/span><span class=\"pi\">:<\/span> <span class=\"s\">${DATABASE_USER}<\/span>\r\n    <span class=\"na\">password<\/span><span class=\"pi\">:<\/span> <span class=\"s\">${DATABASE_PASSWORD}<\/span>\r\n    <span class=\"na\">driver-class-name<\/span><span class=\"pi\">:<\/span> <span class=\"s\">org.postgresql.Driver<\/span>\r\n    <span class=\"na\">connectionProperties<\/span><span class=\"pi\">:<\/span> <span class=\"s\">useUnicode=true;characterEncoding=utf-8;<\/span>\r\n  <span class=\"na\">flyway<\/span><span class=\"pi\">:<\/span>\r\n    <span class=\"na\">locations<\/span><span class=\"pi\">:<\/span> <span class=\"s\">classpath:\/db\/migration<\/span>\r\n    <span class=\"na\">baseline-on-migrate<\/span><span class=\"pi\">:<\/span> <span class=\"no\">true<\/span>\r\n<\/code><\/pre>\n<p>\u5176\u4ed6\u5c5e\u6027\u8bf7\u53c2\u8003\u4ee5\u4e0b\u94fe\u63a5\uff1a<br \/>\nhttps:\/\/spring.pleiades.io\/spring-boot\/docs\/current\/reference\/html\/application-properties.html<\/p>\n<h3>\u521b\u5efaMigration\u6587\u4ef6 Migration<\/h3>\n<p>\u4ee5\u4e0b\u662f\u5173\u4e8e\u8fc1\u79fb\u6587\u4ef6\u547d\u540d\u89c4\u5219\u7684\u53c2\u8003\u94fe\u63a5\uff1ahttps:\/\/garafu.blogspot.com\/2020\/06\/how-to-use-flyway.html\u3002<\/p>\n<p>\u8fd9\u6b21\u5c06\u4ee5\u4ee5\u4e0b\u540d\u79f0\u521b\u5efa\uff1a<br \/>\nV1_0__create_table_text_messages.sql<\/p>\n<p>\u5f53\u6309\u7167 application.yml \u4e2d flyway.locations \u7684\u5b9a\u4e49\uff0c\u5728 resources\/db\/migration\/ \u76ee\u5f55\u4e0b\u653e\u7f6e sql \u6587\u4ef6\uff0c\u5c31\u4f1a\u6267\u884c\u8fc1\u79fb\u64cd\u4f5c\u3002\u56e0\u6b64\uff0c\u9700\u8981\u521b\u5efa\u4e00\u4e2a\u76ee\u5f55\u5e76\u521b\u5efa sql \u6587\u4ef6\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"gp\">$<\/span> <span class=\"nb\">mkdir<\/span> <span class=\"nt\">-p<\/span> sample-spring-boot-kitchensink\/src\/main\/resources\/db\/migration\/\r\n<span class=\"gp\">$<\/span> vi sample-spring-boot-kitchensink\/src\/main\/resources\/db\/migration\/V1_0__create_table_text_messages.sql\r\n<\/code><\/pre>\n<pre class=\"post-pre\"><code><span class=\"k\">CREATE<\/span> <span class=\"k\">TABLE<\/span> <span class=\"n\">text_messages<\/span><span class=\"p\">(<\/span>\r\n  <span class=\"n\">id<\/span> <span class=\"nb\">serial<\/span> <span class=\"k\">PRIMARY<\/span> <span class=\"k\">KEY<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"n\">message<\/span> <span class=\"nb\">varchar<\/span><span class=\"p\">(<\/span><span class=\"mi\">255<\/span><span class=\"p\">)<\/span> <span class=\"k\">NOT<\/span> <span class=\"k\">NULL<\/span>\r\n<span class=\"p\">);<\/span>\r\n<\/code><\/pre>\n<h3>\u521b\u5efa\u76ee\u5f55<\/h3>\n<p>\u521b\u5efamodels\/\uff0crepositories\/\uff0cservices\/\u76ee\u5f55\u4ee5\u653e\u7f6e\u5404\u4e2a\u6e90\u4ee3\u7801\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"gp\">$<\/span> <span class=\"nb\">mkdir <\/span>sample-spring-boot-kitchensink\/src\/main\/java\/com\/example\/bot\/spring\/<span class=\"o\">{<\/span>models,repositories,services<span class=\"o\">}<\/span>\r\n<\/code><\/pre>\n<h3>\u521b\u5efaModel<\/h3>\n<p>\u5728 `sample-spring-boot-kitchensink\/src\/main\/java\/com\/example\/bot\/spring\/models\/TextMessageModel.java` \u521b\u5efa\u4e00\u4e2a\u65b0\u6587\u4ef6\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"kn\">package<\/span> <span class=\"nn\">com.example.bot.spring.models<\/span><span class=\"o\">;<\/span>\r\n\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">javax.persistence.Entity<\/span><span class=\"o\">;<\/span>\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">javax.persistence.Id<\/span><span class=\"o\">;<\/span>\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">javax.persistence.Table<\/span><span class=\"o\">;<\/span>\r\n\r\n<span class=\"nd\">@Entity<\/span>\r\n<span class=\"nd\">@Table<\/span><span class=\"o\">(<\/span><span class=\"n\">name<\/span><span class=\"o\">=<\/span><span class=\"s\">\"text_messages\"<\/span><span class=\"o\">)<\/span>\r\n<span class=\"kd\">public<\/span> <span class=\"kd\">class<\/span> <span class=\"nc\">TextMessageModel<\/span> <span class=\"o\">{<\/span>\r\n    <span class=\"nd\">@Id<\/span>\r\n    <span class=\"kd\">private<\/span> <span class=\"nc\">Integer<\/span> <span class=\"n\">id<\/span><span class=\"o\">;<\/span>\r\n\r\n    <span class=\"kd\">public<\/span> <span class=\"nc\">String<\/span> <span class=\"n\">message<\/span><span class=\"o\">;<\/span>\r\n<span class=\"o\">}<\/span>\r\n<\/code><\/pre>\n<h3>\u521b\u5efa\u5b58\u50a8\u5e93<\/h3>\n<p>\u521b\u5efa\u4ee5\u4e0b\u6587\u4ef6\uff1asample-spring-boot-kitchensink\/src\/main\/java\/com\/example\/bot\/spring\/repositories\/TextMessageRepository.java\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"kn\">package<\/span> <span class=\"nn\">com.example.bot.spring.repositories<\/span><span class=\"o\">;<\/span>\r\n\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">com.example.bot.spring.models.TextMessageModel<\/span><span class=\"o\">;<\/span>\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">org.springframework.data.jpa.repository.JpaRepository<\/span><span class=\"o\">;<\/span>\r\n\r\n<span class=\"kd\">public<\/span> <span class=\"kd\">interface<\/span> <span class=\"nc\">TextMessageRepository<\/span> <span class=\"kd\">extends<\/span> <span class=\"nc\">JpaRepository<\/span><span class=\"o\">&lt;<\/span><span class=\"nc\">TextMessageModel<\/span><span class=\"o\">,<\/span> <span class=\"nc\">Integer<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">{<\/span>\r\n<span class=\"o\">}<\/span>\r\n<\/code><\/pre>\n<h3>\u521b\u5efa\u670d\u52a1<\/h3>\n<p>\u5728\u4e2d\u56fd\uff0c\u672c\u571f\u5316\u7684\u5185\u5bb9\u91cd\u65b0\u8868\u8fbe\u5982\u4e0b\uff1a<br \/>\n\u65b0\u5efa\u4ee5\u4e0b\u6587\u4ef6\uff1a<br \/>\nsample-spring-boot-kitchensink\/src\/main\/java\/com\/example\/bot\/spring\/services\/TextMessageService.java<\/p>\n<pre class=\"post-pre\"><code><span class=\"kn\">package<\/span> <span class=\"nn\">com.example.bot.spring.services<\/span><span class=\"o\">;<\/span>\r\n\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">com.example.bot.spring.repositories.TextMessageRepository<\/span><span class=\"o\">;<\/span>\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">lombok.extern.slf4j.Slf4j<\/span><span class=\"o\">;<\/span>\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">org.springframework.beans.factory.annotation.Autowired<\/span><span class=\"o\">;<\/span>\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">org.springframework.stereotype.Service<\/span><span class=\"o\">;<\/span>\r\n\r\n<span class=\"nd\">@Slf4j<\/span>\r\n<span class=\"nd\">@Service<\/span>\r\n<span class=\"kd\">public<\/span> <span class=\"kd\">class<\/span> <span class=\"nc\">TextMessageService<\/span> <span class=\"o\">{<\/span>\r\n    <span class=\"kd\">private<\/span> <span class=\"nc\">String<\/span> <span class=\"n\">replyTextMessage<\/span><span class=\"o\">;<\/span>\r\n    <span class=\"kd\">private<\/span> <span class=\"kt\">boolean<\/span> <span class=\"n\">replyFlg<\/span><span class=\"o\">;<\/span>\r\n\r\n    <span class=\"nd\">@Autowired<\/span>\r\n    <span class=\"nc\">TextMessageRepository<\/span> <span class=\"n\">textMessageRepository<\/span><span class=\"o\">;<\/span>\r\n\r\n    <span class=\"kd\">public<\/span> <span class=\"kt\">void<\/span> <span class=\"nf\">fetchReplyTextMessage<\/span> <span class=\"o\">(<\/span><span class=\"nc\">String<\/span> <span class=\"n\">message<\/span><span class=\"o\">)<\/span> <span class=\"o\">{<\/span>\r\n        <span class=\"n\">log<\/span><span class=\"o\">.<\/span><span class=\"na\">info<\/span><span class=\"o\">(<\/span><span class=\"s\">\"fetchReplyTextMessage is called: {}\"<\/span><span class=\"o\">,<\/span> <span class=\"n\">message<\/span><span class=\"o\">);<\/span>\r\n\r\n        <span class=\"nc\">Integer<\/span> <span class=\"n\">id<\/span> <span class=\"o\">=<\/span> <span class=\"n\">replyId<\/span><span class=\"o\">(<\/span><span class=\"n\">message<\/span><span class=\"o\">);<\/span>\r\n        <span class=\"k\">if<\/span> <span class=\"o\">(<\/span><span class=\"n\">id<\/span> <span class=\"o\">&gt;<\/span> <span class=\"mi\">0<\/span><span class=\"o\">)<\/span> <span class=\"o\">{<\/span>\r\n            <span class=\"k\">this<\/span><span class=\"o\">.<\/span><span class=\"na\">replyTextMessage<\/span> <span class=\"o\">=<\/span> <span class=\"n\">textMessageRepository<\/span><span class=\"o\">.<\/span><span class=\"na\">findById<\/span><span class=\"o\">(<\/span><span class=\"n\">id<\/span><span class=\"o\">).<\/span><span class=\"na\">get<\/span><span class=\"o\">().<\/span><span class=\"na\">message<\/span><span class=\"o\">;<\/span>\r\n            <span class=\"k\">this<\/span><span class=\"o\">.<\/span><span class=\"na\">replyFlg<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">true<\/span><span class=\"o\">;<\/span>\r\n        <span class=\"o\">}<\/span> <span class=\"k\">else<\/span> <span class=\"o\">{<\/span>\r\n            <span class=\"k\">this<\/span><span class=\"o\">.<\/span><span class=\"na\">replyFlg<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">false<\/span><span class=\"o\">;<\/span>\r\n        <span class=\"o\">}<\/span>\r\n    <span class=\"o\">}<\/span>\r\n\r\n    <span class=\"kd\">public<\/span> <span class=\"nc\">String<\/span> <span class=\"nf\">getReplyMessage<\/span><span class=\"o\">()<\/span> <span class=\"o\">{<\/span>\r\n        <span class=\"k\">return<\/span> <span class=\"k\">this<\/span><span class=\"o\">.<\/span><span class=\"na\">replyTextMessage<\/span><span class=\"o\">;<\/span>\r\n    <span class=\"o\">}<\/span>\r\n\r\n    <span class=\"kd\">public<\/span> <span class=\"kt\">boolean<\/span> <span class=\"nf\">getReplyFlg<\/span><span class=\"o\">()<\/span> <span class=\"o\">{<\/span>\r\n        <span class=\"k\">return<\/span> <span class=\"k\">this<\/span><span class=\"o\">.<\/span><span class=\"na\">replyFlg<\/span><span class=\"o\">;<\/span>\r\n    <span class=\"o\">}<\/span>\r\n\r\n    <span class=\"kd\">private<\/span> <span class=\"nc\">Integer<\/span> <span class=\"nf\">replyId<\/span><span class=\"o\">(<\/span><span class=\"nc\">String<\/span> <span class=\"n\">message<\/span><span class=\"o\">)<\/span> <span class=\"o\">{<\/span>\r\n        <span class=\"k\">switch<\/span> <span class=\"o\">(<\/span><span class=\"n\">message<\/span><span class=\"o\">)<\/span> <span class=\"o\">{<\/span>\r\n            <span class=\"k\">case<\/span> <span class=\"s\">\"\u30cf\u30ed\u30fc\"<\/span><span class=\"o\">:<\/span> <span class=\"o\">{<\/span>\r\n                <span class=\"k\">return<\/span> <span class=\"mi\">1<\/span><span class=\"o\">;<\/span>\r\n            <span class=\"o\">}<\/span>\r\n            <span class=\"k\">case<\/span> <span class=\"s\">\"hello\"<\/span><span class=\"o\">:<\/span> <span class=\"o\">{<\/span>\r\n                <span class=\"k\">return<\/span> <span class=\"mi\">2<\/span><span class=\"o\">;<\/span>\r\n            <span class=\"o\">}<\/span>\r\n            <span class=\"k\">default<\/span><span class=\"o\">:<\/span> <span class=\"o\">{<\/span>\r\n                <span class=\"k\">return<\/span> <span class=\"mi\">0<\/span><span class=\"o\">;<\/span>\r\n            <span class=\"o\">}<\/span>\r\n        <span class=\"o\">}<\/span>\r\n    <span class=\"o\">}<\/span>\r\n<span class=\"o\">}<\/span>\r\n<\/code><\/pre>\n<h3>\u63a7\u5236\u5668<\/h3>\n<p>\u7f16\u8f91\u4e0b\u9762\u7684\u6587\u4ef6:<br \/>\nsample-spring-boot-kitchensink\/src\/main\/java\/com\/example\/bot\/spring\/KitchenSinkController.java<\/p>\n<p>\u4e3a\u4e86\u7b80\u5316\u7406\u89e3\u5904\u7406\u5185\u5bb9\uff0c\u6211\u4eec\u5c06handleTextMessageEvent\u65b9\u6cd5\u91cd\u5199\u4e3a\u4f7f\u7528\u4e00\u4e2a\u65b9\u6cd5\u6765\u5904\u7406\u5168\u90e8\u5185\u5bb9\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"gi\">+import com.example.bot.spring.services.TextMessageService;\r\n<\/span> import org.springframework.beans.factory.annotation.Autowired;\r\n import org.springframework.web.servlet.support.ServletUriComponentsBuilder;\r\n\r\npublic class KitchenSinkController {\r\n     @Autowired\r\n     private LineBlobClient lineBlobClient;\r\n\r\n+    @Autowired\r\n<span class=\"gi\">+    private TextMessageService textMessageService;\r\n+\r\n<\/span>     @EventMapping\r\n     public void handleTextMessageEvent(MessageEvent&lt;TextMessageContent&gt; event) throws Exception {\r\n         TextMessageContent message = event.getMessage();\r\n<span class=\"gd\">-        handleTextContent(event.getReplyToken(), event, message);\r\n<\/span><span class=\"gi\">+\r\n+        log.info(\"TextMessageContent is called\");\r\n+\r\n+        textMessageService.fetchReplyTextMessage(message.getText());\r\n+\r\n+        if (textMessageService.getReplyFlg()) {\r\n+            log.info(\"replyFlg is true\");\r\n+\r\n+            String replyText = textMessageService.getReplyMessage();\r\n+            List&lt;Message&gt; replyMessage = singletonList(new TextMessage(replyText));\r\n+            String replyToken = event.getReplyToken();\r\n+\r\n+            log.info(\"Returns echo message {}: {}\", replyToken, replyText);\r\n+\r\n+            boolean notificationDisabled = false;\r\n+            try {\r\n+                BotApiResponse apiResponse = lineMessagingClient\r\n+                        .replyMessage(new ReplyMessage(replyToken, replyMessage, notificationDisabled))\r\n+                        .get();\r\n+                log.info(\"Sent messages: {}\", apiResponse);\r\n+            } catch (InterruptedException | ExecutionException e) {\r\n+                throw new RuntimeException(e);\r\n+            }\r\n+        }\r\n<\/span>     }\r\n<\/code><\/pre>\n<h3>\u4fee\u6539Procfile\u3002<\/h3>\n<p>\u4e3a\u4e86\u4f7f\u7528sample-spring-boot-kitchensink\uff0c\u9700\u4fee\u6539Procfile\u5982\u4e0b\u6240\u793a\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"gd\">-web: java $JAVA_OPTS -jar sample-spring-boot-echo\/build\/libs\/sample-spring-boot-echo-*.jar --server.port=$PORT\r\n<\/span><span class=\"gi\">+web: java $JAVA_OPTS -jar sample-spring-boot-kitchensink\/build\/libs\/sample-spring-boot-kitchensink-*.jar --server.port=$PORT\r\n<\/span><\/code><\/pre>\n<h3>.gitignore \u6587\u4ef6<\/h3>\n<p>\u5982\u679c\u5ffd\u7565\u4e86.jar\u6587\u4ef6\uff0c\u5c31\u65e0\u6cd5\u90e8\u7f72.\/gradle\/wrapper\/gradle-wrapper.jar\uff0c\u5e76\u4e14\u4f1a\u51fa\u73b0\u4ee5\u4e0b\u9519\u8bef\uff0c\u56e0\u6b64\u9700\u8981\u8fdb\u884c\u7f16\u8f91\u3002<br \/>\n\u9519\u8bef\uff1a\u65e0\u6cd5\u627e\u5230\u6216\u52a0\u8f7d\u4e3b\u7c7borg.gradle.wrapper.GradleWrapperMain\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"err\">#<\/span> Package Files #\r\n<span class=\"gd\">-*.jar\r\n<\/span><span class=\"gi\">+#*.jar\r\n<\/span> *.war\r\n *.ear\r\n<\/code><\/pre>\n<h3>\u5728Heroku\u4e0a\u8bbe\u7f6e\u73af\u5883\u53d8\u91cf\u3002<\/h3>\n<p>\u5728Heroku\u4e0a\u8bbe\u7f6e\u4ee5\u4e0b\u73af\u5883\u53d8\u91cf\u3002<\/p>\n<div>\n<div class=\"post-table\">KeyValueCHANNEL_SECRETLINE\u306eChannel access tokenCHANNEL_TOKENLINE\u306eChannel secretDATABSE_JDBC_URIjdbc:postgresql:\/\/heroku\u306epostgres\u306ehostname:5432\/dbnameDATABSE_USERheroku\u306epostgres\u306euser nameDATABSE_PASSWORDheroku\u306epostgres\u306epassword<\/div>\n<\/div>\n<h2>\u90e8\u7f72<\/h2>\n<p>\u5c06\u5e94\u7528\u7a0b\u5e8f\u90e8\u7f72\u5230Heroku\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"gp\">$<\/span> git remote add heroku https:\/\/git.heroku.com\/<span class=\"k\">${<\/span><span class=\"nv\">HEROKU_APP_NAME<\/span><span class=\"k\">}<\/span>.git\r\n<span class=\"gp\">$<\/span> git add <span class=\"nt\">--all<\/span>\r\n<span class=\"gp\">$<\/span> git commit <span class=\"nt\">-m<\/span> <span class=\"s1\">'Added a sample codeset for Qiita'<\/span>\r\n<span class=\"gp\">$<\/span> git push heroku master\r\n<\/code><\/pre>\n<h2>\u786e\u8ba4\u79fb\u6c11\u7684\u7ed3\u679c<\/h2>\n<p>\u5f53\u5c06\u7a0b\u5e8f\u90e8\u7f72\u5230Heroku\u65f6\uff0c\u5c06\u4f1a\u5728PostgreSQL\u4e2d\u521b\u5efa\u4ee5\u4e0b\u8868\u3002<br \/>\n\u5bf9\u4e8e\u5728\u672c\u5730\u4f7f\u7528Intellij\u7b49\u5f00\u53d1\u5de5\u5177\u7684\u60c5\u51b5\uff0c\u53ea\u9700\u8981\u5728IDE\u4e2d\u8fd0\u884c\u7a0b\u5e8f\uff0c\u5c31\u4f1a\u5728\u8fde\u63a5\u7684\u6570\u636e\u5e93\u4e2d\u521b\u5efa\u8868\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"gp\">$<\/span> heroku pg:psql postgresql-abcdefg-12345 <span class=\"nt\">--app<\/span> line-bot-sample\r\n<\/code><\/pre>\n<pre class=\"post-pre\"><code><span class=\"n\">line<\/span><span class=\"o\">-<\/span><span class=\"n\">bot<\/span><span class=\"o\">-<\/span><span class=\"n\">sample<\/span><span class=\"p\">::<\/span><span class=\"k\">DATABASE<\/span><span class=\"o\">=&gt;<\/span> <span class=\"err\">\\<\/span><span class=\"n\">d<\/span>\r\n                     <span class=\"n\">List<\/span> <span class=\"k\">of<\/span> <span class=\"n\">relations<\/span>\r\n <span class=\"k\">Schema<\/span> <span class=\"o\">|<\/span>         <span class=\"n\">Name<\/span>          <span class=\"o\">|<\/span>   <span class=\"k\">Type<\/span>   <span class=\"o\">|<\/span>     <span class=\"k\">Owner<\/span>\r\n<span class=\"c1\">--------+-----------------------+----------+----------------<\/span>\r\n <span class=\"k\">public<\/span> <span class=\"o\">|<\/span> <span class=\"n\">flyway_schema_history<\/span> <span class=\"o\">|<\/span> <span class=\"k\">table<\/span>    <span class=\"o\">|<\/span> <span class=\"n\">sample<\/span><span class=\"o\">-<\/span><span class=\"k\">user<\/span>\r\n <span class=\"k\">public<\/span> <span class=\"o\">|<\/span> <span class=\"n\">text_messages<\/span>         <span class=\"o\">|<\/span> <span class=\"k\">table<\/span>    <span class=\"o\">|<\/span> <span class=\"n\">sample<\/span><span class=\"o\">-<\/span><span class=\"k\">user<\/span>\r\n <span class=\"k\">public<\/span> <span class=\"o\">|<\/span> <span class=\"n\">text_messages_id_seq<\/span>  <span class=\"o\">|<\/span> <span class=\"n\">sequence<\/span> <span class=\"o\">|<\/span> <span class=\"n\">sample<\/span><span class=\"o\">-<\/span><span class=\"k\">user<\/span>\r\n<span class=\"p\">(<\/span><span class=\"mi\">3<\/span> <span class=\"k\">rows<\/span><span class=\"p\">)<\/span>\r\n<\/code><\/pre>\n<h2>\u63d2\u5165<\/h2>\n<p>\u5c06\u4ee5\u4e0b\u5185\u5bb9\u63d2\u5165\u5230\u6570\u636e\u5e93\u4e2d\u3002\u8fd9\u5c06\u4f5c\u4e3a\u56de\u590d\u6d88\u606f\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"n\">line<\/span><span class=\"o\">-<\/span><span class=\"n\">bot<\/span><span class=\"o\">-<\/span><span class=\"n\">sample<\/span><span class=\"p\">::<\/span><span class=\"k\">DATABASE<\/span><span class=\"o\">=&gt;<\/span> <span class=\"k\">insert<\/span> <span class=\"k\">into<\/span> <span class=\"n\">text_messages<\/span> <span class=\"p\">(<\/span><span class=\"n\">message<\/span><span class=\"p\">)<\/span> <span class=\"k\">values<\/span> <span class=\"p\">(<\/span><span class=\"s1\">'\u30cf\u30ed\u30fc\u30ef\u30fc\u30eb\u30c9'<\/span><span class=\"p\">);<\/span>\r\n<span class=\"k\">INSERT<\/span> <span class=\"mi\">0<\/span> <span class=\"mi\">1<\/span>\r\n<span class=\"n\">line<\/span><span class=\"o\">-<\/span><span class=\"n\">bot<\/span><span class=\"o\">-<\/span><span class=\"n\">sample<\/span><span class=\"p\">::<\/span><span class=\"k\">DATABASE<\/span><span class=\"o\">=&gt;<\/span> <span class=\"k\">insert<\/span> <span class=\"k\">into<\/span> <span class=\"n\">text_messages<\/span> <span class=\"p\">(<\/span><span class=\"n\">message<\/span><span class=\"p\">)<\/span> <span class=\"k\">values<\/span> <span class=\"p\">(<\/span><span class=\"s1\">'Hello world!!'<\/span><span class=\"p\">);<\/span>\r\n<span class=\"k\">INSERT<\/span> <span class=\"mi\">0<\/span> <span class=\"mi\">1<\/span>\r\n<span class=\"n\">line<\/span><span class=\"o\">-<\/span><span class=\"n\">bot<\/span><span class=\"o\">-<\/span><span class=\"n\">sample<\/span><span class=\"p\">::<\/span><span class=\"k\">DATABASE<\/span><span class=\"o\">=&gt;<\/span> <span class=\"k\">select<\/span> <span class=\"o\">*<\/span> <span class=\"k\">from<\/span> <span class=\"n\">text_messages<\/span><span class=\"p\">;<\/span>\r\n <span class=\"n\">id<\/span> <span class=\"o\">|<\/span>    <span class=\"n\">message<\/span>\r\n<span class=\"c1\">----+----------------<\/span>\r\n  <span class=\"mi\">1<\/span> <span class=\"o\">|<\/span> <span class=\"err\">\u30cf\u30ed\u30fc\u30ef\u30fc\u30eb\u30c9<\/span>\r\n  <span class=\"mi\">2<\/span> <span class=\"o\">|<\/span> <span class=\"n\">Hello<\/span> <span class=\"n\">world<\/span><span class=\"o\">!!<\/span>\r\n<span class=\"p\">(<\/span><span class=\"mi\">2<\/span> <span class=\"k\">rows<\/span><span class=\"p\">)<\/span>\r\n<\/code><\/pre>\n<h2>\u5728LINE\u4e0a\u8a2d\u7f6eWebhook URL\u3002<\/h2>\n<p>\u5728LINE\u5f00\u53d1\u8005\u7684Message API\u7684Webhook\u8bbe\u7f6e\u4e2d\uff0c\u6ce8\u518cheroku\u7684URL\u3002<\/p>\n<p>Webhook URL: https:\/\/{HEROKU_APP_NAME}.herokuapp.com\/callback<br \/>\nWebhook \u4f7f\u7528: \u8bbe\u4e3a\u542f\u7528<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d3cb837434c4406c8589f\/79-0.png\" alt=\"IMG_5259.PNG\" \/><\/div>\n<h2>\u5982\u679c\u8981\u7f16\u5199\u6d4b\u8bd5\u4ee3\u7801<\/h2>\n<p>\u5728\u4e0b\u9762\u521b\u5efa\u4e00\u4e2a\u540d\u4e3atest\u7684\u76ee\u5f55\uff0c\u5e76\u5728\u8be5\u76ee\u5f55\u4e0b\u521b\u5efa\u6d4b\u8bd5\u4ee3\u7801\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"gp\">$<\/span> <span class=\"nb\">mkdir<\/span> <span class=\"nt\">-p<\/span> sample-spring-boot-kitchensink\/src\/test\/java\/com\/example\/bot\/spring\/\r\n<\/code><\/pre>\n<h2>\u8bf7\u5728\u6c49\u8bed\u4e2d\u63d0\u4f9b\u4ee5\u4e0b\u5185\u5bb9\u7684\u540c\u4e49\u8868\u8ff0\uff1a<\/h2>\n<p>\u501f\u9274<\/p>\n<p>\u4ee5\u4e0b\u662f\u4e2d\u6587\u7684\u7248\u672c\uff1a<br \/>\nhttps:\/\/tosi-tech.net\/2019\/04\/flyway-with-spring-boot\/ \u53ef\u4ee5\u53c2\u8003\u6b64\u94fe\u63a5\u4e86\u89e3\u5982\u4f55\u5728Spring Boot\u4e2d\u4f7f\u7528Flyway\u8fdb\u884c\u6570\u636e\u5e93\u8fc1\u79fb\u3002<\/p>\n<p>https:\/\/tomokazu-kozuma.com\/how-to-migrate-mysql-using-flyway\/ \u4f60\u53ef\u4ee5\u53c2\u8003\u6b64\u94fe\u63a5\u4e86\u89e3\u5982\u4f55\u4f7f\u7528Flyway\u8fdb\u884cMySQL\u6570\u636e\u5e93\u8fc1\u79fb\u7684\u65b9\u6cd5\u3002<\/p>\n<h2>\u76f8\u5173<\/h2>\n<ul class=\"post-ul\">LINE BOT API\u306ereply\u3067\u305f\u307e\u306bInvalid reply token\u304c\u767a\u751f\u3059\u308b<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>\u603b\u7ed3 \u4f7f\u7528Spring Boot\u3001JPA\u3001 Flyway\u548cHeroku\u521b\u5efa\u4e00\u4e2aLINE Bot\uff0c\u4ece\u6570\u636e\u5e93\u4e2d\u83b7\u53d6 [&hellip;]<\/p>\n","protected":false},"author":11,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-39794","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v21.5 (Yoast SEO v21.5) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>\u5728Heroku\u4e0a\u4f7f\u7528Spring Boot + JPA + Flyway\u6765\u521b\u5efaLINE Bot - Blog - Silicon Cloud<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.silicloud.com\/zh\/blog\/\u5728heroku\u4e0a\u4f7f\u7528spring-boot-jpa-flyway\u6765\u521b\u5efaline-bot\/\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"\u5728Heroku\u4e0a\u4f7f\u7528Spring Boot + JPA + Flyway\u6765\u521b\u5efaLINE Bot\" \/>\n<meta property=\"og:description\" content=\"\u603b\u7ed3 \u4f7f\u7528Spring Boot\u3001JPA\u3001 Flyway\u548cHeroku\u521b\u5efa\u4e00\u4e2aLINE Bot\uff0c\u4ece\u6570\u636e\u5e93\u4e2d\u83b7\u53d6 [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.silicloud.com\/zh\/blog\/\u5728heroku\u4e0a\u4f7f\u7528spring-boot-jpa-flyway\u6765\u521b\u5efaline-bot\/\" \/>\n<meta property=\"og:site_name\" content=\"Blog - Silicon Cloud\" \/>\n<meta property=\"article:published_time\" content=\"2023-05-22T19:10:36+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-04-29T13:11:45+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d3cb837434c4406c8589f\/79-0.png\" \/>\n<meta name=\"author\" content=\"\u65b0, \u97f5\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"\u65b0, \u97f5\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/\",\"url\":\"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/\",\"name\":\"\u5728Heroku\u4e0a\u4f7f\u7528Spring Boot + JPA + Flyway\u6765\u521b\u5efaLINE Bot - Blog - Silicon Cloud\",\"isPartOf\":{\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#website\"},\"datePublished\":\"2023-05-22T19:10:36+00:00\",\"dateModified\":\"2024-04-29T13:11:45+00:00\",\"author\":{\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/4ba4019495123db3038fd0809e6959c9\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/www.silicloud.com\/zh\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"\u5728Heroku\u4e0a\u4f7f\u7528Spring Boot + JPA + Flyway\u6765\u521b\u5efaLINE Bot\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#website\",\"url\":\"https:\/\/www.silicloud.com\/zh\/blog\/\",\"name\":\"Blog - Silicon Cloud\",\"description\":\"\",\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/4ba4019495123db3038fd0809e6959c9\",\"name\":\"\u65b0, \u97f5\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d484b6c6e4ae82e8a9efea989e1d2af46d9b6ef128101e63b18f559fca0ae627?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d484b6c6e4ae82e8a9efea989e1d2af46d9b6ef128101e63b18f559fca0ae627?s=96&d=mm&r=g\",\"caption\":\"\u65b0, \u97f5\"},\"url\":\"https:\/\/www.silicloud.com\/zh\/blog\/author\/yunxin\/\"},{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/#local-main-organization-logo\",\"url\":\"\",\"contentUrl\":\"\",\"caption\":\"Blog - Silicon Cloud\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"\u5728Heroku\u4e0a\u4f7f\u7528Spring Boot + JPA + Flyway\u6765\u521b\u5efaLINE Bot - Blog - Silicon Cloud","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.silicloud.com\/zh\/blog\/\u5728heroku\u4e0a\u4f7f\u7528spring-boot-jpa-flyway\u6765\u521b\u5efaline-bot\/","og_locale":"zh_CN","og_type":"article","og_title":"\u5728Heroku\u4e0a\u4f7f\u7528Spring Boot + JPA + Flyway\u6765\u521b\u5efaLINE Bot","og_description":"\u603b\u7ed3 \u4f7f\u7528Spring Boot\u3001JPA\u3001 Flyway\u548cHeroku\u521b\u5efa\u4e00\u4e2aLINE Bot\uff0c\u4ece\u6570\u636e\u5e93\u4e2d\u83b7\u53d6 [&hellip;]","og_url":"https:\/\/www.silicloud.com\/zh\/blog\/\u5728heroku\u4e0a\u4f7f\u7528spring-boot-jpa-flyway\u6765\u521b\u5efaline-bot\/","og_site_name":"Blog - Silicon Cloud","article_published_time":"2023-05-22T19:10:36+00:00","article_modified_time":"2024-04-29T13:11:45+00:00","og_image":[{"url":"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d3cb837434c4406c8589f\/79-0.png"}],"author":"\u65b0, \u97f5","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"\u65b0, \u97f5","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"5 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/","url":"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/","name":"\u5728Heroku\u4e0a\u4f7f\u7528Spring Boot + JPA + Flyway\u6765\u521b\u5efaLINE Bot - Blog - Silicon Cloud","isPartOf":{"@id":"https:\/\/www.silicloud.com\/zh\/blog\/#website"},"datePublished":"2023-05-22T19:10:36+00:00","dateModified":"2024-04-29T13:11:45+00:00","author":{"@id":"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/4ba4019495123db3038fd0809e6959c9"},"breadcrumb":{"@id":"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/www.silicloud.com\/zh\/blog\/"},{"@type":"ListItem","position":2,"name":"\u5728Heroku\u4e0a\u4f7f\u7528Spring Boot + JPA + Flyway\u6765\u521b\u5efaLINE Bot"}]},{"@type":"WebSite","@id":"https:\/\/www.silicloud.com\/zh\/blog\/#website","url":"https:\/\/www.silicloud.com\/zh\/blog\/","name":"Blog - Silicon Cloud","description":"","inLanguage":"zh-Hans"},{"@type":"Person","@id":"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/4ba4019495123db3038fd0809e6959c9","name":"\u65b0, \u97f5","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d484b6c6e4ae82e8a9efea989e1d2af46d9b6ef128101e63b18f559fca0ae627?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d484b6c6e4ae82e8a9efea989e1d2af46d9b6ef128101e63b18f559fca0ae627?s=96&d=mm&r=g","caption":"\u65b0, \u97f5"},"url":"https:\/\/www.silicloud.com\/zh\/blog\/author\/yunxin\/"},{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.silicloud.com\/zh\/blog\/%e5%9c%a8heroku%e4%b8%8a%e4%bd%bf%e7%94%a8spring-boot-jpa-flyway%e6%9d%a5%e5%88%9b%e5%bb%baline-bot\/#local-main-organization-logo","url":"","contentUrl":"","caption":"Blog - Silicon Cloud"}]}},"_links":{"self":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts\/39794","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/users\/11"}],"replies":[{"embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/comments?post=39794"}],"version-history":[{"count":2,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts\/39794\/revisions"}],"predecessor-version":[{"id":87666,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts\/39794\/revisions\/87666"}],"wp:attachment":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/media?parent=39794"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/categories?post=39794"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/tags?post=39794"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}