【今更ながらSpring BootでWEB開発 #5】データベースの接続

Web

今回の目標

前回はサーバーサイドでフォームのバリデーションを実行する方法を説明しました。

【今更ながらSpring BootでWEB開発 #4】バリデーション
Spring Bootでは受け取ったリクエストのバリデーションをアノテーションを用いることで設定・実行することができます。今回はアノテーションの基本的な使用方法について説明します。

今回はデータベースへの接続方法を説明し、送信されたデータを登録するまでを実装していきます。

データベースへの接続

ここで使用するデータベースは「PostgreSQL」とします。その他のデータベースでもそれほど大きな違いはありません。

事前に「spring-sample」というデータベースを作成し、「inquiry」テーブルを以下の内容で定義しておきます。

CREATE TABLE public.inquiry (
  id serial NOT NULL,
  "name" varchar(64) NOT NULL,
  email varchar(128) NOT NULL,
  contents text NOT NULL,
  created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT inquiry_pkey PRIMARY KEY (id)
);

細かい設定は適当でかまいません。ここではidは自動採番され、createdは現在日時が登録されるようにしています。

ライブラリの読み込み

データベースの接続に必要なライブラリを読み込むようにGradleに設定します。今回使用するのは「Spring Data JDBC」と「PostgreSQL Driver」です。

プロジェクトディレクトリ直下にあるbuild.gradleのdependenciesに次の2行を追加します。

implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
runtimeOnly 'org.postgresql:postgresql'

追加したらGradleの再ビルドを実行します。再ビルドはbuild.gradleで右クリックし、Gradle > Refresh Gradle Project を選択します。

事前に使用することが決まっている場合は、プロジェクト作成時に追加することもできます。

Spring Bootには、データベースアクセスの仕組みとしてSpring Data JDBC以外に「Spring Data JPA」というものがあります。

Spring Data JDBCは単純なSQL文を作成することでデータベースとのやり取りを行います。対してSpring Data JPAはアノテーションを利用することでより簡単に高度なデータベースアクセスを行うことができます。

Spring Data JPAについてはまた別の機会に説明できたらと思います。(頑張って理解しておきます…)

接続情報の設定

データベースの接続情報をapplication.propertiesに設定します。

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

これでデータベースの接続設定は完了です。

データベース処理について

Spring Bootでデータベースを扱う処理は次のようなイメージで実装します。正直ここに関しては、いろいろ調べましたが情報が錯綜しており、結局よくわかりませんでした。よって個人的な解釈を記載します。

Controller

Controllerは今まで実装してきたように、フロントエンドからリクエストを受け取り、ページ表示の制御やエラー制御までを行います。Controllerでは具体的な処理は実装しません。

Entity

Entityはデータを保持するためのオブジェクトです。基本的にテーブルに対応するカラムをフィールドに持ち、Controller、Service、Dao間で受け渡されていきます。

Service

ServiceはControllerからEntityを受け取り、それを基にした具体的な処理を実装します。Serviceは直接DBを参照してはいけません。DBを参照する場合はDAOを経由します。

DAO

DAOは、Data Access Objectの略で、CRUDなどデータベースに関する処理を実装していきます。Readの場合は、結果をEntityに格納します。Create、Update、Deleteは、渡されたEntityの値によって処理を実行します。

DAOを調べているとRepositoryというのがセットで出てきます。正確には2つは違うもの(らしい)ですが、とりあえずは無視させてください…。

お問い合わせ内容の登録

確認ページの送信ボタンがクリックされたら、入力されたお問い合わせ内容を登録し、登録完了ページを表示するように実装します。

確認ページからデータを送信

これまでと同様にフォームを作成してデータを送信するようにconfirm.htmlを書き換えます。今回は「/inquiry/save」にリクエストを送信します。

<form method="post" action="#" th:action="@{/inquiry/save}" th:object="${inquiryForm}">
  <input type="hidden" name="name" th:value="*{name}">
  <input type="hidden" name="email" th:value="*{email}">
  <input type="hidden" name="contents" th:value="*{contents}">
  <input type="submit" value="送信">
</form>

データ送信用に入力欄(input)が必要なのですが、今回は確認ページなので表示したくありません。その場合はinputのtypeにhiddenを設定します。

各クラスの作成

先ほど説明したように、Entity、Service、DAOを次のディレクトリ階層で作成します。

src/main/java
└ com.example.demo
  ├ app
  └ domain
    ├ dao
    ├ entity
    └ service

Entityクラスの作成

Entityクラスとして「Inquiry」クラスを作成します。

public class Inquiry {
  private int id;
  private String name;
  private String email;
  private String contents;
  private LocalDateTime created;

  //Getter, Setterは省略
}

DAOクラスの作成

DAOクラスを作成し、inquiryテーブルへの登録処理(create)を定義していきます。DAOクラスは、インターフェースを作成し、実装クラスとして作成していきます。

  • InquiryDao(Interface)
public interface InquiryDao {
  void create(Inquiry inquiry);
}
  • InquiryDaoImpl(Class)
public class InquiryDaoImpl implements InquiryDao {
  
  private JdbcTemplate jdbcTemplate;
  
  @Autowired
  public InquiryDaoImpl(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
  }

  @Override
  public void create(Inquiry inquiry) {
    jdbcTemplate.update("INSERT INTO inquiry (name, email, contents) VALUES (?, ?, ?)",
        inquiry.getName(), inquiry.getEmail(), inquiry.getContents()); 
  }
}

JdbcTemplateクラスは、Java Data JDBCでデータベースにアクセスするためのクラスです。不必要にインスタンスを生成されないように、InquiryDaoImplのコンストラクタでのみインスタンスを生成するようにします(シングルトン)。

また、コンストラクタに@Autowiredとすることで、引数のJdbcTemplateのインスタンスを自動で生成してくれるようになります。正直この辺りは詳しくは理解できていません。

実際の登録の処理についてですが、JdbcTemplateのupdateメソッドによって実装します。第一引数にクエリ(SQL文)をPrepared Statementの形式で指定します。

Prepared Statementは、SQLインジェクションを防ぐための仕組みです。クエリの「?」はプレースホルダーと呼ばれ、一般的には「?」の位置に該当するデータとその型を指定します。

JdbcTemplateでは、第二引数以降でプレースホルダーを設定していきます。今回はname、email、contentsの値を登録したいので、第二引数以降にinquiryに格納されたそれぞれの値を順番通りに設定します。

Serviceクラスの作成

DAOクラスと同様にインターフェースを作成し、実装クラスとしてServiceクラスを作成します。今回はデータベースの登録のみなので、処理としてはDAOを呼び出すだけです。

  • InquiryService(Interface)
public interface InquiryService {
  void save(Inquiry inquiry);
}
  • InquiryServiceImpl
@Service
public class InquiryServiceImpl implements InquiryService {
  
  private InquiryDao dao;
  
  @Autowired
  public InquiryServiceImpl(InquiryDao dao) {
    this.dao = dao;
  }

  @Override
  public void save(Inquiry inquiry) {
    dao.create(inquiry);
  }
}

DAOクラスをシングルトンとし、@Autowiredでインスタンスを生成します。

Controllerクラスの修正

Controllerクラスに「/inquiry/save」の処理を追加します。

@Controller
@RequestMapping("/inquiry")
public class InquiryController {

  @Autowired
  private InquiryService service;

  //省略

  @PostMapping("/save")
  public String save(@Validated InquiryForm inquiryForm, BindingResult result) {
    if (result.hasErrors()) {
      //TODO Exception
    }
    Inquiry inquiry = new Inquiry(inquiryForm.getName(), inquiryForm.getEmail(), inquiryForm.getContents());
    service.save(inquiry);
    return "inquiry/complete.html";
  }
}

@AutowiredによってServiceクラスのインスタンスを自動で生成されるようにしておきます。

リクエスト部分では、受け取ったリクエストパラメータからEntityを生成し、Serviceクラスで実装したsaveメソッドを実行します。

あとは完了画面を表示して完了です。(完了画面は適当に作ってください。)

実際に動かしてみてデータベースに入力内容が登録されていることを確認しましょう。

あとがき

今回はデータベースのアクセス方法としてJava Data JDBCについて説明しました。またデータベースを扱う処理としてController、Service、Dao、Entityの役割についても説明しました。

実際にはここに例外処理やトランザクションといったことを考えなければいけません。それはまた別の機会に触れられたらと思います。

 

- Spring Bootのおすすめ書籍はコチラ -

コメント

タイトルとURLをコピーしました