見出し画像

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

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

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

本日の授業内容

<Hibernate ORM の利用>
・テーブルの連携
・HQLの利用


テーブルの連携

Hibernate ORM の利用時にテーブルを連携させる際、マッピングファイルでテーブル同士の連携を行うことができます。
(通常のテーブル同士の連携はこちらの記事で取り上げています。)

テーブル同士にはこのような関係性がある場合があります。

one-to-one:1 対 1
one-to-many:1 対 多
many-to-one:多 対 1
many-to-many:多 対 多

特によく出てくる関係性は、多 対 1 を表した関係です。

多 対 1の関係は次のような状態の時です。

親テーブル(membersテーブル):

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

子テーブル(member_typesテーブル):

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

会員情報テーブルには会員の情報が入っており、その中にtype_idカラムには会員種別を表す数字が入っていたとします。そのtype_idカラムに紐づく会員種別テーブルのid(主キー)には会員種別が割り当てられています。そして一人の会員が入れる会員種別が1種類だとします。

このような関係の時、複数の会員情報が個々の会員種別に紐づいている形になるので、多 対 1の関係と言えます。


Javaでテーブル同士を連携させる時、通常であれば結合のためのSQL文を書く必要がありましたが、Hibernate ORMを使うことでマッピングファイルを設定するだけで自動で連携を行なってくれるようになります。

それでは多 対 1 の関係のマッピング方法をみていきましょう。
こちらの記事で使ったDBのテーブルを、引き続き使用する想定で説明していきます。

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

このようなテーブルの会員種別カラムに別のテーブルを紐付けて会員種別名を表示させます。


■many-to-one:多 対 1 のテーブルのマッピング方法

このようなテーブルでマッピングを行うとします。

親テーブル(membersテーブル):

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

子テーブル(member_typesテーブル):

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

やることとしてはまず親テーブルと子テーブルそれぞれのカラムに対応するdomainクラスを作成します。


◇domainクラスの作成
そえぞれのdomainクラスは次のようになります。

例)親テーブルの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; 削除
	//MemberType用のフィールド
	private MemberType memberType;

	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;
        //	}

	//MemberType用のアクセッサ
	public void setMemberType(MemberType memberType) {
		this.memberType = memberType;
	}
	public void setCreated(Date created) {
		this.created = created;
	}

	public Date getCreated() {
		return created;
	}
	public MemberType getMemberType() {
		return memberType;
	}


}


例)子テーブルのドメインクラス(MemberType.java)

package com.example.mvc.domain;

public class MemberType {
	//フィールド
	private Integer id;
	private String name;

	//アクセッサ
	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;
	}
}

説明:
親テーブルのフィールドに
private MemberType memberType;
というものがあるのに注目してください。

これは親テーブルのtype_idフィールドに当たるものです。
子テーブルとの紐付けとして、type_idの代わりに子テーブル型のフィールドを親テーブルに持たせます。

子テーブル用のdomainクラスでは、子テーブルのカラムに対応したフィールドを持たせます。

domainクラスが準備できたら、次はドメインクラスとテーブルのカラムを紐付ける マッピングファイル を作成します。
(マッピングファイルについてはこちらの記事でも触れています。)

◇マッピングファイルの作成
まずは親テーブルとそれに対応するdomainクラスを紐付けるマッピングファイルを作成します。

例)親テーブルとdomainクラスのマッピングファイル(Member.hbm.xml)
格納先: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">

<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> -->
		<!-- many-to-oneでマッピング -->
		<many-to-one name="memberType" column="type_id" lazy="false" />
		<property name="created" column="created" type="date"></property>
	</class>
</hibernate-mapping>

説明:
マッピングファイルに
<many-to-one name="memberType" column="type_id" />
という行があることに注目してください。
これは多 対 1 の関係性をマッピングするための記述です。

name属性 では、親テーブルのdomainクラスで定義した、子テーブルdomainクラス型(MemberType)のフィールド名(memberType) を指定します。

column属性 では紐付けられる親テーブルのカラム名(type_id)を指定します。

ここで注目して欲しいのが lazy="false" という属性が付与されている点です。ここで指定しているのはデータ取得の方法です。lazyをfalseに設定すると、テーブルに紐づく子テーブルのデータの情報を同時に取得してきてくれるということになります。

自動で取得してくれるので便利ではあるのですが、必要のないデータも取得してきてしまうため、読み込みが遅くなってしまいます。
そのため基本的にはこの設定は行わないことが望ましいですが、ここではこの設定を使った取得を行ってみます。


次にこテーブルのマッピングファイルを作成します。

例)子テーブルとdomainクラスのマッピングファイル(MemberType.hbm.xml)
格納先:src/main/resource/hbm/MemberType.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">

<hibernate-mapping package="com.example.mvc.domain">

	<!-- MemberTypeクラスとmember_typesテーブルを紐付ける -->
	<class name="MemberType" table="member_types">
		<!-- 主キーのマッピング -->
		<id name="id" column="id">
			<generator class="native"></generator>
		</id>
		<!-- その他のカラムのマッピング -->
		<property name="name" column="name" type="string"></property>
	</class>
</hibernate-mapping>


◇Bean定義ファイルの修正
Bean定義ファイルにこテーブルに紐づいたプロパティファイルの場所を追加します。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing
		infrastructure -->

	<!-- Enables the Spring MVC @Controller programming model -->
	<mvc:annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving
		up static resources in the ${webappRoot}/resources directory -->
	<mvc:resources mapping="/resources/**"
		location="/resources/" />
	<mvc:resources mapping="/css/**" location="/css/" />
	<mvc:resources mapping="/images/**" location="/images/" />
	<mvc:resources mapping="/js/**" location="/js/" />


	<!-- Resolves views selected for rendering by @Controllers to .jsp resources
		in the /WEB-INF/views directory -->
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/" />
		<property name="suffix" value=".jsp" />
	</bean>

	<context:component-scan
		base-package="com.example.mvc" />

	<!-- Hibernate利用準備 -->
	<!-- データソースの指定 -->
	<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>
				<value>hbm/MemberType.hbm.xml</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.MySQLInnoDBDialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.format_sql">true</prop>
			</props>
		</property>
	</bean>

	<!-- トランザクションマネージャーの設定 -->
	<!-- Transaction Manager の設定 -->
	<bean id="txManager"
		class="org.springframework.orm.hibernate5.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>
	<tx:annotation-driven
		transaction-manager="txManager" />
</beans>

説明:
Hibernate ORM の設定でmappingResourcesに
<value>hbm/MemberType.hbm.xml</value>
を追記しました。


プロジェクトの実行
プロジェクトを実行して listMemberページに遷移すると、membersテーブルのデータが表示できているのがわかります。紐付けた member_types テーブルの会員情報もテキストで表示されているのが確認できます。

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



それでは次に親テーブルのマッピングで lazy="false" の設定を行わない場合のデータの取得を行います。

親テーブルとdomainクラスのマッピングファイルで、行った
<many-to-one name="memberType" column="type_id" lazy="false" />
の設定を
<many-to-one name="memberType" column="type_id" />
に変更します。

MemberDaoファイルのHQL文をこのように書き換えます。

Before

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

After

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

これでlazy="false"の場合と同じ表示結果になります。



HQLはテーブルの結合や、検索条件や並び替え、データの取得範囲と件数の指定など、通常のSQL文と同じようにさまざなデータの操作が行えます。

ただあまり複雑な操作を行う場合には、SQLの方で書いてしまうというのも一つの手かもしれません。


「Hibernate ORM の利用」については以上です。

この記事でJavaプログラミング実習はいったん終わり、次の授業から課題制作に入ります。

課題制作では、各々制作したいWebアプリケーションを考えて、実際に作っていきます。

最後までお読みいただきありがとうございました。

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