見出し画像

職業訓練でJavaを学ぶ!50日目(1/1)「Javaプログラミング実習(フレームワーク実習編)」

職業訓練で、Javaを使ったWebシステム開発・Androidアプリ開発を学ぶことになりました。復習かねて情報発信をします!

これから職業訓練に通うことを検討している方の参考になれば嬉しいです。↓↓職業訓練に通うことになった経緯はこちら
https://note.mu/yukoro/n/ned8d43b0110d

本日の授業内容

<Hibernate ORM 入門>
・Hibernate ORM の概要 
・ Hibernate ORM の利用準備
・Hibernate ORM によるDB操作の基本


Hibernate ORM の概要

Hibernate は、Java のためのオブジェクト関係マッピング (ORM) ライブラリです。O/Rと記述されることもあります。

Java用のORMフレームワークは、アプリケーションとJDBCの間のレイヤーとなって、Mapping:オブジェクトとリレーショナルデータベースのレコードの対応付け(変換)を行ってくれます。

-----------
Javaアプリ ⇄ Hibernate ORM ⇄ JDBC ⇄ RDB
----------

ORMの特徴としては、SQLを記述するのではなく、オブジェクトを操作する感覚でデータベースを操作できるようになる点です。

細かい操作を行いたいときは、細かい操作用にHQL (Hibernate Query Language)という言語も用意されています。これはSQLのHibernate版みたいなものです。

SQLでこのような記述があったとしたら

SELECT * FROM messages

HQLではこのような記述になります。

from Message


■マッピングの指定方法
O/Rマッピングを使うためにはドメインクラスとテーブルの対応関係を指定する必要があります。

またドメインクラスとテーブルを対応させる方法は次の2通りの方法がある
マッピングファイルを使用する方法
アノテーションを使用する方法


■マッピングファイルの使用
マッピングファイルは、オブジェクトとテーブルの対応関係を記述したXMLファイルです。

イメージ
----------
    マッピングファイル
[ドメインクラス] ⇄ [テーブル]
----------


■アノテーションの使用
ドメインクラス内に @Entity, @Table などのアノテーションを付加して紐付けを行います。ただしこの方法だと、ドメインクラス内にアノテーションが増えすぎてしまう恐れもあります。

例)

@Entity
@Table(name = "messages")
public class Message {

    @Id
    @GeneratedValue
    private Integer id;
    ...


Hibernate ORMの利用準備

まずHibernateORMを利用したDBアクセスの手順をみてみましょう。

1. SessionFactoryの準備
2. セッションの開始
3. トランザクションの開始
4. DBの操作(データの取得/追加/更新/削除)
5. トランザクションのコミット
6. セッションの終了

1〜6までの手順がありますが、1はSpringのBeanFactoryが実行し、2,3,5,6はTransactionManagerが実行してくれるため、Springと連携した開発においては、4のDBの操作だけを行えばOKです。ただし、TransactionManagerの利用設定は必要になります。

これら一連のデータベース操作はセッションと呼ばれ、SessionオブジェクトのメソッドでDB操作を行います


今回は、mydbというデータベースのこのようなテーブルの操作を行う想定で話を進めます。
例)mydbデータベースのmembersテーブル

スクリーンショット 2019-09-29 14.59.24


■HibernateによるDB操作の準備

ライブラリの追加
Bean定義ファイル(servlet-context.xml)の記述
 - データソースの設定
 - SessionFactoryの設定
 - TransactionManagerの設定
マッピングファイルの作成
 - 通常は「ドメインクラス名.hbm.xml」という名前にする


◇POM.xmlでのライブラリの追加
Mavenを利用する場合、依存関係に以下のパッケージを追加します。
(バージョンは任意)

グループ  id: org.hibernate
アーティファクト id: hibernate-core
バージョン: 5.4.2.Final

グループ  id: org.springframework
アーティファクト id: spring-orm
バージョン: ${org.springframework-version}

${org.springframework-version}は SpringFrameworkのバージョンに自動的に合わせてくれる設定方法です。


◇Bean定義ファイルの設定
Bean定義ファイル(servlet-context.xml)の名前空間タブで、jeetx にチェックを入れて保存します。

スクリーンショット 2019-09-28 17.31.19


◇Bean定義ファイル:データソースとSessionFactoryの設定
Bean定義ファイルにデータソースHibernateORMのSession Factory の設定を記述します。

例)mydbデータベースのmembersテーブルとdomainクラスのMember.javaと紐付けを行う想定

...省略...

<!-- データソースの指定 -->
<jee:jndi-lookup id="dataSource"jndi-name="jdbc/mydb" />

<!-- Hibernate ORM の設定 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
	<!-- データソースの指定 -->
	<property name="dataSource" ref="dataSource" />

	<!-- 使用するマッピングファイルの指定 -->
	<property name="mappingResources">
		<list>
		    <value>hbm/Member.hbm.xml</value>
		</list>
	</property>

	<!-- 利用するデータベースの方式の指定 -->
	<property name="hibernateProperties">
		<props>
		    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
		    
                    <!-- 以下はオプション -->
                    <!-- 実行されるSQLを整形してコンソールに表示 -->
		    <prop key="hibernate.show_sql">true</prop>
		    <prop key="hibernate.format_sql">true</prop>
                </props>
	</property>
</bean>

説明:
---------
jndi-name属性の値はcontext.xmlのResourceLinkで設定した値に合わせます。マッピングファイルの指定では、
テーブルと紐付けしたいdomainクラス名.hbm.xml
としてください。
---------


◇Bean定義ファイル:TransactionManagerの設定
次にTransactionManagerの設定を行います。

補足:
==========
確認としてTransactionManagerはDBアクセスの手順において
2. セッションの開始
3. トランザクションの開始
5. トランザクションのコミット
6. セッションの終了
を実行してくれるものです。

そのため、開発者は「現在のセッションを管理するSessionオブジェクトを取得」の部分だけ行えば良くなります。
例)

Session session = sessionFactory.openSession(); //セッションの開始
session.beginTransaction(); // トランザクションの開始

// 現在のセッションを管理するSessionオブジェクトを取得
Session session = sessionFactory.getCurrentSession();
// Sessionオブジェクトを使用したDB操作
Member member = (Member) session.get(Member.class, 3);
member.setAge(25);
session.update(member);

session.getTransaction().commit(); // トランザクションのコミット
session.close(); // セッションのクローズ

==========

TransactionManagerのBean定義ファイルでの設定
例)

...

<!-- Transaction Manager の設定 -->
<bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
	<property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- アノテーションによるトランザクション管理を有効にする -->
<tx:annotation-driven transaction-manager="txManager" />

説明:
--------

Bean定義ファイルにTransactionManagerとannotation-drivenを設定することで、@Transactionalというアノテーションで、トランザクション管理ができるようになります。

Transaction Manager の設定のid名は任意です。
propertyのname属性は、Hibernate ORM の設定でidとして設定した値と一致します。

annotation-drivenの設定のtransaction-manager属性は、Transaction Manager の設定のid属性と一致します。
--------


◇マッピングファイルの作成
マッピングファイルを作成して、ドメインクラスとテーブルの紐づけを行います。

例)格納先:src/main/resource/hbm/Member.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping
DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!-- domainクラスとテーブルのカラムを紐付け -->
<hibernate-mapping package="com.example.mvc.domain">
	<class name="Member" table="members">
		<!-- 主キーのマッピング -->
		<id name="id" column="id">
			<generator class="native"></generator>
		</id>
		<!-- その他のカラムのマッピング -->
		<property name="name" column="name" type="string"></property>
		<property name="age" column="age" type="integer"></property>
		<property name="address" column="address" type="string"></property>
		<property name="typeId" column="type_id" type="integer"></property>
		<property name="created" column="created" type="date"></property>
	</class>
</hibernate-mapping>

説明:
---------
階層のhbmはパッケージです。
package属性はプロジェクトのドメインクラスのパッケージを指定します。

設定全体をhibernate-mappingタグで囲み、package属性で、ドメインクラスのあるパッケージを指定します。

直下には<class>タグが入り、<class>タグで対応するクラスとテーブルを指定します。

主キーのみpropertyタグでなく、idタグで設定し、主キーの生成方法は、generatorタグで指定します。
下記のような選択肢がありますが、 nativeが一般的なようです。
 native・・・データベース固有の方法でIDを生成
 increment・・・IDを自動的にインクリメント(連番)で生成
 assigned・・・アプリケーション側でIDを生成

<property>タグのname属性でドメインクラスのフィールド名を指定し、
column属性でテーブルのカラム名を指定、type属性でデータの種類を指定します。
---------

設定が完了したので次からDB操作を行ってみます。


DB操作の基本

■ドメインクラスについて
Hibernate ORMで使用するドメインクラスが満たすべき条件は下記の通りです。
・デフォルトコンストラクタ(引数無しのコンストラクタ)があること(必須)
・プロパティへのアクセッサメソッドがあること(推奨)
・プライマリキー(主キー)のプロパティとアクセッサメソッドがあること(推奨)

例)membersテーブルと紐付けるdomainクラスのMember.java

package com.example.mvc.domain;

import java.util.Date;

public class Member {
	private Integer id;
	private String name;
	private Integer age;
	private String address;
	private Integer typeId;
	private Date created;

	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public Integer getTypeId() {
		return typeId;
	}
	public void setTypeId(Integer typeId) {
		this.typeId = typeId;
	}
	public Date getCreated() {
		return created;
	}
	public void setCreated(Date created) {
		this.created = created;
	}
}


■Spring連携時のDaoの実装例

例)DAO/トランザクション処理の自動化とSessionFactoryの注入

@Transactional
@Repository
public class MemberDao {

    @Autowired
    private SessionFactory sessionFactory;

    private Session getSession() {
        return sessionFactory.getCurrentSession();
    }
...

説明:

@Transactional
@Repository
public class MemberDao {
...

@Transactional によって、トランザクション処理を自動化し、@Autowired でSessionFactoryを注入します。
@Transactional は org.springframworkからインポートします。

コンポーネントスキャン対象にするための @Repository アノテーションもつけます。

    @Autowired
    private SessionFactory sessionFactory;

依存性の注入を行います。

何が注入されているかというと、Bean定義ファイルのHibernateの設定で<bean>タグにid="sessionFactory"としましたが、このsessionFactoryが生成されれ注入されます。
このsessionFactoryは名前の通り、セッションを作り出します。

private Session getSession() {
    return sessionFactory.getCurrentSession();
}

sessionFactroryによって作り出されたセッションを使って、データベースの操作を行います。

それではこのgetSession()メソッドを使った、Hibernate ORMでデータベースの様々操作方法をみていきます。


■様々なDB操作
◇全データの取得
まずはDBから全行のデータを取得する方法です。

オブジェクトのリストを取得する際は、createQueryメソッドでHQLを記述し、getResultListメソッドでリスト形式でオブジェクトを取得します。

例)DAO/オブジェクトリストの取得

@SuppressWarnings("unchecked")
public List<Member> findAll() throws Exception {
    return getSession().createQuery("from Member").getResultList();
}
package com.example.mvc.domain;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Transactional
@Repository
public class MemberDao {

	    @Autowired
	    private SessionFactory sessionFactory;

	    private Session getSession() {
	        return sessionFactory.getCurrentSession();
	    }

	    //全員のデータを取得
	    public List<Member> findAll() {
	    	return getSession().createQuery("from Member", Member.class).getResultList();
	    }
}

説明:
getSession()でセッション型のオブジェクトを手に入れる
createQuery()でHQL文を使う
"from member" の部分がHQL文
getResultList()でリスト形式のデータを取得
取得したものをreturnで返します。

次にControllerを作成します。

例)Conrtoller/オブジェクトリストの取得

package com.example.mvc.controller;

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 com.example.mvc.domain.Member;
import com.example.mvc.domain.MemberDao;

@Controller
public class MemberController {

	@Autowired
	private MemberDao dao;

	@RequestMapping("/listMember")
	public String list(Model model) {
		List<Member> members = dao.findAll();
		model.addAttribute("members", members);
		return "listMember";
	}
}

説明:
/listMember のURLにリクエストがあった際に実行するlist()メソッドを定義します。

DAOで定義したfindAll()メソッドを使って、Controller側でオブジェクトリストを取得します。

取得したオブジェクトリストをModelに格納してlistMember.jspを起動します。

例)JSP/オブジェクトの取得

<%@ page pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>会員一覧</title>
</head>
<body>
	<h1>会員一覧</h1>

	<table border="1">
	<tr>
		<th>ID</th>
		<th>氏名</th>
		<th>年齢</th>
		<th>住所</th>
		<th>会員種別</th>
		<th>登録日</th>
	</tr>
	<c:forEach var="member" items="${members}">
		<tr>
			<td><c:out value="${member.id}" /></td>
		</tr>
	</c:forEach>
	</table>
</body>
</html>

説明:
JSP側では<c:forEach>を使って、リストデータを表示します。

プロジェクトの実行
プロジェクトを実行したらURL末尾に listMember をつけてアクセスします。
DBのmembersテーブルのデータが表示されました。

スクリーンショット 2019-09-29 15.49.53


◇単一データの取得
IDを指定した単一オブジェクトの取得は、getメソッドの引数として、ドメインクラスと主キーを指定します。

例)DAO/IDを指定した単一オブジェクトの取得

public Member findById(Integer id) throws Exception {
    return getSession().get(Member.class, id);
}


◇データの追加
データの追加はsaveメソッドの引数としてドメインオブジェクトを指定します。saveOrUpdateメソッドでも可能です。

例)DAO/データの追加

public void add(Member member) throws Exception {
    member.setCreated(new Date());
    getSession().save(member);
}

例)Conrtoller/データの追加

...フォーム入力値の取得とバリデーション...
//オブジェクト生成
Member member = new Member();

//値の取得
member.setName(name);
member.setAge(age);

//値のセット
memberDao.add(member);

この時のController側の処理としては、オブジェクトを生成し、フォーム
から取得した値をセットします。


◇データの更新
データの更新はupdateメソッドの引数としてドメインオブジェクトを指定します。saveOrUpdateメソッドでもOKです。

例)DAO/データの更新

public void edit(Member member) throws Exception {
    getSession().update(member);
}

例)Controller/データの更新

...フォーム入力値の取得とバリデーション...
//オブジェクト生成
Member member = memberDao.findById(id);

//値の取得
member.setName(name);
member.setAge(age);

//値のセット
memberDao.edit(member);

ControllerはURIテンプレート等から取得したidを使い、更新対象をオブジェクトとして取得します。


◇データの削除
データの削除はdeleteメソッドの引数としてドメインオブジェクトを指定します。データを削除する場合は、JavaScript等で確認を行うのが望ましいです。

例)DAO/データの削除

public void edit(Member member) throws Exception {
getSession().delete(member);
}

例)Controller/データの削除

Member member = memberDao.findById(id);
memberDao. remove(member);

ControllerはURIテンプレート等から取得したidを使い、更新対象をオブジェクトとして取得します。


「Hibernate ORM 入門」については以上です。最後までお読みいただきありがとうございました!

この記事が気に入ったらサポートをしてみませんか?