学習14日目:便利なフレームワーク「Hibernate」。

今日の学習時間。

  • Day:14
  • Today:6h
  • Total:94h

学習内容について。

「DTOとDAO」を使ってスッキリさせる。

  • DTO(Data Transfer Object)
    • データベースの各カラムに対応した変数をもつクラス
  • DAO(Data Access Object)
    • データベースへ接続する部分やSQL文を実行する部分を担うクラス

下記コードがDTOの役割。本来はすべてのカラムをプロパティとして持たせるべきであるが長いので省略した。

package dbSample;

public class Country {
    private String name;
    private Integer population;

    public String getName() {
        return name;
    }

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

    public Integer getPopulation() {
        return population;
    }

    public void setPopulation(Integer population) {
        this.population = population;
    }
}

下記コードはDAOの役割。

package dbSample;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class CountryDAO {
    private Connection con;
    private PreparedStatement pstmt;
    private ResultSet rs;

    public List<Country> getCountryFromName(String name) {
        List<Country> results = new ArrayList<Country>();

        try {
            this.getConnection();

            pstmt = con.prepareStatement("select * from country where Name = ?");

            pstmt.setString(1, name);
            rs = pstmt.executeQuery();

            while (rs.next()) {
                Country country = new Country();
                country.setName(rs.getString("Name"));
                country.setPopulation(rs.getInt("Population"));

                results.add(country);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            this.close();
        }

        return results;
    }

    public void getConnection() throws SQLException, ClassNotFoundException {
        Class.forName("com.mysql.jdbc.Driver");

        con = DriverManager.getConnection(
                "jdbc:mysql://localhost/world?useSSL=false",
                "root",
                "password");
    }

    private void close() {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (pstmt != null) {
            try {
                pstmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

最後に下記コードを作成し、メソッドを実行。

package dbSample;

import java.util.Iterator;
import java.util.List;

public class DbConnectSample06 {

    public static void main(String[] args) {
        CountryDAO countryDAO = new CountryDAO();

        List<Country> countries = countryDAO.getCountryFromName("Aruba");

        Iterator<Country> it = countries.iterator();
        while (it.hasNext()) {
            Country country = it.next();
            System.out.println(country.getName());
            System.out.println(country.getPopulation());
        }
    }
}

/* 実行結果

Aruba
105000

*/

データ構造の異なる「インピーダンス・ミスマッチ」。

  • 関係データベース
    • Relational Data Base:RDB
    • 縦と横の2次元の表でデータを管理
    • 正規化を行うとデータ構造がわかりにくくなる
      • データ管理はしやすくなる
  • オブジェクト指向的なデータ構造
    • 属性情報をモデルにできるのでデータ構造は非常にわかりやすい
    • 複数件のデータの比較をオブジェクトで扱うのに労力がかかる

このように両者は内部構造が大きく異なる(インピーダンス・ミスマッチ)ので下記コードのように変換処理(結果を詰める)が必要になる。

Country country = new Country();
country.setName(rs.getString("Name"));
country.setPopulation(rs.getInt("Population"));

上記コードのような変換処理を一々行うのは面倒なので下記のような方法を用いることもできる。

①:Serializable
  • オブジェクトデータ全体をそのままデータベースに格納するという方法
    • オブジェクトをデータベースへ保存できる形に変換して保存
    • 取り出す場合も、反対の変換処理を行ってオブジェクトとして持つ
  • ①:blob 形式のカラムを持つテーブルをMySQLに作成
  • ②:Country クラスに Serializable インターフェイスを実装
デメリット
  • データベース側からデータを見ることが不可能になるので検索が大変になる
    • blob形式はテキスト形式のデータではないから(バイナリデータ)
②:O/Rマッピング
  • Object / Relational Mappingの略
  • データベースとの連携部分に特化したフレームワーク
    • 問い合わせ結果を詰める作業などを自動で実行してくれる
    • 関係データベースの構造を考える労力が省ける
  • ただし、場合によってはSQL文を使った方が簡単になる場合がある
    • データベースに対する知識が必要
  • フレームワークの文法を学習する必要あり
  • Hibernateは広くスタンダードに用いられているO/Rマッピングのフレームワーク
    • Javaが公式で取り決めているO/Rマッピングの仕様(JPA)に則ったフレームワーク

「Hibernate」とJPAのセットアップ。

STEP
プロジェクトを作成。
STEP
Mavenプロジェクトへ変換。
  • パッケージ・エクスプローラー画面から「構成」→「Mavenプロジェクトへ変換」
  • pom.xmlが作成される
STEP
Hibernateをインストール。
  • pom.xmlの「依存関係」にJDBCドライバとHibernateを追加(下記参照)
  • 入力したらpom.xmlを保存
  • グループId:mysql
  • アーティファクトId:mysql-connector-java
  • バージョン:5.1.45
  • グループId:org.hibernate
  • アーティファクトId:hibernate-core
  • バージョン:5.2.13.Final
STEP
 JPAをセットアップ。
  • mysql-connector-java-5.1.45.jarのファイルパスを確認しておく
  • パッケージ・エクスプローラー画面から「構成」→「JPAプロジェクトへ変換」
ファセット・プロジェクトの変更
  • 構成に「基本JPA構成」を選択
  • プロジェクト・ファセットは「Java」と「JPA」の2つのみにチェック
  • バージョンは、JPAは2.1、Javaは1.8
JPAファセット
  • プラットフォームは「Generic 2.1」
  • JPA実装の型は「ライブラリー構成を無効」
  • 接続は「なし」
  • 永続化クラス管理は「注釈付きクラスをpersistence.xmlに記述」
STEP
persistence.xmlに必要な情報を追記。
  • STEP4を完了するとsrc/META-INFフォルダにpersistence.xmlというファイルが追加される
  • persistence.xmlに必要な情報を追記
    • ファイルを開き、「一般」タブの内容を表示
「一般」タブ

「永続化プロバイダー」に下記を入力。

org.hibernate.jpa.HibernatePersistenceProvider
「接続」タブ
  • トランザクション・タイプを「リソース・ローカル」にする
  • データベース欄のJDBC接続プロパティー欄にある4つの入力項目に下記を記入
  • ドライバー:com.mysql.jdbc.Driver
  • URL:jdbc:mysql://localhost/world?useSSL=false
  • ユーザー:root
  • パスワード:個別のもの
「プロパティー」タブ

以下の項目を追加することで開発作業が便利になる。

名前備考
hibernate.dialectorg.hibernate.dialect.MySQL5DialectMySQL独自のSQL文もHibernateが扱えるようになる
hibernate.show_sqltrue実行したSQLの内容をeclipseのコンソールに表示する
hibernate.format_sql
true表示するSQLを自動で整形してくれる

最後に忘れずファイルを保存。

STEP
「ソース」タブを開いて確認。
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="techacademy_orm" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/world?useSSL=false"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="password"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

上記のようになっていればOK。

Hibernateを使ってみる。

package entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="city")
public class City {
    @Id
    @Column(name = "ID")
    private Integer id;

    @Column(name = "Name")
    private String name;

    @Column(name = "CountryCode")
    private String countryCode;

    @Column(name = "District")
    private String district;

    @Column(name = "Population")
    private Integer population;

    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 String getCountryCode() {
        return countryCode;
    }

    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }

    public String getDistrict() {
        return district;
    }

    public void setDistrict(String district) {
        this.district = district;
    }

    public Integer getPopulation() {
        return population;
    }

    public void setPopulation(Integer population) {
        this.population = population;
    }

}
Cityクラス保存時のエラー対処法
  1. persistence.xmlを再度開く
  2. 一般タブの右側にある「管理クラス」にCityを追加
  3. persistence.xmlを保存

課題:personテーブルからのデータ取得。

HibernateとJPAのセットアップから。。

package entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "person")
public class Person {
    @Id
    @Column(name = "ID")
    private Integer id;

    @Column(name = "Name")
    private String name;

    @Column(name = "Age")
    private Integer age;

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

なぜかエラー連発で悩んだけど、原因は単純にpersistence.xmlの接続タブにあるURLを変更していないことだった。URLは(jdbc:mysql://localhost/world?useSSL=false)のままだったので、このworldの部分をデータベース名に変更する必要がある。

今日の反省と明日の目標。

カリキュラムが始まって以来好調で、特にエラーもなく進んできました。なんだ、プログラミングって簡単じゃないか、とか思ったりして調子に乗ってた部分もあったと思う。しかし、今日は初めてエラーの壁にぶち当たり、1つの問題に1時間近く悩むことになってしまいました。

原因はなんて事のない、ただの確認ミスだった訳だけど、いろいろ試してもデバッグされない焦りが徐々に高まっていくのを感じました。服作りではちょっと縫い合わせがズレても、全体に及ぼす影響は大きくはない。しかし、プログラミングでは1文字の打ち間違いでもエラーになってしまいます。

結局はなぜそのようなコードになるのかを考えながら書くのが、素早くデバッグする上で大切だと思いました。じゃないと、どこが間違えているのか推測ができないので。カリキュラムでは時間の関係上で説明が省かれている箇所が多々あるので、自分で意味を調べないとまた同じ事態に陥ってしまうかもしれません。

全てのコードや設定には理由がある。それがプログラミングの面白さでもあると再認識した今日のエラーでした。

明日はWebアプリケーションを制作する上で重要な「インターネット」について学習していきます。

閉じる