はじめに
今回も引き続きSpringチュートリアルの内容をまとめていきたいと思います
開発環境やIDEAについては下記記事参照
Accessing Relational Data using JDBC with Spring(Spring JDBC JdbcTemplate で SQL 発行
)
このガイドでは、Spring を使用してリレーショナルデータにアクセスするプロセスを順を追って説明します。
Spring の JdbcTemplate を使用して、リレーショナルデータベースに保存されているデータにアクセスするアプリケーションを構築します。
日本語サイト
英語サイト
Create a Customer Object(Customer オブジェクトを作成する
)
使用する単純なデータアクセスロジックは、顧客の姓と名を管理
このデータをアプリケーションレベルで表すには、次のリスト(src/main/java/com/example/relationaldataaccess/Customer.java から)が示すように、Customerクラスを作成する必要がある
package com.example.relationaldataaccess;
// Customer クラスの定義
public class Customer {
// 顧客IDを表すフィールド(データの保存場所)
private long id;
// 顧客の名前(名)を表すフィールド
private String firstName;
// 顧客の名前(姓)を表すフィールド
private String lastName;
/**
* コンストラクタ
* 新しい Customer オブジェクトを作成するときに、
* id, firstName, lastName の3つの値を指定して初期化する
*/
public Customer(long id, String firstName, String lastName) {
this.id = id; // フィールド id に引数 id を代入
this.firstName = firstName; // フィールド firstName に引数 firstName を代入
this.lastName = lastName; // フィールド lastName に引数 lastName を代入
}
/**
* オーバーライドされた toString() メソッド
* オブジェクトの内容を文字列としてわかりやすく表現するためのメソッド
* 例えばデバッグ時にこのメソッドが呼ばれて、顧客情報を見やすく表示できる
*/
@Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
// 「getters & setters omitted for brevity」
// ここには通常、フィールドの値を取得・設定するメソッドが入りますが、
// コードの簡潔さのために省略されています。
// 例えば、getId(), setId(), getFirstName(), setFirstName() などがあります。
}
- フィールド: 顧客のIDや名前を保持する変数
- コンストラクタ: オブジェクト生成時にフィールドを初期化する特別なメソッド
-
toString()メソッド: オブジェクトの内容を文字列化してわかりやすく表示。主にログやデバッグで役立つ -
getter/setterメソッド: フィールドに安全にアクセス・変更するためのメソッド(コード上では省略)
★オーバーライドについて
- 親クラス(スーパークラス)が持っているメソッドを、自分(子クラス)で再定義して上書きすること
- Javaでは、すべてのクラスは暗黙的に Object クラスを継承している
- Objectクラスには便利なメソッドがいくつかあり、その中に
toString()(オブジェクトを文字列に変換して返すメソッド)がある - オブジェクトの情報をわかりやすく文字列で表示したいときに使う
オーバーライドありと無し違い
// オーバーライドなし
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person("Taro");
System.out.println(p);
}
}
// ↓出力例
Person@6d06d69c
// オーバーライドあり
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
// ここでtoStringメソッドをオーバーライドする
@Override
public String toString() {
return "Personの名前は " + name + " です";
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person("Taro");
System.out.println(p);
}
}
// ↓出力結果
Personの名前は Taro です
Store and Retrieve Data(データの保存と取得)
package com.example.relational_data_access;
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
// アプリ起動後に自動でこのクラスの run() メソッドが呼ばれる
public class RelationalDataAccessApplication implements CommandLineRunner {
// ログ出力のためのロガーを準備
private static final Logger log = LoggerFactory.getLogger(RelationalDataAccessApplication.class);
public static void main(String args[]) {
// Spring Boot アプリケーションを起動
SpringApplication.run(RelationalDataAccessApplication.class, args);
}
// Spring により自動的に JdbcTemplate が注入される(DB接続&SQL実行ツール)
// @Autowired によって Spring がこのツールを自動で準備してくれる(DI:依存性注入)
@Autowired
JdbcTemplate jdbcTemplate;
@Override
public void run(String... strings) throws Exception {
// ログに「テーブル作成開始」を表示
log.info("Creating tables");
// 既存の customers テーブルがあれば削除(毎回リセットする)
jdbcTemplate.execute("DROP TABLE customers IF EXISTS");
// customers テーブルを新たに作成(id, 名前, 苗字)
jdbcTemplate.execute("CREATE TABLE customers(" +
"id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");
// フルネームのリスト(例: "John Woo")を、["John", "Woo"] に分解
List<Object[]> splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long").stream()
.map(name -> name.split(" ")) // 空白で名前を分割
.collect(Collectors.toList()); // 分割したものをリストとして集める
// 名前ごとにログ出力(確認のため)
splitUpNames.forEach(name ->
log.info(String.format("Inserting customer record for %s %s", name[0], name[1]))
);
// 上記の名前データを「一括」でデータベースに登録(バルクインサート)
jdbcTemplate.batchUpdate(
"INSERT INTO customers(first_name, last_name) VALUES (?, ?)",
splitUpNames
);
// 「first_name が Josh の人」を検索してログ出力
log.info("Querying for customer records where first_name = 'Josh':");
jdbcTemplate.query(
"SELECT id, first_name, last_name FROM customers WHERE first_name = ?",
// 取得したデータを Customer オブジェクトに変換(ラムダ式)
(rs, rowNum) -> new Customer(
rs.getLong("id"),
rs.getString("first_name"),
rs.getString("last_name")
),
"Josh" // 検索条件の値(first_name = Josh)
).forEach(customer -> log.info(customer.toString())); // 検索結果をログ出力
}
}
★JDBCについて
- Javaのプログラムとデータベースの間に入って仲立ちをしてくれる部品
★一括で登録(バルクインサート)について
通常インサート
jdbcTemplate.update("INSERT INTO customers(first_name, last_name) VALUES (?, ?)", "John", "Woo");
jdbcTemplate.update("INSERT INTO customers(first_name, last_name) VALUES (?, ?)", "Jeff", "Dean");
- 1回のSQL実行で1件のレコードを挿入する方法
- → たとえば 100件登録するなら、100回 update() を呼ぶことになる
バルクインサート
jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames);
- Spring の
JdbcTemplateの機能で、1つのSQLで複数データをまとめて処理できる - ※
batchUpdate()= バルクインサートの実装 -
batch= 一括で処理する -
insert= 挿入する - バルクインサート(bulk insert) という言い方は技術用語で、大量データを一気に登録する手法のこと