2017年度 Java 永続化フレームワークについての考察(8) Hibernate5

#前ポスト
- [2017年度 Java 永続化フレームワークについての考察(1)](http://qiita.com/shiroma_yuki/items/8d434862bba072bff264)
- [2017年度 Java 永続化フレームワークについての考察(2) Doma2](http://qiita.com/shiroma_yuki/items/d0e3b319626f4278fc6f)
- [2017年度 Java 永続化フレームワークについての推測(3) Reladomo](https://qiita.com/shiroma_yuki/items/1e3d83202afa6cf5dbef)
- [2017年度 Java 永続化フレームワークについての考察(4) jOOQ](https://qiita.com/shiroma_yuki/items/1f64cac07afc745579a5)
- [2017年度 Java 永続化フレームワークについての考察(5) Iciql](https://qiita.com/shiroma_yuki/items/42e2cf97076c964e8629)
- [2017年度 Java 永続化フレームワークについての考察(6) Ebean](https://qiita.com/shiroma_yuki/items/283c89ad13f90e25c0d5)
- [2017年度 Java 永続化フレームワークについての考察(7) EclipseLink](https://qiita.com/shiroma_yuki/items/5eca0c61d4a7e7c54886)

#前置き
隙間時間を利用して書いているので精度の甘い箇所があれば、ご指摘いただけると幸いです。
まずEOLであるものは除外。
有償の機能については検討対象に含むが、実際に使用はしません、懐の問題で。
「まぁ、たぶん明記されてなくてもPostgreならいけるだろ」ぐらいの間抜けな理由で使用DBはPostgreに固定。

#環境
- PostgreSQL 9.6.3
- [5.2 series - Hibernate ORM](http://hibernate.org/orm/releases/5.2/)
    - hibernate-core:5.2.12.Final
    - hibernate-entitymanager:5.2.12.Final
    - hibernate-java8:5.2.12.Final
    - hibernate-jpamodelgen:5.2.12.Final
- Java 64bit 8u144

#テーブル構成
![employeeテーブルからpostテーブルにID紐づけ](https://qiita-image-store.s3.amazonaws.com/0/48666/ec265f75-ad54-4f56-a5f8-2b460723047a.png)


#対応範囲
|ORM|Transaction|Data Model|DSL|
|:-:|:---------:|:--------:|:-:|
|○|○|※|○|
○:対応
×:非対応
※:多対多の関係性である場合、関連テーブルの作成を行う機能がある

#所感
- エンティティの自動生成がないのツラい
- 設定ファイルが重い、実に重い
- 旧版まではあったマッピングファイルのclass直下one-to-many要素がないので迷った
    - bagとかsetとかのコレクション系要素の子要素に移っている、まぁ、確かにその通りだ

#サンプル
##単テーブル検索
###全件検索
```Main.java
// JPQLを最初に推してくるチュートリアル……しかも型安全じゃないの……
session.createQuery("from Employee").list().stream().forEach(o -> {
    Employee e = (Employee)o;
    Main.sysout(e);
});

// CriteriaBuilderでも良いのに、チュートリアルなんなの……
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Employee> query = builder.createQuery(Employee.class);
Root<Employee> root = query.from(Employee.class);
session.createQuery(query.select(root).select(root)).getResultList().stream().forEach(Main::sysout);

// EntityManagerからでもJPQLを推してくるチュートリアル……
em.createQuery("from Employee", Employee.class).getResultList().stream().forEach(Main::sysout);

// やっぱりCriteriaBuilderで良い、チュートリアルなんなの……
CriteriaBuilder builder2 = em.getCriteriaBuilder();
CriteriaQuery<Employee> query2 = builder2.createQuery(Employee.class);
Root<Employee> root2 = query2.from(Employee.class);
em.createQuery(query2.select(root2).select(root2)).getResultList().stream().forEach(Main::sysout);
```
```Main#sysout(Employee).java
public static void sysout(Employee e) {
    System.out.format("%1s: %2s, %3s, %4s\n", e.id, e.first_name, e.middle_name, e.last_name);
}
```

###主キー検索
```Main.java
// session使う版
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Employee> query = builder.createQuery(Employee.class);
Root<Employee> root = query.from(Employee.class);
session.createQuery(query.select(root).select(root).where(builder.equal(root.get(Employee_.id), 1L)))
        .getResultList()
        .stream()
        .forEach(Main::sysout);

// EntityManager使う版
CriteriaBuilder builder2 = em.getCriteriaBuilder();
CriteriaQuery<Employee> query2 = builder2.createQuery(Employee.class);
Root<Employee> root2 = query2.from(Employee.class);
em.createQuery(query2.select(root2).select(root2).where(builder2.equal(root2.get(Employee_.id), 1L)))
        .getResultList()
        .stream()
        .forEach(Main::sysout);
```

##テーブル結合
```Main.java
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Employee> query = builder.createQuery(Employee.class);
Root<Employee> root = query.from(Employee.class);
session.createQuery(query.select(root).select(root).where(builder.equal(root.get(Employee_.id), 1L)))
        .getResultList()
        .stream()
        .peek(Main::sysout)
        .map(e -> e.posts)
        .flatMap(ps -> ps.stream())
        .forEach(Main::sysout);

CriteriaBuilder builder2 = em.getCriteriaBuilder();
CriteriaQuery<Employee> query2 = builder2.createQuery(Employee.class);
Root<Employee> root2 = query2.from(Employee.class);
em.createQuery(query2.select(root2).select(root2).where(builder2.equal(root2.get(Employee_.id), 0L)))
        .getResultList()
        .stream()
        .peek(Main::sysout)
        .map(e -> e.posts)
        .flatMap(ps -> ps.stream())
        .forEach(Main::sysout);
```
```Main#sysout(Post).java
public static void sysout(Post p) {
    System.out.format("\t%1s: %2s, %3s\n", p.id, p.employee_id, p.name);
}
```
```Employee.java
@Entity
@Table(name="employee")
public class Employee {
    @Id
    public long id;
    public String first_name;
    public String middle_name;
    public String last_name;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "id", referencedColumnName = "employee_id", table = "post")
    public List<Post> posts = new ArrayList<>();

    public long getId() {return this.id;}
    public void setId(long id) {this.id = id;}
    public String getFirst_name() {return this.first_name;}
    public void setFirst_name(String first_name) {this.first_name = first_name;}
    public String getMiddle_name() {return this.middle_name;}
    public void setMiddle_name(String middle_name) {this.middle_name = middle_name;}
    public String getLast_name() {return this.last_name;}
    public void setLast_name(String last_name) {this.last_name = last_name;}
    public List<Post> getPosts() {return this.posts;}
    public void setPosts(List<Post> posts) {this.posts = posts;}
}
```
```Post.java
@Entity
@Table(name = "post")
public class Post {
    @Id
    public long id;
    public long employee_id;
    public String name;

    public long getId() {return this.id;}
    public void setId(long id) {this.id = id;}
    public long getEmployee_id() {return this.employee_id;}
    public void setEmployee_id(long employee_id) {this.employee_id = employee_id;}
    public String getName() {return this.name;}
    public void setName(String name) {this.name = name;}
}
```
```Employee.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="hibernate.models">
    <class name="Employee" table="employee">
        <id name="id" column="id"/>
        <property name="first_name" column="first_name"/>
        <property name="middle_name" column="middle_name"/>
        <property name="last_name" column="last_name"/>
        <bag name="posts" table="post">
            <key column="employee_id"/>
            <one-to-many class="Post"/>
        </bag>
    </class>
</hibernate-mapping>
```
```Post.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="hibernate.models">
    <class name="Post" table="post">
        <id name="id" column="id"/>
        <property name="employee_id" column="employee_id"/>
        <property name="name" column="name"/>
    </class>
</hibernate-mapping>
```

#要点
- ほぼJPA実装
- 設定がヘビーなので専門家がいないと安心して運用はできない気がする
- マッピングXMLに合わせてDDLを発行したりできる、無論選択できるけど楽なような怖いような……

#ハマったポイント
- 「JPA実装だしEclipseLinkと同じでいけるよね」
    - 動かない
        - そもそもマッピングXMLとか書いてなかった
- 「JPAのアノテーションと同じ感じでいけるよね」
    - 前述の通り、one-to-manyがプロパティと同レベルじゃない、まずはDTDとかの読込が必要

#後ポスト
まだない。

#参考記事
- [Hibernate Core リファレンスガイド](https://access.redhat.com/documentation/ja-jp/jboss_enterprise_application_platform/5/html/hibernate_core_reference_guide/)