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

3.2.5.(Entity Manager)(トランザクション制御+エラーページ) Spring+MVC+DB+Test構築サンプル

前提

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

本ページは先に以下の6ページの内容を実施してからの内容となります。
1.準備
2.共通部分構築
3.2.1.Entity Manager 単一テーブルのCRUD
3.2.2.Entity Manager N:1テーブルのCRUD+自由なクエリ
3.2.3.Entity Manager 複合主キーテーブルのCRUD
3.2.4.Entity Manager 複数テーブルの結合+NativeQuery


目次

1.application-config.xml編集
2.Serviceクラスの変更1
3.Serviceクラスの変更2
4.ErrorController.javaの作成
5.error.jspの作成
6.確認実行

1.application-config.xml編集

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"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/data/jpa
		http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/jdbc
		http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd">
	
	<context:property-placeholder location="classpath:jdbc.properties" />
	<context:annotation-config />
	<context:component-scan base-package="jp.mitchy"/>
	
	<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
	
	<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 id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="showSql" value="false" />
				<property name="database" value="POSTGRESQL" />
			</bean>
		</property>
	</bean>
	
	<tx:annotation-driven transaction-manager="transactionManager"/>
	
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
</beans>
<tx:annotation-driven transaction-manager="transactionManager" />
はトランザクション関連のアノテーションを使うための設定です。

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
はトランザクション制御クラスの定義です。


2.Serviceクラスの変更1

「jp.mitchy.service.impl」パッケージにある「DepartmentServiceImpl」クラスを変更しましょう。
「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.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
//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);
	}

	@Transactional(readOnly=false, propagation=Propagation.REQUIRES_NEW, rollbackFor=Exception.class)
	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;
	}
	
}
execute メソッドに @Transactional アノテーションをつけています。
これでこのメソッドがトランザクション制御されます。

readOnly=false をつけることで更新が可能となります。
(デフォルトでfalseなので省略可能です)

propagation=Propagation.REQUIRES_NEW をつけることで常に新規にトランザクションを新規開始します。
propagation=Propagation.REQUIRES にすると、トランザクションが開始されていればそのトランザクションを利用し、
開始されていなければ新規開始します。

rollbackFor=Exception.class をつけることで例外発生時にロールバックされます。
rollbackFor=XxxException.class のようにすると XxxException 例外が発生したときだけロールバックされます。


3.Serviceクラスの変更2

「jp.mitchy.service.impl」パッケージにある「StaffServiceImpl」クラスを変更しましょう。
「StaffServiceImpl.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 org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import jp.mitchy.dao.StaffDao;
import jp.mitchy.dto.TestResultDto;
import jp.mitchy.entity.Staff;
import jp.mitchy.service.StaffService;

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

	@Transactional(readOnly=false, propagation=Propagation.REQUIRES_NEW, rollbackFor=Exception.class)
	public TestResultDto<Staff> execute(Staff entity) {
		TestResultDto<Staff> result = new TestResultDto<Staff>();
		
		// INSERT
		dao.addEntity(entity);
		
		// UPDATE
		entity.setName("退職太郎3");
		dao.updateEntity(entity);
		
		// わざと重複エラーを起こす
		// INSERT
		dao.addEntity(entity);
		
		// DELETE
		dao.removeEntity(entity.getId());
		
		// SELECT 単体
		Staff entity2 = dao.findById(102L);
		result.setEntity(entity2);
		
		// SELECT 複数
		List<Staff> list = dao.getAllEntity();
		result.setList(list);

		return result;
	}
	
}


4.ErrorController.javaの作成

次に例外発生時に自動で動作するエラー用コントローラ・クラスを作ります。
プロジェクト名を右クリックして「New」>「Class」を選択します。
図:STS

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

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

import javax.persistence.PersistenceException;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class ErrorController {

	/**
	 * DB操作例外発生時にエラーページに遷移
	 * 
	 * @param model
	 * @return 表示jsp
	 */
	@ExceptionHandler(PersistenceException.class)
	public String persistenceExceptionHandler(Model model) {
		model.addAttribute("msg", "データベースの操作に失敗しました。");
		
		// view/common/error.jsp を表示
		return "common/error";
	}
	
	/**
	 * 例外発生時にエラーページに遷移
	 * 
	 * @param model
	 * @return 表示jsp
	 */
	@ExceptionHandler(Throwable.class)
	public String throwableHandler(Model model) {
		model.addAttribute("msg", "例外が発生しました。");
		
		// view/common/error.jsp を表示
		return "common/error";
	}
	
}
クラス名の上に @ControllerAdvice アノテーションをつけています。
これをつけることによってコントローラーをまたいで横断的に様々なハンドリングができるようになります。

各メソッドの上に @ExceptionHandler アノテーションをつけています。
これをつけることによって例外をハンドリングできるようになります。
アノテーションのカッコ内にハンドリングしたい例外クラスを指定しています。
persistenceExceptionHandler メソッドには PersistenceException.class クラスを指定しているので
DB関連例外が発生した場合に動作します。
throwableHandler メソッドには Throwable.class クラスを指定しているので
その他の全ての例外が発生した場合に動作します。

ともに戻り値として遷移先のJSP(view/common/error.jsp)を指定しています。


5.error.jspの作成

最後に例外が発生した際に表示されるエラー用のページ(JSP)を作成します。
プロジェクト名を右クリックして「New」>「Other」を選択します。
図:STS

「New」ダイアログが表示されます。
ツリーから「Web」>「JSP File」を選択して「Next」ボタンを押します。
図:New

「New JSP File」画面でフォルダを
src/main/webapp/WEB-INF/view/common/
とし、「File name」を「error.jsp」として「Finish」ボタンを押します。
図:New JSP File

「error.jsp」の内容を以下のように書き換えて保存しましょう。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
	<title>error</title>
</head>
<body>
<h1>
	${msg}
</h1>
 
</body>
</html>
受け取ったメッセージを表示するだけのシンプルな画面です。


6.確認実行

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

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

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

まずは「Department Test」のリンクをクリックしてみましょう。
こちらは正常動作するサービスを呼び出すので変更前と同じくDB取得結果が表示されます。
図:ブラウザ

前の画面に戻って「Staff Test」のリンクをクリックしてみましょう。
こちらは重複エラーでDB操作例外が発生するサービスを呼び出すので
エラー画面に遷移することが確認できます。
(Consoleの出力内容も確認してみましょう)
図:ブラウザ

PostgreSQLの中身を確認したり、Serviceクラスを元に戻してテスト実行したりして、
先ほどの例外発生時にちゃんとロールバックされていることを確認しましょう。

以上で Entity Manager を使ったトランザクション制御+エラーページの
サンプル構築は完了です。


ダウンロード

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


更新履歴

2018/10/14 新規作成

TOPJavaspring → This Page
Valid CSS!