Spring Frameworkでデータベース操作~JdbcTemplate、Spring Data JPA、MyBatisの3つのパターン
その中でも、代表的なものとして、JdbcTemplateを使用する方法、Spring Data JPAを使用する方法、MyBatisを使う方法が挙げられます。
1. JdbcTemplate、Spring Data JPA、MyBatisの3つのパターン
Spring Frameworkを使ってデータベースを操作する方法には、いくつかの方法があります。
その中でも、代表的なものとして、JdbcTemplateを使用する方法、Spring Data JPAを使用する方法、MyBatisを使う方法が挙げられます。
JdbcTemplateは、Spring Frameworkが提供するJDBCのラッパーであり、SQLクエリを実行するための簡易的なインタフェースを提供します。
Spring Data JPAは、JPA(Java Persistence API)を実装したSpring Frameworkのモジュールであり、オブジェクト指向的なアプローチでデータベースを操作することができます。
MyBatisは、SQLマッピングフレームワークであり、XMLを使ってSQLクエリを定義することができます。
今回の記事では、Spring Frameworkを使ったPostgreSQLデータベース操作について紹介します。
データベースにはあらかじめindex_listテーブルを用意し、主キーにscreen_id、その他のカラムにindex_title、index_exp、index_date、index_memoが存在するものとします。
それでは、それぞれの特徴や使い方を見ていきましょう。
2. JdbcTemplate
JdbcTemplateとは、Spring Frameworkが提供するデータベースアクセスライブラリの1つで、JDBC APIをラッピングして簡単に扱えるようにしたものです。
JDBC APIは、Javaからデータベースにアクセスするための標準的なAPIですが、面倒なコードが必要であり、手間がかかります。
JdbcTemplateを使用することで、JDBC APIに比べて簡潔にコードを書くことができ、かつトランザクション管理や例外処理なども自動的に行うことができます。
また、データベースにアクセスするためのコードが書かれたDAO(Data Access Object)を、JdbcTemplateを使用することで簡単に実装することができます。
JdbcTemplateは、Spring Frameworkの中でもコアな部分であり、多くのプロジェクトで使用されています。
以下が、実装例になります。
2-1. TestJdbcTemplateController.java
package com.learningift.databasetest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.learningift.databasetest.dao.IndexListDao;
/**
*
* JdbcTemplate のデータベース接続テスト
*
*/
@Controller
public class TestJdbcTemplateController {
private final String SCREEN_ID = "Test001";
@Autowired
private IndexListDao indexListDao;
@RequestMapping(value = "/" + SCREEN_ID, method = RequestMethod.GET)
public String getSomething(Model model) {
model.addAttribute("indexListDto", indexListDao.findAll());
model.addAttribute("thisPage", indexListDao.findByScreenId(SCREEN_ID));
return SCREEN_ID;
}
}
2-2. IndexListDao.java
package com.learningift.databasetest.dao;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.learningift.databasetest.domain.test.IndexListDto;
import com.learningift.databasetest.mapper.test.IndexResultSetExtractor;
import com.learningift.databasetest.mapper.test.IndexRowMapper;
@Repository
public class IndexListDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public List findAll() {
final String sql = "SELECT * From index_list ORDER BY screen_id Limit 10";
return jdbcTemplate.query(sql, new IndexResultSetExtractor());
}
public IndexListDto findByScreenId(String screenId) {
String sql = "SELECT * FROM index_list WHERE screen_id='" + screenId + "'";
return jdbcTemplate.queryForObject(sql, new IndexRowMapper());
}
}
2-3. IndexListDto.java
package com.learningift.databasetest.domain.test;
import java.util.Date;
import com.learningift.databasetest.utility.BeanUtil;
/**
* Indexで使用するリストのDto
*/
public class IndexListDto {
// コンストラクタ
public IndexListDto(){
}
// コンストラクタ
public IndexListDto(IndexListDto indexListDto){
BeanUtil.copyProperties(this, indexListDto);
}
// 画面ID
private String screenId;
// タイトル
private String indexTitle;
// 説明
private String indexExp;
// 日付
private Date indexDate;
// メモ
private String indexMemo;
// ゲッター
public String getScreenId() {
return this.screenId;
}
public String getIndexTitle() {
return this.indexTitle;
}
public String getIndexExp() {
return this.indexExp;
}
public Date getIndexDate() {
return this.indexDate;
}
public String getIndexMemo() {
return this.indexMemo;
}
// セッター
public void setScreenId(String screenId) {
this.screenId = screenId;
}
public void setIndexTitle(String indexTitle) {
2-4. IndexResultSetExtractor.java
package com.learningift.databasetest.mapper.test;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import com.learningift.databasetest.domain.test.IndexListDto;
/**
* IndexListDtoのMapperクラス
*
*/
public class IndexResultSetExtractor implements ResultSetExtractor> {
@Override
public List extractData(ResultSet rs) throws SQLException, DataAccessException {
List indexList = new ArrayList<>();
while(rs.next()) {
IndexListDto list = new IndexListDto();
list.setScreenId(rs.getString("screen_id"));
list.setIndexTitle(rs.getString("index_title"));
list.setIndexExp(rs.getString("index_exp"));
list.setIndexDate(rs.getDate("index_date"));
list.setIndexMemo(rs.getString("index_memo"));
indexList.add(list);
}
return indexList;
}
}
2-5. IndexRowMapper.java
package com.learningift.databasetest.mapper.test;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import com.learningift.databasetest.domain.test.IndexListDto;
public class IndexRowMapper implements RowMapper {
@Override
public IndexListDto mapRow(ResultSet rs, int rowNum) throws SQLException {
IndexListDto index = new IndexListDto();
index.setScreenId(rs.getString("screen_id"));
index.setIndexTitle(rs.getString("index_title"));
index.setIndexExp(rs.getString("index_exp"));
index.setIndexDate(rs.getDate("index_date"));
index.setIndexMemo(rs.getString("index_memo"));
return index;
}
}
このコードは、Spring FrameworkのJdbcTemplateを使用してデータベースにアクセスするためのサンプルです。
TestJdbcTemplateControllerクラスは、Spring MVCのコントローラークラスで、/Test001のGETリクエストを処理します。
このメソッドは、IndexListDaoを介してデータベースからデータを取得し、Modelオブジェクトにセットして、ビューに渡します。
IndexListDaoクラスは、データベースのクエリを実行するためのJdbcTemplateを使用して、IndexListDtoオブジェクトのリストを取得します。
findAll()メソッドは、データベースからすべてのIndexListDtoオブジェクトを取得し、IndexResultSetExtractorを使用してResultSetをIndexListDtoオブジェクトのリストに変換します。
findByScreenId()メソッドは、screenIdを使用して特定のIndexListDtoオブジェクトを取得します。
IndexListDtoは、データベースのindex_listテーブルの各行のフィールドに対応するJavaオブジェクトです。
IndexResultSetExtractorとIndexRowMapperクラスは、ResultSetからIndexListDtoオブジェクトにマップするためのカスタムマッパーです。
IndexResultSetExtractorは複数の行に対して実行され、IndexRowMapperは単一の行に対して実行されます。
3. Spring Data JPA
Spring Data JPAは、Java Persistence API (JPA)の実装で、Spring Frameworkの一部です。
JPAは、Javaでオブジェクト指向プログラムを行う際に、データベースを扱うための仕様を定めたものであり、データベースの操作を抽象化してくれます。
Spring Data JPAは、このJPAを利用し、簡潔で保守しやすいコードでデータベースを操作できるようにします。
Spring Data JPAでは、Repositoryと呼ばれるインターフェースを定義し、そのインターフェースに基づいて、自動的にデータベースへのアクセスを行うクラスを生成してくれます。
これにより、開発者はJPAの複雑な操作を意識せずに、シンプルなコードでデータベース操作ができるようになります。
また、Spring Data JPAには、クエリメソッドと呼ばれる便利な機能もあります。
これは、メソッド名からクエリを自動的に生成する機能であり、データベースへのアクセスをより簡単に行えるようにします。
以下が、実装例になります。
3-1. TestSpringDataJpaController.java
package com.learningift.databasetest;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
*
* Spring Data JPAデータベース接続テスト
*
*/
@Controller
public class TestSpringDataJpaController {
private final String SCREEN_ID = "Test002";
@Autowired
IndexListRepository indexListRepository;
@RequestMapping(value = "/" + SCREEN_ID, method = RequestMethod.GET)
public String getSomething(Model model) {
model.addAttribute("indexListDto", findAll());
model.addAttribute("thisPage", findThisPart());
return SCREEN_ID;
}
private List findAll() {
List list = indexListRepository.findAll(Sort.by(Sort.Direction.ASC, "screenId"));
return list;
}
private IndexListDto findThisPart() {
IndexListDto dto = indexListRepository.findByScreenId(SCREEN_ID);
return dto;
}
}
3-2. IndexListRepository.java
package com.learningift.databasetest;
import java.util.List;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* IndexListDtoオブジェクトに対してCRUD操作を提供するインターフェイス
*/
@Repository
public interface IndexListRepository extends JpaRepository {
IndexListDto findByScreenId(String screenId);
List findAll(Sort sort);
}
IndexListDto.java
package com.learningift.databasetest;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* IndexListDtoのエンティティクラス
*/
@Entity
@Table(name = "index_list")
public class IndexListDto {
@Id
@Column(name = "screen_id", unique = true)
private String screenId;
@Column(name = "index_title")
private String indexTitle;
@Column(name = "index_exp")
private String indexExp;
@Column(name = "index_date")
private Date indexDate;
@Column(name = "index_memo")
private String indexMemo;
// コンストラクタ
public IndexListDto() {
}
// ゲッター
public String getScreenId() {
return this.screenId;
}
public String getIndexTitle() {
return this.indexTitle;
}
public String getIndexExp() {
&nb
4. MyBatis
MyBatisは、データベースアクセスを行うためのオープンソースのJava ORMフレームワークの1つであり、SQLをXMLで定義することで、Javaオブジェクトとデータベースの間のマッピングを簡単に行うことができます。
MyBatisは、JDBCに比べてSQL文の記述が簡潔で、また、複雑なSQL文を簡単に扱うことができます。
また、Javaオブジェクトとデータベースの間のマッピングが簡単にできるため、開発効率の向上にもつながります。
MyBatisは、独自のORM機能を持つため、HibernateやSpring Data JPAなどの他のORMフレームワークと比べて柔軟性が高く、SQLのチューニングやパフォーマンスの最適化にも適しています。
ただし、SQLを直接書く必要があるため、実装によってはセキュリティ上の問題が発生する可能性があることに注意が必要です。
以下が、実装例になります。
4-1. TestMyBatisController.java
package com.learningift.databasetest;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.learningift.databasetest.domain.mybatis.IndexList;
import com.learningift.databasetest.domain.mybatis.IndexListExample;
import com.learningift.databasetest.mapper.mybatis.IndexListMapper;
/**
*
* MyBatisデータベース接続テスト
*
*/
@Controller
public class TestMyBatisController {
private final String SCREEN_ID = "Test003";
@Autowired
private IndexListMapper indexListMapper;
@RequestMapping(value = "/" + SCREEN_ID, method = RequestMethod.GET)
public String getSomething(Model model) {
model.addAttribute("indexListDto", findAll());
model.addAttribute("thisPage", findThisPart());
return SCREEN_ID;
}
private List findAll() {
IndexListExample example = new IndexListExample();
example.setOrderByClause("screen_id");
List list = indexListMapper.selectByExample(example);
return list;
}
private IndexList findThisPart() {
return indexListMapper.selectByPrimaryKey(SCREEN_ID);
}
}
4-2. generatorConfig.xml
connectionURL="jdbc:postgresql://localhost:5432/database_test"
userId="admin"
password="admin">
4-3. MyBatis Generator
MyBatis Generatorは、MyBatisで使用するJavaクラスやXMLを自動生成するツールです。
Generatorを使用することで、CRUD(Create, Read, Update, Delete)処理を実行するためのMyBatisのXMLマッパーや、SQLを実行するためのJavaクラスを自動生成することができます。
Generatorは、データベースのテーブル定義からMyBatisで使用するJavaクラスやXMLを生成するための設定ファイルを使用します。
設定ファイルには、生成するJavaクラスのパッケージ名や生成先のディレクトリ、生成するXMLマッパーのパッケージ名や生成先のディレクトリ、生成するテーブル名などが指定されます。
Generatorが生成するJavaクラスには、データベースのテーブルに対応するクラスや、クエリの結果を格納するためのクラスなどがあります。
XMLマッパーには、SQLを定義するためのタグが記述されており、JavaクラスとSQLマッピングを行うための情報が含まれています。
Generatorは、MyBatisの環境設定ファイルに設定することで使用することができます。
Generatorを使用することで、手動でJavaクラスやXMLを作成する手間を省くことができます。
5. まとめ
今回は、Spring Frameworkでデータベースを操作できる、JdbcTemplate、Spring Data JPA、MyBatisの3つのパターンの基本的な実装を見てみました。
以下のようにまとめることができるかと思います。
1.JdbcTemplate
・JDBCを直接使用することで、シンプルで高速なデータアクセスが可能
・SQLを手動で記述する必要があり、煩雑になることがある
・ORMツールに比べて柔軟性が高く、カスタムクエリを作成しやすい
2.Spring Data JPA
・JPAの実装であり、ORMツールであるため、データベースアクセスのためのコードが少なくなる
・Spring Bootとの親和性が高く、自動設定により開発がスムーズに進む
・標準的なCRUD操作に加え、リポジトリのメソッド名によるクエリ自動生成機能があり、簡単なクエリの場合は手軽に実装できる
3.MyBatis
・SQLを手動で記述することで、SQLのチューニングがしやすく、パフォーマンスを最適化できる
・カスタムクエリの作成が容易であり、ORMツールに比べて柔軟性が高い
・XMLやアノテーションを使ってSQLを定義することができるため、可読性に優れる
以上のように、JdbcTemplateはシンプルで柔軟性が高い、Spring Data JPAはORMツールであり、開発がスムーズで手軽に実装できる、MyBatisはSQLのチューニングがしやすく柔軟性が高い、といった特徴があります。
それぞれの特徴を踏まえて、開発者は使い分けることが求められます。