はじめに
ある程度実用的な Java ウエブアプリを作る場合、別のクラスのインスタンスをコレクションで含むエンティティを hibernate で操作する、みたいなことをよくやる。
が、意外にこのためのサンプルが少ない。
簡単なサンプルを作成したので、ちと解説。
モデルの準備
最終的に操作したい親のクラスを Person とした。
この次に Phone クラスを登場させるが、Person と Phone の関係は1対多(OneToMany)で一方向のみ。
hibernate がどのように動作するのか確認したいなら、これで十分でしょう。
@Entity(name = "person")
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(cascade = CascadeType.ALL)
private List<phone> phones = new ArrayList<>();
(以下、ゲッター・セッター)
ただし、フィールド変数に他のクラス(小クラス phone)のリスト phones を含めた。
ここを String とかにすると実用性ないので。
で、phone はこんな感じ。
@Entity(name = "phone")
public class Phone implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String number;
(以下、セッター・ゲッター)
1対多の関係ならば、このクラスに Person の情報はいらないので、これでいい。
mapped by とかそういうややこしいのは要らない。忘れましょう。
ここまででも、ビルドして適当なアプリケーションサーバにデプロイすると(hibernate が適切に設定されているならば)指定したデータベースにテーブルを作ってくれる。

結合テーブル person_phone ができるが、このテーブルで person と phone を関連づけているわけですね。
見た目はスッキリしていないが、確実に動くプログラムを作成するのと hibernate がどの情報を用いて動作するのか把握するのは初学者にとって大事なのでそちらを優先。
いきなり難しいことをやる必要はない。
CRUD 操作
ここまでできたら、CRUD 操作用の REST api を作成。
まず、アプリケーションパスを設定(IDEが勝手に作ってくれると思うけど)。
@ApplicationPath("resources")
public class JAXRSConfiguration extends Application {
}
GET
次に、GET 命令で適当なオブジェクトを永続化させてみよう。
@Path("/personphone")
public class TestPersonPhone {
@PersistenceContext(unitName="適当な名前")
private EntityManager em;
@GET
@Produces(MediaType.TEXT_PLAIN)
@Transactional
public String testPersist() {
Person person = new Person();
Phone phone1 = new Phone();
Phone phone2 = new Phone();
List<Phone> phones = new ArrayList<>();
phone1.setNumber("123");
phone2.setNumber("456");
phones.add(phone1);
phones.add(phone2);
person.setPhones(phones);
em.persist(person);
return "person phone persist";
}
}
デプロイ後、ブラウザで
http://localhost:8080/(プログラム名)/resources/personphone
にアクセスするとブラウザ上では person phone persist が返ってくる。
一方、データベースを覗くと phone テーブルのカラム number に “123”, “456” が入っているはずだ。
POST
これだけだと面白くないので、POST での操作も試してみよう。
上記のプログラムに以下のコードを追加。
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Transactional
public String testPersist2(String json) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person = mapper.readValue(json, new TypeReference<>() {});
em.persist(person);
return "person phone persist post";
}
デプロイ後、例えば、コマンドラインから
curl -i -X POST -H "Content-Type: application/json" --data-binary '{"phones":[{"number":"789"},{"number":"012"}]}' http://localhost:8080/(プログラム名)/resources/personphone
と叩く。
phone テーブルのカラム number に “789”, “012” が入っているはずだ。
jackson でデシリアライズ
POST の方のキモは、jackson のオブジェクトマッパーを用いて、TypeReference を指定してデシリアライズしたところ。
person = mapper.readValue(json, new TypeReference<>() {});
ここを
person = mapper.readValue(json, Person.class);
などとしてしまうと、コレクション部分(phones)を無視してしまう。
独自の型情報を使った場合、それを使った旨を言語処理系に教えないと動かないのは当たり前なんだが、どういうわけかここら辺に言及した記事はあまり見かけなかった。
おわりに
いかがでしたでしょうか?
簡単な hibernate 〜 CRUD 操作に加えて、実用的な curl コマンドの使い方や jackson の小技を入れてみました。
ところで、ワイがこういった書き方をする理由は、Java とりわけ EE 環境の解説記事に違和感を感じているから。
以前にも JavaEE/JakartaEE ではけっこう有名とされている人の記事に間違いを見つけたが、ああいう間違え方は他の言語使用者からするとありえんと思う。
筋の悪い間違え方を続けていると通常は次第に相手にされなくなっていくと思うが、本邦の JAVAer はなぜかそういうセンスのない人の言動を「あの人が言ったから」ということで重宝がる傾向があるようだ。
権威を好んでロジックをないがしろにする文化に継続的な発展はありえませんよ。