技術メモ

技術メモ

ラフなメモ

SpringJDBCを試す

SpringJDBCを試してみました。

事前準備

PostgreSQLについてはdockerで構築しました。docker コマンドが使える環境で以下を実行します。

docker run --name my-db -p 5432:5432 -e POSTGRES_USER=dev -e POSTGRES_PASSWORD=dev -d postgres:9.6

またサンプルデータとして利用するために以下のSQLを実行しておきます。

psqleditなどを利用して、dockerで立ち上げたPostgreSQLにログインできるか確認しておきましょう。

さて、ソース構成は以下のようにしました。Configurationはxmlで定義することにします。

└─src
   └─main
      ├─java
      │  └─sample
      │      │  Main.java
      │      │
      │      ├─dao
      │      │      CustomerDao.java
      │      │      SampleDao.java
      │      │
      │      └─model
      │              Customer.java
      │
      └─resources
          │  datasource.xml
          │  jdbc.properties
          │
          └─spring
                  applicationContext.xml

データアクセスする際のアーキテクチャは以下のようになっています。

f:id:tutuz:20190421201526p:plain

https://terasolunaorg.github.io/guideline/public_review/ArchitectureInDetail/DataAccessCommon.htmlより引用

試す

データソース(DataSource)

データソース(DataSource)はデータベースにアクセスするためのコネクションをアプリケーションに提供する役割を担います。データソースを使用することにより、プログラムの中でJDBCドライバーのロードなどを行う必要はなくなります。さらに、データベースに依存する部分がデータソースに吸収されるため、プログラムの可搬性も向上します。

今回はプロパティファイル(jdbc.properties)に値を保持し、dataSourceのBean定義に関してはdatasource.xmlに定義することにしました。

database.driverClassName=org.postgresql.Driver
database.url=jdbc:postgresql://localhost:5432/dev
database.username=dev
database.password=dev
  • datasource.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${database.driverClassName}" />
        <property name="url" value="${database.url}" />
        <property name="username" value="${database.username}" />
        <property name="password" value="${database.password}" />
        <property name="defaultAutoCommit" value="false" />
    </bean>

</beans>

以下でクラスパス上に存在するjdbc.propertiesを読み込みます。

<context:property-placeholder location="classpath:jdbc.properties"/>

データソースにはApacheのDBCPを利用しています。org.apache.commons.dbcp2.BasicDataSource をBeanとして利用するためにBean定義をしています。また同時に jdbc.properties で定義した各種設定を注入しています。

 <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${database.driverClassName}" />
        <property name="url" value="${database.url}" />
        <property name="username" value="${database.username}" />
        <property name="password" value="${database.password}" />
        <property name="defaultAutoCommit" value="false" />
    </bean>

SpringJDBC

SpringJDBCとは、SQLの内容に関わらず共通的に行われる定型的なJDBCの処理をSpringが代替する機能です。例えば以下があります。

  • コネクションのオープン/クローズ
  • SQLステートメントの実行
  • 処理結果行の繰り返し処理
  • 例外ハンドリング

SpringJDBCを利用することで、実装範囲を以下のような重要な処理に限定することができます。

  • SQLの定義
  • パラメータの設定
  • ResultSetの取得結果に対するロジックの実装

SpringJDBCの JdbcTemplate を利用するために、applicationContext.xmlにBean定義をしておきます。

  • applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <import resource="classpath:datasource.xml"/>

    <context:component-scan base-package="sample.dao" />

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource" />
    </bean>

</beans>

Daoクラスの実装

それぞれのDao @Repository を付与してDIコンテナに登録しておきます。以下は1行を返却するDaoです。SpringJDBCのJdbcTemplateは @Autowired で注入します。

package sample.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class SampleDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public int findCountDatabase() {
        String sql = "select count(1) from pg_database;";
        return jdbcTemplate.queryForObject(sql, Integer.class);
    }

}

このDaoクラスは以下のように利用できます。

    SampleDao sampleDao = context.getBean(SampleDao.class);
    int databaseCount = sampleDao.findCountDatabase();
    System.out.println(databaseCount);
    // 4

複数行の検索結果を扱う場合は以下が実装例です。SpringJDBCのRowMapperを利用することで取得結果のResultSetをPOJOに変換する機構を備えています。CustomerDao.javaではラムダ式でRowMapperのmapRowメソッドをoverrideしています。

package sample.dao;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import sample.model.Customer;

@Repository
public class CustomerDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public List<Customer> getCustomerList() {
        String sql = "select id, firstname, lastname, street, city from customer order by id";
        return jdbcTemplate.query(sql, (rs, rowNum) -> {
            Customer customer = new Customer();
            customer.setId(rs.getInt("id"));
            customer.setFirstName(rs.getString("firstName"));
            customer.setLastName(rs.getString("lastName"));
            customer.setStreet(rs.getString("street"));
            customer.setCity(rs.getString("city"));
            return customer;
        });
    }
}
    CustomerDao customerDao = context.getBean(CustomerDao.class);
    List<Customer> customerList = customerDao.getCustomerList();
    for (Customer customer : customerList) {
        System.out.println(customer);
    }
    // Customer(id=0, firstName=Laura, lastName=Steel, street=429 Seventh Av., city=Dallas)
    // Customer(id=1, firstName=Susanne, lastName=King, street=366 - 20th Ave., city=Olten)
    // Customer(id=2, firstName=Anne, lastName=Miller, street=20 Upland Pl., city=Lyon)
    // Customer(id=3, firstName=Michael, lastName=Clancy, street=542 Upland Pl., city=San Francisco)
    // ...

あとがき

今回試したソースは以下においてあります。

github.com

次にやりたいこと

SpringJDBCのトランザクション関連について動作を確認したいですね。

参考