アクセスありがとうございます!次は「とあるエンジニアのエソラゴト」で検索して頂けると嬉しいです!

Springboot+Doma2+Gradle + Dockerを使って、SQLファイルベースでDBアクセスを実現する

はじめに

Springbootでアプリケーションを開発する際、DBのライブラリは何を使っているのでしょうか?

自分の経験上、結構色んな種類のものがあって非常に悩みどころです。

今回は、SQLファイルを使ってSQL文を発行出来るDoma2というDBアクセスフレームワークを使用して、簡単にDBアクセスを実現していきます。

一個、パターンを覚えておくと、DBアクセスを利用する色んな現場で使えますので、是非ともご確認頂きたいと思います。

環境

環境は以下の通り。

  • Springboot : 2.2.6
  • Doma2 : 2.20.0
  • PostgreSQL : 11.7
  • Docker : 19.03.8

DBはDockerを使用して、PostgreSQLをインストールします。

フォルダ構成

今回、作成するファイルのフォルダ構成は以下となります。

demo2-sample
|--src
|  |--main
|  |  |--java
|  |  |  |--com
|  |  |  |  |--example
|  |  |  |  |  |--Demo2SampleApplication.java
|  |  |  |  |  |--controller
|  |  |  |  |  |  |--SampleController.java
|  |  |  |  |  |--dao
|  |  |  |  |  |  |--SampleDao.java
|  |  |  |  |  |--entity
|  |  |  |  |  |  |--Sample.java
|  |  |  |  |  |--service
|  |  |  |  |  |  |--SampleService.java
|  |  |  |  |  |  |--impl
|  |  |  |  |  |  |  |--SampleServiceImpl.java
|  |  |--resources
|  |  |  |--META-INF
|  |  |  |  |--com
|  |  |  |  |  |--example
|  |  |  |  |  |  |--dao
|  |  |  |  |  |  |  |--SampleDao
|  |  |  |  |  |  |  |  |--selectAll.sql
|  |  |  |--application.properties
|  |--test
|--build.gradle
|--Dockerfile
|--docker-compose.yml
ファイルの数が多く、フォルダ構成が結構ややこしいですね。

ポイントは「resources/META-INF」以下のSQLファイルです。

Doma2はSQLファイルベースで、DBアクセスします。

利点は以下となります。

  • Doma2はSQLをJavaのクラスやアノテーションを組み合わせて構成するのではなく、SQLファイルで構成されている。
  • SQL文を理解出来る人であれば、そのままその知識を使うことが出来る。
  • SQLがベースなので、複雑なSQLも簡単に実装が出来る

Springbootプロジェクトの作成

Spring Initializrで楽々プロジェクト作成

Springbootのプロジェクトを作成する際は、以下を使用するのが便利です。

Spring Initializr

あとで、build.gradleを修正するので、こんな感じで、適当に作っちゃって下さい。

build.gradleの修正

build.gradleの修正はとても難しい!

モジュール間の依存関係を考えたりとか、一番頭を悩ましやすいポイントではないでしょうか?

多分、ここが一番ハマる。(自分もハマった)

Doma2を使う場合は、以下のようにbuild.gradleを修正する。

plugins {
    id 'org.springframework.boot' version '2.2.6.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

// テンポラリディレクトリのパスを定義する
ext.domaResourcesDir = "${buildDir}/tmp/doma-resources"

// domaが注釈処理で参照するリソースをテンポラリディレクトリに抽出
task extractDomaResources(type: Copy, dependsOn: processResources)  {
    from processResources.destinationDir
    include 'doma.compile.config'
    include 'META-INF/**/*.sql'
    include 'META-INF/**/*.script'
    into domaResourcesDir
}

// テンポラリディレクトリ内のリソースをcompileJavaタスクの出力先ディレクトリにコピーする
task copyDomaResources(type: Copy, dependsOn: extractDomaResources)  {
    from domaResourcesDir
    into compileJava.destinationDir
}

repositories {
    mavenCentral()
}

compileJava {
    // 上述のタスクに依存させる
    dependsOn copyDomaResources
    // テンポラリディレクトリをcompileJavaタスクの入力ディレクトリに設定する
    inputs.dir domaResourcesDir
    options.encoding = 'UTF-8'
}

compileTestJava {
    options.encoding = 'UTF-8'
    // テストの実行時は注釈処理を無効にする
    options.compilerArgs = ['-proc:none']
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'org.postgresql:postgresql'
    annotationProcessor 'org.projectlombok:lombok'
    implementation "org.seasar.doma.boot:doma-spring-boot-starter:1.1.1"
    // domaの注釈処理を実行することを示す
    annotationProcessor "org.seasar.doma:doma:2.20.0"
    // domaへの依存を示す
    implementation "org.seasar.doma:doma:2.20.0"
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

test {
    useJUnitPlatform()
}
上手く設定が出来ていないと、ビルドした際にDoma2のimplファイルが出力されないので、注意下さい!

application.propertiesの作成

application.propertiesはこんな感じで。

DB接続に関するプロパティを設定する。

以下の設定はPostgreSQLですが、自分の環境に合わせて書き換えて下さい。

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://db:5432/sample_db
spring.datasource.username=postgres
spring.datasource.password=password

正しく書くことが出来れば、DB接続が出来ます。

Dockerを使った環境準備

Dockerを使って、環境準備をしていきます。

関連記事

はじめに 読者 Dockerっていう言葉を最近よく聞くけど、よく分からない 読者 Dockerって何が便利なの? 今回は、そんな疑問に答えます! Dockerをまともに使えないエンジニアはかなり遅れているとい[…]

Dockerfileの修正

Dockerfileは以下のような感じで定義。

FROM openjdk:8

RUN mkdir /api
WORKDIR /api
COPY ./gradlew /api
COPY ./build.gradle /api
COPY ./settings.gradle /api
COPY ./src /api/src
COPY ./gradle /api/gradle
ENTRYPOINT ["sh", "./gradlew", "bootRun"]
関連記事

はじめに 前回の記事で「Dockerをなぜ使うべきなのか」を解説しました。 [sitecard subtitle=関連記事 url=https://ya6mablog.com/why-use-docker/ target=] […]

docker-compose.ymlの作成

docker-compose.ymlは以下のような感じで。

サービスとして、以下を定義する。

  1. PostgreSQLを動かすdb
  2. REST APIを動かすapi

コード例は以下です。

version: "3"

services:
  db:
    image: postgres:11-alpine
    container_name: db
    ports:
      - 5433:5432
    environment:
      POSTGRES_PASSWORD: password
      POSTGRES_DB: sample_db
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
    volumes:
      - db_data:/var/lib/postgresql/data
  api:
    build: ./
    container_name: api
    ports:
      - "8080:8080"
    environment:
      spring.datasource.url: "jdbc:postgresql://db:5432/sample_db"
    depends_on:
      - db
volumes:
  db_data: {}

ここまで出来たら、以下のコマンドを流して、環境構築が完了します。

# Dockerイメージのビルド
$ docker-compose build

# Dockerイメージの起動
$ docker-compose up -d

環境ができたらPostgreSQL上に、以下のようにsampleテーブルを作成して、適当にデータを入れて下さい!

create table public.sample ( id serial not null,
name text not null,
age int not null,
constraint sample_pkey primary key (id) );
ここまででDockerの設定が完了しました!

SpringbootにDoma2処理の実装

では、ここまでで環境設定が出来ましたので、Springbootに実装をしていきましょう!

Daoの作成

Daoには実行するSQLのインターフェースを作成する。

ビルドした際に、このインターフェースの実装クラスが出来上がる。

もし、上記で記載したbuild.gradleの設定が間違えていると、implクラスが正常に出力されませんので、ご注意!!
package com.example.dao;

import java.util.List;
import org.seasar.doma.Dao;
import org.seasar.doma.Select;
import org.seasar.doma.boot.ConfigAutowireable;
import com.example.entity.Sample;

@ConfigAutowireable
@Dao
public interface SampleDao {

  @Select
  List<Sample> selectAll();

}

SQLファイルの作成

Doma2が発行するSQLファイルを作成する必要があるため、作成する。

ポイントは以下の2点!!

  1. SQLファイルの配置場所で「resources/META-INF」以下にソースコードのDaoと同じパッケージになるように配置する。
  2. ファイル名はDaoで定義したメソッド名と同じ名前になるようにする。

抜粋すると、こんな感じ。

demo2-sample
|--src
|  |--main
|  |  |--java
|  |  |  |--com
|  |  |  |  |--example
|  |  |  |  |  |--dao
|  |  |  |  |  |  |--SampleDao.java
|  |  |--resources
|  |  |  |--META-INF
|  |  |  |  |--com
|  |  |  |  |  |--example
|  |  |  |  |  |  |--dao
|  |  |  |  |  |  |  |--SampleDao
|  |  |  |  |  |  |  |  |--selectAll.sql

設定が間違っていると、コンパイルエラーになります。

エンティティの実装

エンティティを実装する。

例なので、すごく簡単な構造で。

アノテーションの設定を忘れないようご注意下さい!

package com.example.entity;

import java.io.Serializable;
import org.seasar.doma.Column;
import org.seasar.doma.Entity;
import org.seasar.doma.GeneratedValue;
import org.seasar.doma.GenerationType;
import org.seasar.doma.Id;
import org.seasar.doma.Table;
import lombok.Getter;
import lombok.Setter;

@Entity
@Table(name = "sample")
@Getter
@Setter
public class Sample implements Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private long id;

  @Column(name = "name")
  private String name;

  @Column(name = "age")
  private int age;

}

サービスの実装

こちらもすごく簡単な構造で。

package com.example.service;

import java.util.List;
import com.example.entity.Sample;

public interface SampleService {

  public abstract List<Sample> findAll();
}

こちらは実装クラス。

アノテーションの設定忘れにご注意!

package com.example.service.impl;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.dao.SampleDao;
import com.example.entity.Sample;
import com.example.service.SampleService;

@Service
public class SampleServiceImpl implements SampleService {

  @Autowired
  private SampleDao sampleDao;

  @Override
  public List<Sample> findAll() {
    return sampleDao.selectAll();
  }
}

コントローラーの実装

REST APIを作成する。

package com.example.controller;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.entity.Sample;
import com.example.service.SampleService;

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

  @Autowired
  private SampleService sampleService;

  @CrossOrigin
  @RequestMapping(value = "/", method = RequestMethod.GET)
  public List<Sample> index() {
    return sampleService.findAll();
  }
}

では、動かしてみよう!

お待たせしました!ここまでで、動かす準備が整いました!

Dockerコンテナを停止している場合は、再度起動コマンドをコマンドプロンプト、またはターミナルから打ち込んで下さい。

すると、DBとSpringbootが起動しますので、「http://localhost:8080/api/sample/」にアクセスして下さい。

登録したデータがJSON形式で返ってきたら成功です!

返ってこない場合は、どこか設定が間違えている可能性がありますので、もう一度、記事を見直して見て下さい!

是非とも、このパターンを覚えて色んな現場やプロジェクトで使ってみて下さい!

最新情報をチェックしよう!