Accessing Relational Data using JDBC with Spring

f:id:Naotsugu:20150110034558p:plain

Getting Started · Accessing Relational Data using JDBC with Spring

このガイドでは Spring でリレーショナルデータへアクセスする手順を見ていきます。

What you’ll build

Spring の JdbcTemplate でリレーショナルデータベースに保存したデータにアクセスするアプリケーションを作成します。

What you’ll need

How to complete this guide

以下から雛形をダウンロードできます。

git clone https://github.com/spring-guides/gs-relational-data-access.git

が、ここではスクラッチで Gradle 使って進めることにします。

Build with Gradle

プロジェクト用のディレクトリを用意して、その中にソースディレクトリ作成します。

mkdir -p src/main/java/hello

build.gradle 作成していきます。

touch build.gradle

内容は以下。

buildscript {
    repositories {
        maven { url "https://repo.spring.io/libs-release" }
        mavenLocal()
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.10.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'

jar {
    baseName = 'gs-relational-data-access'
    version =  '0.1.0'
}

repositories {
    mavenLocal()
    mavenCentral()
    maven { url "https://repo.spring.io/libs-release" }
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter")
    compile("org.springframework:spring-jdbc")
    compile("com.h2database:h2")
    testCompile("junit:junit")
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.11'
}

spring-jdbc と h2 の依存を追加しています。

Create a Customer object

簡単なデータアクセスを動かすので、customer の姓と名を管理することにします。 Customer クラスを作成します。

touch src/main/java/hello/Customer.java

中身は以下。

package hello;

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
}

Store and retrieve data

Spring は JdbcTemplate というテンプレートクラスを提供しています。これはJDBC 経由でSQLでリレーショナルデータベースを扱うのに便利なクラスです。 JDBC を扱うほとんどのコードは、リソースの取得やコネクションの管理、例外ハンドリング、一般的なエラーチェックなどで、本質的でないコードを扱わなければなりません。 JdbcTemplate はこれらの処理の面倒を見てくれるため、あなたのやるべきタスクに焦点をあてることができます。

Application クラスを作成します。

touch src/main/java/hello/Application.java

中身は以下。

package hello;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

public class Application {

    public static void main(String args[]) {
        // simple DS for test (not for production!)
        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
        dataSource.setDriverClass(org.h2.Driver.class);
        dataSource.setUsername("sa");
        dataSource.setUrl("jdbc:h2:mem");
        dataSource.setPassword("");

        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        System.out.println("Creating tables");
        jdbcTemplate.execute("drop table customers if exists");
        jdbcTemplate.execute("create table customers(" +
                "id serial, first_name varchar(255), last_name varchar(255))");

        String[] names = "John Woo;Jeff Dean;Josh Bloch;Josh Long".split(";");
        for (String fullname : names) {
            String[] name = fullname.split(" ");
            System.out.printf("Inserting customer record for %s %s\n", name[0], name[1]);
            jdbcTemplate.update(
                    "INSERT INTO customers(first_name,last_name) values(?,?)",
                    name[0], name[1]);
        }

        System.out.println("Querying for customer records where first_name = 'Josh':");
        List<Customer> results = jdbcTemplate.query(
                "select * from customers where first_name = ?", new Object[] { "Josh" },
                new RowMapper<Customer>() {
                    @Override
                    public Customer mapRow(ResultSet rs, int rowNum) throws SQLException {
                        return new Customer(rs.getLong("id"), rs.getString("first_name"),
                                rs.getString("last_name"));
                    }
                });

        for (Customer customer : results) {
            System.out.println(customer);
        }
    }
}

この例では Spring の SimpleDriverDataSourceを使ってJDBCDataSource を設定しています。そして JdbcTemplateインスタンス化にこの DataSource を使っています。

SimpleDriverDataSource は便利なクラスですが、商用環境向けではありません。

JdbcTemplate を設定すれば、後はデータベースとの接続は簡単です。

最初に、JdbcTemplate の execute メソッドでいくつかの DDL を発行します。

そして新しく作成されたテーブルに、いくつかのレコードを登録した後、JdbcTemplate の update メソッドを使います。 第一引数にはクエリ文字、それに続く引数には "?" で定義された埋め込み変数の値を指定します。

? の引数を指定することで、JDBC での変数埋め込みの際の SQL インジェクション攻撃を防ぐことができます。

最後に query メソッドでテーブルから条件に該当するレコードを検索します。ここでもクエリのパラメータとして再び "?" を使い、実際にクエリに渡したい値を与えてやります。query メソッドの最後の引数は RowMapper<T>インスタンスです。Spring ほとんどの仕事を受け持ってくれますが、結果セットのデータをどのように扱うかは把握できません。Spring はレコードの結果毎に RowMapper<T>インスタンスに結果を渡し、その結果を集約してコレクションとして返却します。

Build an executable JAR

Gradle を使っているなら、アプリケーションの実行は ./gradlew bootRun とするだけです。

実行可能 jar を作成するには以下のようにし(初回のみgradle wrapperが必要)、

./gradlew build

jar を実行すればアプリケーションが起動します。

java -jar build/libs/gs-accessing-data-jpa-0.1.0.jar

実行すると以下のような出力が得られるでしょう。

Creating tables
Inserting customer record for John Woo
Inserting customer record for Jeff Dean
Inserting customer record for Josh Bloch
Inserting customer record for Josh Long
Querying for customer records where first_name = 'Josh':
Customer[id=3, firstName='Josh', lastName='Bloch']
Customer[id=4, firstName='Josh', lastName='Long']