kz’s blog

興味のあることについて書いていきます。

Heroku の Spring Boot アプリでテーブル接続を行う

環境

macOS Mojave 10.14.1
Eclipse (Pleiades) Oxygen.3a Release (4.7.3a)

使用するもの

  • Heroku
  • Spring Boot
  • PostgreSQL
  • Gradle
  • Lombok

事前準備

あらかじめ Java 8 をインストールしておいてください。

Heroku 登録および設定

登録

下記サイトから登録をします。

signup.heroku.com

CLI インストール

brew install heroku/brew/heroku

Homebrew がない場合はこちらからダウンロードしてインストールします。

PostgreSQL インストール

下記サイトを参照し、①〜③を実施します。

postgresapp.com

データ作成

上記①で起動した PostgreSQL アプリから使用するデータベースをダブルクリックし、ターミナルを立ち上げます。

あらかじめ以下のようにテーブルを作成しておきます。

# select * from sample;
 id | name 
----+------
  1 | hoge
  2 | fuga
  3 | piyo
(3 rows)

Spring Boot アプリケーション作成

作成方法は下記記事を参考にして頂ければ。

www.kz62.net

依存追加

build.gradledependenciesに追加します。

dependencies {
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('org.springframework.boot:spring-boot-devtools')
    compileOnly('org.projectlombok:lombok')
    runtime('org.postgresql:postgresql')
    implementation('org.springframework.boot:spring-boot-starter-web')
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

org.springframework.boot:spring-boot-devtoolsはホットデプロイするために使用。

データベース接続設定

src/main/resources/application.propertiesを作成します。

spring.datasource.url=${DATABASE_URL}
spring.datasource.driverClassName=org.postgresql.Driver

${DATABASE_URL}については環境変数から取得するようにしています。

こうすることで、Heroku にデプロイした時は Heroku 上のデータベースに接続することができるようになります。

Mac での環境変数の設定

こちらのサイトを参考にさせていただきました。

setenv.DATABASE_URL.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  <plist version="1.0">
  <dict>
  <key>Label</key>
  <string>setenv.DATABASE_URL</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/launchctl</string>
    <string>setenv</string>
    <string>DATABASE_URL</string>
    <string>jdbc:postgresql://localhost:5432/{データベース名}</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>ServiceIPC</key>
  <false/>
</dict>
</plist>

ソースファイル作成

Entity

package com.example.sample.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Entity
@Getter
@Setter
@ToString
public class Sample {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;

    private String name;

}

Lombok を使用して Getter, Setter を省略しています。

DAO

package com.example.sample.domain;

import org.springframework.data.jpa.repository.JpaRepository;

public interface SampleRepository  extends JpaRepository<Sample, Integer>{

}

Service

package com.example.sample.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.sample.domain.Sample;
import com.example.sample.domain.SampleRepository;

@Service
@Transactional
public class SampleService {

    @Autowired
    SampleRepository sampleRepository;

    public List<Sample> selectAll() {
        return sampleRepository.findAll();
    }

}

Controller

package com.example.sample.web;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.example.sample.domain.Sample;
import com.example.sample.service.SampleService;

@RestController
@RequestMapping("sample")
public class SampleController {

    @Autowired
    SampleService sampleService;

    @RequestMapping(value = "", method = RequestMethod.GET)
    public String hello() {
        return "Hello world.";
    }

    @RequestMapping(value = "select-all", method = RequestMethod.GET)
    public List<Sample> selectAll() {
        return sampleService.selectAll();
    }
}

注意点

注意点として、パッケージ構成を上記の通りにしないと@Autowiredでインジェクションができません。 (アノテーションを別途定義すればできないことはない)

こちらのサイトを参考にさせて頂きました。

lombok インストール方法

少しハマったので書いておきます。

build.gradleに依存を入れただけだと適切に動かないので、以下の手順を実行します。

  • Eclipse のアプリのパッケージ内を表示します。
  • Contents/MacOSlombok.jarを入れます。※ ファイル名注意
  • Contents/Eclipse/eclipse.iniに2行追記
-Xbootclasspath/a:lombok.jar
-javaagent:lombok.jar

ローカルで起動確認

Eclipse でプロジェクト名のコンテキストメニューを開き、Spring Boot アプリケーションで実行します。

この時、以下のようなエラーが出る場合があると思います。

Caused by: java.sql.SQLFeatureNotSupportedException: org.postgresql.jdbc.PgConnection.createClob() メソッドはまだ実装されていません。

この場合、src/main/resources/hibernate.propertiesを作成します。

hibernate.jdbc.lob.non_contextual_creation=true

画面確認

http://localhost:8080/sample/select-all

URL にアクセスし、 sample テーブルにインサートした内容が JSON で表示されていればOKです。

Heroku 側の起動確認

以下、ターミナルでの作業です。

Git コミット

git init
git add .
git commit -m "commit message"

Heroku にデプロイ

heroku login
heroku create
git push heroku master
heroku open

この状態で、ドメイン/sampleにアクセスするとHello world.が表示されます。

Heroku の DB 設定

PostgreSQL のアドオンを追加

heroku addons:create heroku-postgresql

Heroku の PostgreSQL に接続

heroku pg:psql

ローカルのテーブルと同様に作成します。

この状態で、ドメイン/sample/select-allにアクセスするとテーブルの内容が JSON で表示されます。

2回目以降のデプロイ

git add .
git commit -m "commit message"
git push heroku master
heroku open