タイトル
 メニューにないコーナーはTopからいけます
TOPJavaspring → This Page

3.1.1.(JdbcTemplate)(単一テーブルのCRUD) Spring+MVC+DB+Test構築サンプル

前提

このページに記載している内容は 2018/09/24 に書かれたものです。
掲載している画面や方法が将来的に変更されている場合があります。
また、掲載しているインストール方法は Windows 8.1 の場合です。
開発環境は
・Windows 8.1
・JDK 8
・STS(Spring Tool Suite) 3.9.5
・PostgreSQL 9.5.14
とします。

本ページは先に以下の2ページの内容を実施してからの内容となります。
1.準備
2.共通部分構築

参考書やサイトによくある
・コントローラに直接DBアクセス処理を記載
・インタフェースを使わない
といった形式ではなく、実務で良く使われているであろう
以下のようなパッケージ構成で作成します。
(Facadeパターンは面倒すぎたのでやめました)
※AOPだけオマケなのでパッケージ構成が微妙ですがご容赦を。。。
図:パッケージ構成

処理遷移もこんな感じです。
図:処理遷移

JdbcTemplate は従来の JDBC をもっと簡単に使えるように用意された Spring Jdbc の機能で、
・非常にシンプルで簡単(SQLを実行するだけ)
・シンプルなので低機能(ORMなどの機能はない)
といった特徴があります。


目次

1.pom.xml編集(PostgreSQL追加)
2.Maven Clean
3.Maven Install
4.jdbc.properties作成
5.application-config.xml編集(DB設定追加)
6.Entityクラスの作成
7.Daoインタフェースの作成
8.Daoクラスの作成
9.DTOクラスの作成
10.Serviceインタフェースの作成
11.Serviceクラスの作成
12.TestController.java編集
13.index.jsp編集
14.確認実行

1.pom.xml編集(PostgreSQL追加)

まずは「pom.xml」を編集して「Maven」に「PostgreSQL」と「JDBC」の利用に
必要なライブラリを揃えてもらいます。
プロジェクト直下にある「pom.xml」をダブルクリックして開きます。
開いたら下のタブを「pom.xml」に切り替えます(最初からpom.xmlになっている場合もあります)
図:pom.xml

以下の内容を <dependencies> から </dependencies> の間に追記して保存します。
保存すると必要なライブラリのダウンロードやビルドが始まるのでしばらく待ちましょう。

		<!-- postgreSQL -->
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<version>9.4-1206-jdbc42</version>
		</dependency>
		
		<!-- Spring JDBC -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>


2.Maven clean

プロジェクト名を右クリックして「Run As」>「Maven clean」を選択します。
「Console」に「[INFO] Finished at ...」と表示されるまで待ちましょう。
図:STS
図:Console


3.Maven install

プロジェクト名を右クリックして「Run As」>「Maven install」を選択します。
先ほどと同様に「Console」に「[INFO] Finished at ...」と表示されるまで待ちましょう。
エラーが出たら「プロジェクトのクリーン」を実施してから「Maven install」を再実施しましょう。
図:STS


4.jdbc.properties作成

データベース接続情報を持ったプロパティファイルを作成します。
プロジェクトを展開し、
/src/main/resources/
フォルダで右クリックして「New」>「File」を選択します。
図:STS

「New File」ダイアログが表示されたら、
フォルダの末尾が /src/main/resources/
となっていることを確認し、
「File name」に
jdbc.properties
と入力して「Finish」ボタンを押しましょう。
図:New File

jdbc.properties がエディタで開くので
以下の内容を記入して保存しましょう。
#Database Configuration
jdbc.driverClassName=org.postgresql.Driver
jdbc.url=jdbc:postgresql://localhost:5432/testdb
jdbc.username=mitchy
jdbc.password=ppp

プロパティの名前で設定値の意味はなんとなく分かると思うので
値はご自分の環境に合わせて変更しましょう。

5.application-config.xml編集(DB設定追加)

application-config.xml にもデータベース接続情報を設定します。
/src/main/resources/spring
フォルダにある
application-config.xml
をダブルクリックして開きましょう。
開いたら下のタブを「Source」にします。
(最初から「Source」になっている場合もあります)
図:STS

以下の内容に書き換えて保存しましょう。
<?xml version="1.0" encoding="UTF-8"?>

<beans
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	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" />
	<context:component-scan base-package="jp.mitchy"/>
	
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
	
	<bean class="org.springframework.jdbc.core.JdbcTemplate">
		<constructor-arg ref="dataSource" />
	</bean>
	
</beans>
<context:property-placeholder location="classpath:jdbc.properties" />
は先ほど作成したプロパティファイルのパスを示す設定です。

<context:component-scan base-package="jp.mitchy"/>
は今から作成するクラスがあるパッケージを示す設定です。

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
はデータベース情報です。
プロパティファイルに記載された各種情報を読み込んで設定されます。
プロパティファイルを利用せず、ここに直接設定値を記載することもできますが、
プロパティファイルだけを差し替えることで簡単に環境を変更できるので
開発環境、テスト環境、本番環境など色々な環境で実行する必要がある実務では
プロパティファイルに設定値を外出しすることが一般的です。

<bean class="org.springframework.jdbc.core.JdbcTemplate">
は JdbcTemplate を利用するための設定です。


6.Entityクラスの作成

今回は「単一テーブルのCRUD」ということで、
準備編で用意した最も単純なテーブル「department」テーブルを使うことにします。
図:department

1テーブルにつき対応した1Entityクラスを作成します。
プロジェクト名を右クリックして「New」>「Class」を選択します。
図:STS

「New Java Class」ダイアログが表示されます。
・「Package」は「jp.mitchy.entity」と入力
・「Name」は「Department」と入力
・それ以外の項目はそのまま
「Finish」ボタンを押します。
図:New Java Class

作成された「Department.java」が開くので、内容を以下のように書き換えて保存しましょう。
package jp.mitchy.entity;

public class Department {
	private Long id;
	private String name;
	
	public Department() {
	}
	
	public Department(Long id, String name) {
		this();
		this.id = id;
		this.name = name;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "[id=" + id + ",name=" + name + "]";
	}
}
テーブルのカラムに合わせて id と name をメンバフィールドに持ちます。
あとはコンストラクタと表示用の toString メソッドだけの簡単な構造です。


7.Daoインタフェースの作成

次に実際にデータベース操作をするDAO(Data Access Object)を
作りたいところですが、実務ではインタフェースを作り、
そのインタフェースを実装したクラスにすることが多いので
今回もまずはインタフェースを作成します。
インタフェースを使うメリットは自分で調べて下さい(^^;;

同じく1テーブルにつき対応した1DAOインタフェースを作成します。
プロジェクト名を右クリックして「New」>「Interface」を選択します。
図:STS

「New Java Interface」ダイアログが表示されます。
・「Package」は「jp.mitchy.dao」と入力
・「Name」は「DepartmentDao」と入力
・それ以外の項目はそのまま
「Finish」ボタンを押します。
図:New Java Interface

作成された「DepartmentDao.java」が開くので、内容を以下のように書き換えて保存しましょう。
package jp.mitchy.dao;

import java.util.List;

import jp.mitchy.entity.Department;

public interface DepartmentDao {
	public List<Department> getAllEntity();
	public Department findById(Long id);
	public void addEntity(Department entity);
	public void updateEntity(Department entity);
	public void removeEntity(Department data);
	public void removeEntity(Long id);
}
メソッド名を見てなんとなく何をするメソッドか想像はつくと思いますが、
上からそれぞれ
・全レコード取得
・1レコード取得
・レコード追加
・レコード更新
・レコード削除(引数がエンティティ)
・レコード削除(引数がID)
です


8.Daoクラスの作成

先ほど作成したインタフェースを実装したクラスを作成します。
同じく1テーブルにつき対応した1DAOクラスを作成します。
プロジェクト名を右クリックして「New」>「Class」を選択します。
図:STS

「New Java Class」ダイアログが表示されます。
・「Package」は「jp.mitchy.dao.impl」と入力
・「Name」は「DepartmentDaoImpl」と入力
・それ以外の項目はそのまま
「Finish」ボタンを押します。
図:New Java Class

作成された「DepartmentDaoImpl.java」が開くので、内容を以下のように書き換えて保存しましょう。
package jp.mitchy.dao.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
// import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import jp.mitchy.dao.DepartmentDao;
import jp.mitchy.entity.Department;

@Repository
public class DepartmentDaoImpl implements DepartmentDao {
	
	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	public DepartmentDaoImpl() {
		init();
	}
	
	public void init(){
		// @Autowired がうまく機能しない場合は以下のコメントを外す
		// SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
	}

	public List<Department> getAllEntity() {
		System.out.println("getAllEntity()");
		/**
		// 結果がMapでいいならqueryForList(sql)が一番シンプルに取得できる
		List<Map<String, Object>> list = jdbcTemplate.queryForList("SELECT * FROM Department");
		// ただしEntityには自力で変換が必要
		List<Department> result = new ArrayList<Department>();
		for ( Map<String, Object> map : list ) {
			Department entity = new Department( (Long)map.get("id") , (String)map.get("name"));
			result.add(entity);
		}
		return result;
		*/
		return jdbcTemplate.query("SELECT * FROM Department",
				new BeanPropertyRowMapper<Department>(Department.class));
	}

	public Department findById(Long id) {
		return jdbcTemplate.queryForObject("SELECT * FROM Department WHERE id = ?",
				new Object[] { id },
				new BeanPropertyRowMapper<Department>(Department.class));
	}
	
	public void addEntity(Department entity) {
		System.out.println("addEntity(entity)");
		jdbcTemplate.update("INSERT INTO Department VALUES(?, ?)",
				entity.getId(), entity.getName());
	}

	public void updateEntity(Department entity) {
		System.out.println("updateEntity(entity)");
		jdbcTemplate.update("UPDATE Department SET name = ? WHERE id = ?",
				entity.getName(), entity.getId());
	}

	public void removeEntity(Department entity) {
		System.out.println("removeEntity(entity)");
		jdbcTemplate.update("DELETE FROM Department WHERE id = ?",
				entity.getId());
	}

	public void removeEntity(Long id) {
		System.out.println("removeEntity(id)");
		jdbcTemplate.update("DELETE FROM Department WHERE id = ?",
				id);
	}

}
クラス名の上に @Repository アノテーションを付けることで、
MVCにおけるデータ層のクラスとして Spring のDIコンテナに bean として登録されます。

jdbcTemplate フィールドに @Autowired アノテーションを付けることで
自動的に jdbcTemplate のオブジェクトがバインドされます。

init メソッドの内容は基本的には不要です。
場合によっては必要になることがありますが、
本サイトのサンプルでは必要ありません。

それぞれのデータアクセスのメソッドでは実際に jdbcTemplate を使って
レコードの取得、レコードの変更を行っています。
良く見てみると分かるのですが、
複数レコードの取得には query メソッド、
単一レコードの取得には queryForObject メソッド、
レコードの変更には追加・更新・削除いずれも update メソッドを使っています。
つまり、この3メソッドだけおさえておけば
基本的なCRUD処理は対応できるようになります。

実は複数レコードの取得には、もっと簡単な queryForList メソッドがあります。
しかし、残念なことに戻り値が Map の List になるため、
取得したレコードの操作が煩雑になってしまいます。
そのため、エンティティクラスの List で取得できる query メソッドを使っています。
参考のために queryForList メソッドで対応した場合のコードも
コメントでクラス内に記載しておきました。

query メソッドも queryForList メソッドも戻り値が違うだけで使い方はほぼ同じです。
SQL にパラメータが不要な場合は
 引数1:SQL文
 引数2:エンティティクラスに変換するためのBeanPropertyRowMapper
SQL にパラメータが必要な場合は
 引数1:SQL文
 引数2:SQLパラメータ(Object型の配列で指定)
 引数3:エンティティクラスに変換するためのBeanPropertyRowMapper
です。

レコードの追加、更新、削除はもっと簡単です。
update メソッドに
 引数1:SQL文
 引数2:SQLパラメータ1
 引数3:SQLパラメータ2
 ・・・:SQLパラメータ3
を指定するだけです。


9.DTOクラスの作成

Controller が Service の結果を受け取るために使うDTOクラスを作成します。
次ページ以降の複数サービスで共通的に使えるように今回は汎用的なDTOクラスにします。
プロジェクト名を右クリックして「New」>「Class」を選択します。
図:STS

「New Java Class」ダイアログが表示されます。
・「Package」は「jp.mitchy.dto」と入力
・「Name」は「TestResultDto」と入力
・それ以外の項目はそのまま
「Finish」ボタンを押します。
図:New Java Class

作成された「TestResultDto.java」が開くので、内容を以下のように書き換えて保存しましょう。
package jp.mitchy.dto;

import java.util.List;

/**
 * TestControllerに返すServiceの戻り値のDTO
 * 
 * @param <T> Serviceごとに型が違うので総称型で対応
 */
public class TestResultDto<T> {
	private T entity;
	private List<T> list;
	
	public T getEntity() {
		return entity;
	}
	public void setEntity(T entity) {
		this.entity = entity;
	}
	public List<T> getList() {
		return list;
	}
	public void setList(List<T> list) {
		this.list = list;
	}
	
}
総称型 <T> については特に解説しないので自分で調べて下さい(^^;;
今回のサンプルでは TestController が result.jsp に
1レコードとリストの2種類しか返さず、
Service も同じく1レコードとリストの2種類だけを返すものにしています。


10.Serviceインタフェースの作成

次にサービス層のインタフェース・クラスを作ります。
今回もまずはインタフェースを作成します。
プロジェクト名を右クリックして「New」>「Interface」を選択します。
図:STS

「New Java Interface」ダイアログが表示されます。
・「Package」は「jp.mitchy.service」と入力
・「Name」は「DepartmentService」と入力
・それ以外の項目はそのまま
「Finish」ボタンを押します。
図:New Java Interface

作成された「DepartmentService.java」が開くので、内容を以下のように書き換えて保存しましょう。
package jp.mitchy.service;

import jp.mitchy.dto.TestResultDto;
import jp.mitchy.entity.Department;

public interface DepartmentService {
	public TestResultDto<Department> execute(Department entity);
}
エンティティを引数とし、TestResultDto を返す execute メソッドのみのシンプルなインタフェースです


11.Serviceクラスの作成

先ほど作成したインタフェースを実装したクラスを作成します。
プロジェクト名を右クリックして「New」>「Class」を選択します。
図:STS

「New Java Class」ダイアログが表示されます。
・「Package」は「jp.mitchy.service.impl」と入力
・「Name」は「DepartmentServiceImpl」と入力
・それ以外の項目はそのまま
「Finish」ボタンを押します。
図:New Java Class

作成された「DepartmentServiceImpl.java」が開くので、内容を以下のように書き換えて保存しましょう。
package jp.mitchy.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import jp.mitchy.dao.DepartmentDao;
import jp.mitchy.dto.TestResultDto;
import jp.mitchy.entity.Department;
import jp.mitchy.service.DepartmentService;

@Service
public class DepartmentServiceImpl implements DepartmentService {
	
	@Autowired
	private DepartmentDao dao;
	
	public DepartmentServiceImpl() {
		init();
	}
	
	public void init(){
		// @Autowired がうまく機能しない場合は以下のコメントを外す
		// SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
	}

	public TestResultDto<Department> execute(Department entity) {
		TestResultDto<Department> result = new TestResultDto<Department>();
		
		// INSERT
		dao.addEntity(entity);
		
		// UPDATE
		entity.setName("廃止");
		dao.updateEntity(entity);
		
		// DELETE
		dao.removeEntity(entity.getId());
		
		// SELECT 単体
		Department entity2 = dao.findById(2L);
		result.setEntity(entity2);
		
		// SELECT 複数
		List<Department> list = dao.getAllEntity();
		result.setList(list);

		return result;
	}
	
}
クラス名の上に @Service アノテーションを付けることで、
サービス層のクラスとして Spring のDIコンテナに bean として登録されます。

dao フィールドに @Autowired アノテーションを付けることで
自動的に DepartmentDaoImpl のオブジェクトがバインドされます。

execute メソッドの内容は単一テーブルのCRUDということで、
・INSERT
・UPDATE
・DELETE
・SELECT 単体
・SELECT 複数
を実施するようにしています。
※トランザクション制御は後のページでやります。


12.TestController.java編集

jp.mitchy.controller にある TestController.java を変更し、
今回作成したサービスを呼び出して実行するようにします。
内容を以下のように書き換えて保存しましょう。
前もって作成しておいた test メソッドは削除してかまいません。
package jp.mitchy.controller;

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 jp.mitchy.dto.TestResultDto;
import jp.mitchy.entity.Department;
import jp.mitchy.service.DepartmentService;

@RequestMapping("/test/*")
@Controller
public class TestController {
	
	@Autowired
	private DepartmentService deptService;
	
	@RequestMapping(value = "/dept", method = RequestMethod.GET)
	public String dept(Model model) {
		// ------------------------------
		// 単テーブルのCURD確認
		// ------------------------------
		
		Department entity = new Department(4L, "新事業部");
		
		// サービスの実行
		TestResultDto<Department> dto = deptService.execute(entity);
		
		// 結果をセット
		model.addAttribute("data", dto.getEntity());
		model.addAttribute("list", dto.getList());

		// view/test/result.jsp を表示
		return "test/result";
	}
	
}

deptService フィールドに @Autowired アノテーションを付けることで
自動的に DepartmentServiceImpl のオブジェクトがバインドされます。

dept メソッドに
@RequestMapping(value = "/dept", method = RequestMethod.GET)
を付けることで http://サーバ/プロジェクト名/test/dept
の URL に対応します。

dept メソッドではサービスを実行します。
サービスでは ID=4 のレコードを追加、変更、削除し、
さらに ID=2 のレコードを取得、全レコードを取得します。
取得結果は DTO にセットして Controller に返します。
Controller はサービスから受け取った結果をモデルにセットし、
view/test/result.jsp を表示するようにして処理終了です。


13.index.jsp編集

最後に、作成した処理を呼び出せるように index.jsp を編集します。
src/main/webapp/index.jsp を開き、内容を以下のように書き換えて保存しましょう。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>

<html>
	<head>
		<meta charset="utf-8">
		<title>Welcome</title>
	</head> 
	<body>
		<c:url value="/test/dept" var="messageUrl1" />
		<a href="${messageUrl1}">Department Test</a><br/>
	</body>
</html>


14.確認実行

念のためにいつもの「Maven Clean」「Maven Install」をやっておきましょう。
エラーが出たら「Project」>「Clean」をやってから
再度「Maven Install」です。

プロジェクト名を右クリックして「Run As」>「Run On Server」を選択します。
図:STS

少し時間がかかりますが「Console」に状況が表示されていきます。
しばらくすると内蔵ブラウザが立ち上がり、「Department Test」と表示されます。
図:ブラウザ

「Department Test」のリンクをクリックしてみましょう。
リンク先 /test/dept に連動する TestController クラスの dept メソッドが呼び出され、
/WEB-INF/view/test/result.jsp が表示されることが確認できます。
テーブル操作も正常に実行されていますね。
(Consoleの出力内容も確認してみましょう)
図:ブラウザ

長かったですが、以上で JdbcTemplate を使った単一テーブルのCRUDの
サンプル構築は完了です。


ダウンロード

作成したプロジェクトのソースをダウンロードできます。
311WebDbSample1JdbcTemplate.zip


更新履歴

2018/09/24 新規作成

TOPJavaspring → This Page
Valid CSS!