LoginSignup
14
13

More than 5 years have passed since last update.

spring bootの組み込みh2dbにjdbcTemplateでアクセスする

Posted at

1. はじめに

今回はspring-jdbcのjdbcTemplateを利用して、spring bootの組み込みh2dbにDBアクセスする方法について説明したいと思います。といってもh2dbに関する話はほとんどなく、単純にjdbcTemplateの使い方になります。
組み込みh2dbの準備については「spring bootで組み込みh2dbを利用する方法」を参照ねがいます。

jdbcTemplateではなく他のORMapperでDBアクセスをする場合は以下の記事を参照ねがいます。

2. JdbcTemplateによるDBアクセスのポイント

JdbcTemplateによるDBアクセスをする際のポイントについて以下に示します。
基本的なCRUDであれば、ここで示す内容で十分かと思います。

  • パラメータ部分を?で定義するJdbcTemplate:パラメータ名で定義するNamedParameterJdbcTemplateがある
    • 可読性が高いNamedParameterJdbcTemplateの利用を推奨
  • DBアクセスするクラスはリポジトリと呼び、@Repositoryクラスアノテーションを付与する
  • NamedParameterJdbcTemplate@Autowired等でリポジトリクラスにインジェクションする
  • SQL文は文字列型として定数や変数として定義する
    • @Valueで外出しするのも可能
    • パラメータ部分は:パラメータ名で定義する
  • パラメータが多い場合はBeanPropertySqlParameterSourceのコンストラクタを利用して定義するのが便利
  • パラメータが少ない場合はMapSqlParameterSourceのビルダーパターンを利用して定義するのが便利
  • INSERTDELETEUPDATEのSQLはjdbcTemplateupdateメソッドを利用する
    • 戻り値のintは更新結果の行数である
  • SELECTのSQL文の場合、結果を特定のクラスにマッピングするためRowMapperを利用する
  • 1行を取得するSELECTのSQLはjdbcTemplatequeryForObjectメソッドを利用する
  • 複数行を取得するSELECTのSQLはjdbcTemplatequeryメソッドを利用する

3. サンプル

3.1. テーブルの定義

spring bootではクラスパス直下のschema.sqlファイルを起動時に自動で実行してくれます。
このファイルに今回利用するテーブルのCREATE TABLE文を記述します。
h2dbのデータ型については以下の公式ガイドラインを参照ください。
http://www.h2database.com/html/datatypes.html

src/main/resources/schema.sql
-- file_info table
CREATE TABLE IF NOT EXISTS file_info (
    file_id varchar(100) PRIMARY KEY,
    file_type varchar(100) NOT NULL,
    file_name varchar(200) NOT NULL,
    file_path varchar(200) NOT NULL,
    content_type varchar(100) NOT NULL,
    content_length bigint NOT NULL,
    registered_date timestamp NOT NULL
);

3.2. マッピング対象のクラス

テーブルに対応するマッピング対象のクラスを定義します。
といっても普通のPOJOのクラスです。

FileInfo.java
package com.example.demo.domain.model;

import java.io.Serializable;
import java.sql.Timestamp;

public class FileInfo implements Serializable {

    private static final long serialVersionUID = 1L;

    private String contentType;
    private long contentLength;
    private String fileName;
    private String fileType;
    private String fileId;
    private String filePath;
    private Timestamp registeredDate;

    // constructor,setter,getter omitted
}

3.3. リポジトリのクラス

今回の記事のメインとなるリポジトリのクラスを定義します。
ポイントは前述した「2. JdbcTemplateによるDBアクセスのポイント」の通りです。

実装上の注意としては検索結果が0件の場合、org.springframework.dao.EmptyResultDataAccessExceptionが発生するということです。今回は他のORMapperと同じく、0件の場合はnullを返すようにしました。

なおDataAccessExceptionDemoSystemExceptionにラップしているのは当方の都合です。jdbcTemplateの使い方とは直接的には関係ありません。

FileInfoRepository.java
package com.example.demo.domain.repository;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;

import com.example.demo.domain.common.DemoSystemException;
import com.example.demo.domain.model.FileInfo;

@Repository
public class FileInfoRepository {

    private static final Logger LOGGER = LoggerFactory.getLogger(FileInfoRepository.class);

    private static final String INSERT_SQL = "INSERT INTO file_info ("
            + "file_id, file_type, file_name, file_path, content_type, content_length, registered_date) values ("
            + ":fileId, :fileType, :fileName, :filePath, :contentType, :contentLength, :registeredDate)";

    private static final String DELETE_BY_KEY_SQL = "DELETE FROM file_info WHERE file_id = :fileId";

    private static final String UPDATE_BY_KEY_SQL = "UPDATE file_info SET file_type = :fileType, file_name = :fileName, "
            + "file_path = :filePath, content_type = :contentType, content_length = :contentLength, registered_date = :registeredDate "
            + "WHERE file_id = :fileId";

    private static final String FIND_ONE_SQL = "SELECT file_id, file_type, file_name, file_path, content_type, content_length, registered_date "
            + "FROM file_info WHERE file_id = :fileId";

    private static final String FIND_ALL_SQL = "SELECT file_id, file_type, file_name, file_path, content_type, content_length, registered_date "
            + "FROM file_info ORDER BY file_type, registered_date";

    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;

    public int insert(FileInfo fileInfo) {
        try {
            SqlParameterSource param = new BeanPropertySqlParameterSource(fileInfo);
            return jdbcTemplate.update(INSERT_SQL, param);
        } catch(DataAccessException e) {
            LOGGER.error("DataAccessError : INSERT_SQL param:{}, error:{}", fileInfo, e);
            throw new DemoSystemException("DataAccessError : FileInfoRepository INSERT_SQL", e);
        }
    }

    public int deleteByKey(String fileId) {
        try {
            SqlParameterSource param = new MapSqlParameterSource().addValue("fileId", fileId);
            return jdbcTemplate.update(DELETE_BY_KEY_SQL, param);
        } catch(DataAccessException e) {
            LOGGER.error("DataAccessError : DELETE_BY_KEY_SQL param:{}, error:{}", fileId, e);
            throw new DemoSystemException("DataAccessError : FileInfoRepository DELETE_BY_KEY_SQL", e);
        }
    }

    public int updateByKey(FileInfo fileInfo) {
        try {
            SqlParameterSource param = new BeanPropertySqlParameterSource(fileInfo);
            return jdbcTemplate.update(UPDATE_BY_KEY_SQL, param);
        } catch(DataAccessException e) {
            LOGGER.error("DataAccessError : UPDATE_BY_KEY_SQL param:{}, error:{}", fileInfo, e);
            throw new DemoSystemException("DataAccessError : FileInfoRepository UPDATE_BY_KEY_SQL", e);
        }
    }

    public FileInfo fineOne(String fileId) {
        try {
            SqlParameterSource param = new MapSqlParameterSource().addValue("fileId", fileId);
            return jdbcTemplate.queryForObject(FIND_ONE_SQL, param, fileInfoRowMapper());
        } catch(EmptyResultDataAccessException e) {
            return null;
        } catch(DataAccessException e) {
            LOGGER.error("DataAccessError : FIND_ONE_SQL param:{}, error:{}", fileId, e);
            throw new DemoSystemException("DataAccessError : FileInfoRepository FIND_ONE_SQL", e);
        }
    }

    public List<FileInfo> findAll() {
        try {
            return jdbcTemplate.query(FIND_ALL_SQL, fileInfoRowMapper());
        } catch(EmptyResultDataAccessException e) {
            return null;
        } catch(DataAccessException e) {
            LOGGER.error("DataAccessError : FIND_ALL_SQL error:{}", e);
            throw new DemoSystemException("DataAccessError : FileInfoRepository FIND_ALL_SQL", e);
        }
    }

    private RowMapper<FileInfo> fileInfoRowMapper() {

        return new RowMapper<FileInfo>() {
            @Override
            public FileInfo mapRow(ResultSet rs, int rowNum) throws SQLException {

                FileInfo fileInfo = new FileInfo();
                fileInfo.setContentLength(rs.getLong("content_length"));
                fileInfo.setContentType(rs.getString("content_type"));
                fileInfo.setFileId(rs.getString("file_id"));
                fileInfo.setFileName(rs.getString("file_name"));
                fileInfo.setFilePath(rs.getString("file_path"));
                fileInfo.setFileType(rs.getString("file_type"));
                fileInfo.setRegisteredDate(rs.getTimestamp("registered_date"));
                return fileInfo;
            }
        };
    }
}

4. さいごに

今回はspring-jdbcのjdbcTemplateを利用して、spring bootの組み込みh2dbにDBアクセスする方法について説明しました。
簡単なDBアクセスであればORMapperを導入しなくてもjdbcTemplateで十分かと思います。

14
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
13