このガイドでは、Spring を使用してリレーショナルデータにアクセスするプロセスを順を追って説明します。

構築するもの

Spring の JdbcTemplate を使用して、リレーショナルデータベースに保存されているデータにアクセスするアプリケーションを構築します。

必要なもの

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

ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。

最初から始めるには、Spring Initializr から開始に進みます。

基本スキップするには、次の手順を実行します。

完了したときは、gs-relational-data-access/complete のコードに対して結果を確認できます。

Spring Initializr から開始

すべての Spring アプリケーションの場合、Spring Initializr (英語) から開始する必要があります。Initializr は、アプリケーションに必要なすべての依存関係をすばやく取得する方法を提供し、多くのセットアップを行います。この例では、JDBC API と H2 Database の依存関係が必要です。次のイメージは、このサンプルプロジェクト用に設定された Initializr を示しています。

initializr
前の図は、Maven がビルドツールとして選択された Initializr を示しています。Gradle も使用できます。また、com.example および relational-data-access の値をそれぞれグループおよびアーティファクトとして表示します。このサンプルの残りの部分では、これらの値を使用します。

次のリストは、Maven を選択したときに作成される pom.xml ファイルを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>relational-data-access</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>relational-data-access</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jdbc</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

次のリストは、Gradle を選択したときに作成される build.gradle ファイルを示しています。

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

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

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
	runtimeOnly 'com.h2database:h2'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

Customer オブジェクトを作成する

使用する単純なデータアクセスロジックは、顧客の姓と名を管理します。このデータをアプリケーションレベルで表すには、次のリスト(src/main/java/com/example/relationaldataaccess/Customer.java から)が示すように、Customer クラスを作成します。

package com.example.relationaldataaccess;

public class Customer {
  private long id;
  private String firstName, lastName;

  public Customer(long id, String firstName, String lastName) {
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
  }

  @Override
  public String toString() {
    return String.format(
        "Customer[id=%d, firstName='%s', lastName='%s']",
        id, firstName, lastName);
  }

  // getters & setters omitted for brevity
}

データの保存と取得

Spring は、JdbcTemplate と呼ばれるテンプレートクラスを提供し、SQL リレーショナルデータベースと JDBC を簡単に操作できるようにします。ほとんどの JDBC コードは、リソースの取得、接続管理、例外処理、およびコードの目的とはまったく関係のない一般的なエラーチェックに没頭しています。JdbcTemplate はあなたのためにそれらすべてを処理します。しなければならないのは、手元のタスクに集中することです。次のリスト(src/main/java/com/example/relationaldataaccess/RelationalDataAccessApplication.java から)は、JDBC を介してデータを保管および取得できるクラスを示しています。

package com.example.relationaldataaccess;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@SpringBootApplication
public class RelationalDataAccessApplication implements CommandLineRunner {

  private static final Logger log = LoggerFactory.getLogger(RelationalDataAccessApplication.class);

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

  @Autowired
  JdbcTemplate jdbcTemplate;

  @Override
  public void run(String... strings) throws Exception {

    log.info("Creating tables");

    jdbcTemplate.execute("DROP TABLE customers IF EXISTS");
    jdbcTemplate.execute("CREATE TABLE customers(" +
        "id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");

    // Split up the array of whole names into an array of first/last names
    List<Object[]> splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long").stream()
        .map(name -> name.split(" "))
        .collect(Collectors.toList());

    // Use a Java 8 stream to print out each tuple of the list
    splitUpNames.forEach(name -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1])));

    // Uses JdbcTemplate's batchUpdate operation to bulk load data
    jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames);

    log.info("Querying for customer records where first_name = 'Josh':");
    jdbcTemplate.query(
        "SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[] { "Josh" },
        (rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name"))
    ).forEach(customer -> log.info(customer.toString()));
  }
}

@SpringBootApplication は、次のすべてを追加する便利なアノテーションです。

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

  • @EnableAutoConfiguration: クラスパス設定、他の Bean、およびさまざまなプロパティ設定に基づいて、Bean の追加を開始するよう Spring Boot に指示します。

  • @ComponentScancom.example.relationaldataaccess パッケージの他のコンポーネント、構成、およびサービスを探すように Spring に指示します。この場合、何もありません。

main() メソッドは、Spring Boot の SpringApplication.run() メソッドを使用してアプリケーションを起動します。

Spring Boot は H2(インメモリリレーショナルデータベースエンジン)をサポートし、自動的に接続を作成します。spring-jdbc を使用しているため、Spring Boot は自動的に JdbcTemplate を作成します。@Autowired JdbcTemplate フィールドは自動的にロードし、使用可能にします。

この Application クラスは Spring Boot の CommandLineRunner を実装します。これは、アプリケーションコンテキストがロードされた後に run() メソッドを実行することを意味します。

最初に、JdbcTemplate の execute メソッドを使用して DDL をインストールします。

2 番目に、文字列のリストを取得し、Java 8 ストリームを使用して、Java 配列の名 / 姓のペアに分割します。

次に、JdbcTemplate の batchUpdate メソッドを使用して、新しく作成したテーブルにいくつかのレコードをインストールします。メソッド呼び出しの最初の引数はクエリ文字列です。最後の引数(Object インスタンスの配列)は、? 文字があるクエリに代入される変数を保持します。

単一挿入ステートメントの場合、JdbcTemplate の insert メソッドが適切です。ただし、複数の挿入の場合、batchUpdate を使用することをお勧めします。
引数に ? を使用して、変数をバインドするよう JDBC に指示することにより、SQL インジェクション攻撃 (英語) を回避します。

最後に、query メソッドを使用して、条件に一致するレコードをテーブルで検索します。再び ? 引数を使用してクエリのパラメーターを作成し、呼び出しを行うときに実際の値を渡します。最後の引数は、各結果行を新しい Customer オブジェクトに変換するために使用される Java 8 ラムダです。

Java 8 ラムダは、Spring の RowMapper などの単一メソッドインターフェースにうまくマッピングされます。Java 7 以前を使用している場合、匿名インターフェース実装をプラグインして、メソッド本体をラムダ式の本体と同じにすることができます。

実行可能 JAR を構築する

コマンドラインから Gradle または Maven を使用してアプリケーションを実行できます。必要なすべての依存関係、クラス、およびリソースを含む単一の実行可能 JAR ファイルを構築して実行することもできます。実行可能な jar を構築すると、開発ライフサイクル全体、さまざまな環境などで、サービスをアプリケーションとして簡単に提供、バージョン管理、およびデプロイできます。

Gradle を使用する場合、./gradlew bootRun を使用してアプリケーションを実行できます。または、次のように、./gradlew build を使用して JAR ファイルをビルドしてから、JAR ファイルを実行できます。

java -jar build/libs/gs-relational-data-access-0.1.0.jar

Maven を使用する場合、./mvnw spring-boot:run を使用してアプリケーションを実行できます。または、次のように、./mvnw clean package で JAR ファイルをビルドしてから、JAR ファイルを実行できます。

java -jar target/gs-relational-data-access-0.1.0.jar
ここで説明する手順は、実行可能な JAR を作成します。クラシック WAR ファイルを作成することもできます。

次の出力が表示されます。

2019-09-26 13:46:58.561  INFO 47569 --- [           main] c.e.r.RelationalDataAccessApplication    : Creating tables
2019-09-26 13:46:58.564  INFO 47569 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2019-09-26 13:46:58.708  INFO 47569 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2019-09-26 13:46:58.809  INFO 47569 --- [           main] c.e.r.RelationalDataAccessApplication    : Inserting customer record for John Woo
2019-09-26 13:46:58.810  INFO 47569 --- [           main] c.e.r.RelationalDataAccessApplication    : Inserting customer record for Jeff Dean
2019-09-26 13:46:58.810  INFO 47569 --- [           main] c.e.r.RelationalDataAccessApplication    : Inserting customer record for Josh Bloch
2019-09-26 13:46:58.810  INFO 47569 --- [           main] c.e.r.RelationalDataAccessApplication    : Inserting customer record for Josh Long
2019-09-26 13:46:58.825  INFO 47569 --- [           main] c.e.r.RelationalDataAccessApplication    : Querying for customer records where first_name = 'Josh':
2019-09-26 13:46:58.835  INFO 47569 --- [           main] c.e.r.RelationalDataAccessApplication    : Customer[id=3, firstName='Josh', lastName='Bloch']
2019-09-26 13:46:58.835  INFO 47569 --- [           main] c.e.r.RelationalDataAccessApplication    : Customer[id=4, firstName='Josh', lastName='Long']

要約

おめでとう! Spring を使用して、単純な JDBC クライアントを開発しました。

Spring Boot には、接続プールを構成およびカスタマイズするための多くの機能があります。たとえば、メモリ内データベースではなく外部データベースに接続するためです。詳細については、参照ガイドを参照してください。

関連事項

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

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

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