JPA で MySQL データアクセス

このガイドでは、MySQL データベースに接続された Spring アプリケーションを作成するプロセスを順を追って説明します(他のほとんどのガイドや多くのサンプルアプリケーションが使用するインメモリの組み込みデータベースとは対照的です)。Spring Data JPA を使用してデータベースにアクセスしますが、これは多くの選択肢のうちの 1 つにすぎません(たとえば、プレーンな Spring JDBC を使用できます)。

構築するもの

MySQL データベースを作成し、Spring アプリケーションを構築して、新しく作成したデータベースに接続します。

MySQL は GPL でライセンスされているため、配布するプログラムバイナリも GPL を使用する必要があります。GNU General Public License (英語) を参照してください。

必要なもの

このガイドを完了する方法

ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了することも、このリポジトリ [GitHub] (英語) のコードを確認してソリューションに直接進むこともできます。

ローカル環境で最終結果を確認するには、次のいずれかを実行します。

MySQL データベースの設定

アプリケーションをビルドする前に、まず MySQL データベースを構成する必要があります。このガイドでは、Spring Boot Docker Compose サポートを使用することを前提としています。このアプローチの前提条件は、開発マシンに Docker デスクトップ (英語) などの Docker 環境が利用可能であることです。次の操作を実行する依存関係 spring-boot-docker-compose を追加します。

  • 作業ディレクトリで compose.yml やその他の一般的な compose ファイル名を検索します

  • 検出された compose.yml を使用して docker compose up を呼び出します

  • サポートされているコンテナーごとにサービス接続 Bean を作成する

  • アプリケーションのシャットダウン時に docker compose stop を呼び出します

Docker Compose サポートを使用するには、このガイドに従うだけです。取り込んだ依存関係に基づいて、Spring Boot は正しい compose.yml ファイルを見つけ、アプリケーションの実行時に Docker コンテナーを起動します。

Spring Initializr から開始

IDE を使用する場合はプロジェクト作成ウィザードを使用します。IDE を使用せずにコマンドラインなどで開発する場合は、この事前に初期化されたプロジェクトからプロジェクトを ZIP ファイルとしてダウンロードできます。このプロジェクトは、このチュートリアルの例に合うように構成されています。

プロジェクトを手動で初期化するには:

  1. IDE のメニューまたはブラウザーから Spring Initializr を開きます。アプリケーションに必要なすべての依存関係を取り込み、ほとんどのセットアップを行います。

  2. Gradle または Maven のいずれかと、使用する言語を選択します。このガイドは、Java を選択したことを前提としています。

  3. 依存関係をクリックし、Spring WebSpring Data JPAMySQL ドライバーDocker Compose サポートTestcontainers を選択します。

  4. 生成をクリックします。

  5. 結果の ZIP ファイルをダウンロードします。これは、選択して構成された Web アプリケーションのアーカイブです。

EclipseIntelliJ のような IDE は新規プロジェクト作成ウィザードから Spring Initializr の機能が使用できるため、手動での ZIP ファイルのダウンロードやインポートは不要です。

@Entity モデルを作成する

次のリスト(src/main/java/com/example/accessingdatamysql/User.java)に示すように、エンティティモデルを作成する必要があります。

package com.example.accessingdatamysql;

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

@Entity // This tells Hibernate to make a table out of this class
public class User {
  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Integer id;

  private String name;

  private String email;

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }
}

Hibernate は、エンティティを自動的にテーブルに変換します。

リポジトリを作成する

次のリスト(src/main/java/com/example/accessingdatamysql/UserRepository.java)に示すように、ユーザーレコードを保持するリポジトリを作成する必要があります。

package com.example.accessingdatamysql;

import org.springframework.data.repository.CrudRepository;

import com.example.accessingdatamysql.User;

// This will be AUTO IMPLEMENTED by Spring into a Bean called userRepository
// CRUD refers Create, Read, Update, Delete

public interface UserRepository extends CrudRepository<User, Integer> {

}

Spring は、同じリポジトリの Bean にこのリポジトリインターフェースを自動的に実装します(ケースに変更があります。userRepository と呼ばれます)。

コントローラーの作成

次のリスト(src/main/java/com/example/accessingdatamysql/MainController.java)に示すように、アプリケーションへの HTTP リクエストを処理するコントローラーを作成する必要があります。

package com.example.accessingdatamysql;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller // This means that this class is a Controller
@RequestMapping(path="/demo") // This means URL's start with /demo (after Application path)
public class MainController {
  @Autowired // This means to get the bean called userRepository
         // Which is auto-generated by Spring, we will use it to handle the data
  private UserRepository userRepository;

  @PostMapping(path="/add") // Map ONLY POST Requests
  public @ResponseBody String addNewUser (@RequestParam String name
      , @RequestParam String email) {
    // @ResponseBody means the returned String is the response, not a view name
    // @RequestParam means it is a parameter from the GET or POST request

    User n = new User();
    n.setName(name);
    n.setEmail(email);
    userRepository.save(n);
    return "Saved";
  }

  @GetMapping(path="/all")
  public @ResponseBody Iterable<User> getAllUsers() {
    // This returns a JSON or XML with the users
    return userRepository.findAll();
  }
}
上記の例では、2 つのエンドポイントに POST と GET を明示的に指定しています。デフォルトでは、@RequestMapping はすべての HTTP 操作をマップします。

アプリケーションクラスを作成する

Spring Initializr は、アプリケーションの単純なクラスを作成します。次のリストは、Initializr がこの例(src/main/java/com/example/accessingdatamysql/AccessingDataMysqlApplication.java 内)で作成したクラスを示しています。

package com.example.accessingdatamysql;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AccessingDataMysqlApplication {

  public static void main(String[] args) {
    SpringApplication.run(AccessingDataMysqlApplication.class, args);
  }

}

この例では、AccessingDataMysqlApplication クラスを変更する必要はありません。

Spring Initializr は、メインクラスに @SpringBootApplication アノテーションを追加します。@SpringBootApplication は、次のすべてを追加する便利なアノテーションです。

  • @Configuration: アプリケーションコンテキストの Bean 定義のソースとしてクラスにタグを付けます。

  • @EnableAutoConfiguration: Spring Boot は、追加した依存関係に基づいて、Spring アプリケーションを自動的に構成しようとします。

  • @ComponentScan: Spring に他のコンポーネント、構成、サービスを検索するように指示します。特定のパッケージが定義されていない場合は、アノテーションを宣言するクラスのパッケージから再帰スキャンが開始されます。

アプリケーションの実行

この時点で、アプリケーションを実行して、コードの動作を確認できます。メインメソッドは、IDE またはコマンドラインから実行できます。ソリューションリポジトリからプロジェクトをクローンした場合、IDE が compose.yaml ファイルを間違った場所で探す可能性があることに注意してください。IDE を構成して正しい場所を探すか、コマンドラインを使用してアプリケーションを実行できます。./gradlew bootRun コマンドと ./mvnw spring-boot:run コマンドは、アプリケーションを起動し、compose.yaml ファイルを自動的に見つけます。

アプリケーションをテストする

アプリケーションが実行されたため、curl または同様のツールを使用してテストできます。テストできる 2 つの HTTP エンドポイントがあります。

GET localhost:8080/demo/all: すべてのデータを取得します。POST localhost:8080/demo/add: 1 人のユーザーをデータに追加します。

次の curl コマンドは、ユーザーを追加します。

$ curl http://localhost:8080/demo/add -d name=First -d [email protected] (英語)  

返信は次のようになります。

Saved

次のコマンドは、すべてのユーザーを表示します。

$ curl http://localhost:8080/demo/all

返信は次のようになります。

[{"id":1,"name":"First","email":"[email protected] (英語)  "}]

アプリケーション構築の準備

アプリケーションをパッケージ化して実行するには、Spring Boot Docker Compose サポートを使用するのではなく、外部の MySQL データベースを提供する必要があります。このタスクでは、提供されている compose.yaml ファイルをいくつかの変更を加えて再利用できます。まず、compose.yaml の ports エントリを 3306:3306 に変更します。次に、guide-mysql の container_name を追加します。

これらの手順を実行すると、compose.yaml ファイルは次のようになります。

services:
  mysql:
    container_name: 'guide-mysql'
    image: 'mysql:latest'
    environment:
      - 'MYSQL_DATABASE=mydatabase'
      - 'MYSQL_PASSWORD=secret'
      - 'MYSQL_ROOT_PASSWORD=verysecret'
      - 'MYSQL_USER=myuser'
    ports:
      - '3306:3306'

これで、docker compose up を実行してこの MySQL コンテナーを起動できます。

3 番目に、アプリケーションにデータベースへの接続方法を伝える必要があります。この手順は、以前は Spring Boot Docker Compose サポートによって自動的に処理されていました。これを行うには、application.properties ファイルを次のように変更します。

spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=myuser
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.show-sql: true

アプリケーションの構築

このセクションでは、このガイドを実行するさまざまな方法について説明します。

アプリケーションの実行方法に関係なく、出力は同じになります。

アプリケーションを実行するには、アプリケーションを実行可能ファイル jar としてパッケージ化できます。./gradlew clean build コマンドは、アプリケーションを実行可能ファイル jar にコンパイルします。その後、java -jar build/libs/accessing-data-mysql-0.0.1-SNAPSHOT.jar コマンドを使用して jar を実行できます。

あるいは、Docker 環境が利用可能な場合は、buildpacks を使用して、Maven または Gradle プラグインから直接 Docker イメージを作成できます。Cloud Native Buildpacks を使用すると、どこでも実行できる Docker 互換イメージを作成できます。Spring Boot には、Maven と Gradle の両方に対する buildpack のサポートが直接含まれています。つまり、コマンドを 1 つ入力するだけで、ローカルで実行されている Docker デーモンに適切なイメージをすばやく取り込むことができます。Cloud Native Buildpacks を使用して Docker イメージを作成するには、./gradlew bootBuildImage コマンドを実行します。Docker 環境が有効になっている場合は、docker run --network container:guide-mysql docker.io/library/accessing-data-mysql:0.0.1-SNAPSHOT コマンドを使用してアプリケーションを実行できます。

--network フラグは、外部コンテナーが使用している既存のネットワークにガイドコンテナーを接続するように Docker に指示します。詳細については、Docker ドキュメント (英語) を参照してください。

ネイティブイメージのサポート

Spring Boot は、マシンに GraalVM ディストリビューションがインストールされている場合、ネイティブイメージへのコンパイルもサポートします。ネイティブビルドツールを使用して Gradle でネイティブイメージを作成するには、まず、Gradle ビルドに org.graalvm.buildtools.native を含む plugins ブロックが含まれていることを確認します。

plugins {
	id 'org.graalvm.buildtools.native' version '0.9.28'
...

次に、./gradlew nativeCompile コマンドを実行してネイティブイメージを生成します。ビルドが完了したら、build/native/nativeCompile/accessing-data-mysql コマンドを実行することで、ほぼ瞬時に起動してコードを実行できるようになります。

Buildpacks を使用したネイティブイメージを作成することもできます。./gradlew bootBuildImage コマンドを実行してネイティブイメージを生成できます。ビルドが完了したら、docker run --network container:guide-mysql docker.io/library/accessing-data-mysql:0.0.1-SNAPSHOT コマンドを使用してアプリケーションを起動できます。

Docker でアプリケーションをテストする

上記の Docker 命令を使用してアプリケーションを実行した場合、ターミナルまたはコマンドラインからの単純な curl コマンドは機能しなくなります。これは、ターミナルまたはコマンドラインからアクセスできない Docker ネットワーク (英語) でコンテナーを実行しているためです。curl コマンドを実行するには、3 番目のコンテナーを起動して curl コマンドを実行し、同じネットワークに接続します。

まず、MySQL データベースおよびアプリケーションと同じネットワーク上で実行されている新しいコンテナーへの対話型シェルを取得します。

docker run --rm --network container:guide-mysql -it alpine

次に、コンテナー内のシェルから curl をインストールします。

apk add curl

最後に、アプリケーションをテストするに従って、curl コマンドを実行できます。

セキュリティを変更する

本番環境では、SQL インジェクション攻撃にさらされる可能性があります。ハッカーは DROP TABLE またはその他の破棄的な SQL コマンドを挿入する可能性があります。セキュリティ対策として、アプリケーションをユーザーに公開する前に、データベースにいくつかの変更を加える必要があります。

次のコマンドは、Spring アプリケーションに関連付けられているユーザーからすべての特権を取り消します。

mysql> revoke all on db_example.* from 'myuser'@'%';

これで、Spring アプリケーションはデータベースで何もできなくなりました。

アプリケーションには何らかの特権が必要であるため、次のコマンドを使用して、アプリケーションに必要な最小限の特権を付与します。

mysql> grant select, insert, delete, update on db_example.* to 'myuser'@'%';

すべての特権を削除して一部の特権を付与すると、Spring アプリケーションに、構造(スキーマ)ではなくデータベースのデータのみを変更するために必要な特権が付与されます。

データベースに変更を加える場合:

  1. 許可を再付与します。

  2. spring.jpa.hibernate.ddl-auto を update に変更します。

  3. アプリケーションを再実行します。

次に、ここに示す 2 つのコマンドを繰り返して、アプリケーションを再び本番環境で安全に使用できるようにします。さらに良い方法は、Flyway や Liquibase などの専用の移行ツールを使用することです。

要約

おめでとう! MySQL データベースにバインドされた Spring アプリケーションを開発し、本番の準備ができました!

関連事項

次のガイドも役立ちます。

新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください [GitHub] (英語)

すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の Attribution、NoDerivatives creative commons ライセンス (英語) でリリースされています。

コードを入手する