33
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

あなたがやってるDB周りの実装は無駄かもしれませんよ?JPAでSQLいらず。

Posted at

この投稿はJavaEEのアーキテクチャを再興する。(まとめページ)に属する投稿です。他の記事もお楽しみあれ。

JavaでWebシステムを作るとき、たいていはRDBも触ると思います。JavaでEntityを作成して、それと同じ形でDBのCreate文を作成して、フィールド名かカラム名をTypoしてバグを埋め込む。みなさんも一度は体験したことがあるかと存じます。
JPA2.0を使うと、JPAがEntityからRDBのテーブルを作成してくれるので、Create文を見ることなく、RDBにオブジェクトを保存できます。db:migrateとかコマンドも打つことなく、JPAがすべてを整えてくれます。便利。

本稿では、システムを作る上では絶対に出てくる、テーブル同士の関連をJPAで表現するためにはどうするかをお伝えします。
とはいえ、Javaの普通の関連を作り、アノテーションを貼るだけです。前置きよりも簡単に読めるかと思います。

なお、JPAの簡単な説明はこちらをご覧ください。

サンプル

サンプルはGithubのこちらに乗せてあります。
RelationMain.javaで、CartItemというクラスを保存、取得を行っています。
サンプルではJPAの実装として、EclipseLink、RDBにH2を使用しています。

解説

まずは保存対象のクラスを見ていきます。

Item.java抜粋(関連を定義している部分)
@ManyToOne(cascade = CascadeType.ALL)
private Cart cart;
Cart.java抜粋(関連を定義している部分)
@OneToMany(mappedBy = "cart", cascade = CascadeType.ALL)
private List<Item> items = new ArrayList<Item>();

ItemとCartそれぞれでお互いを参照しています。また、@ManyToOne@OneToManyでそれぞれの関係性を定義しています。cascadeは、関連付けを保存時や参照時に有効にするかを選択します。ここではすべての場合で関連付けを有効にしたいので、CascadeType.ALLを指定しています。また、mappedByでは関連先、Itemのフィールド名を指定しています。
関連付けはこれだけ。簡単ですね。

RelationMain.java抜粋(保存時)
Cart cart = new Cart();

cart.addItem(new Item("ITEM1"));
cart.addItem(new Item("ITEM2"));

cart.setKey(UUID.randomUUID().toString());

em.persist(cart);

cartを作成し、itemを関連付けています。Cart#addItemでは、Itemとcartを相互に関連付けています。
これだけでDBに外部キー関連が保存されます。

RelationMain.java抜粋(Cart検索時)
List<Cart> carts = em.createQuery(cq.select(r)).getResultList();
System.out.println("carts length = " + carts.size());
for (Cart cart : carts) {
    System.out.println("# cart id = " + cart.getId());
    List<Item> items = cart.getItems();
    for (Item item : items) {
        System.out.println("\t item id = " + item.getId() + ", name = " + item.getName() + ", cartId");
    }
}

Cartを検索すると、Itemも一緒に取得できることがわかります。
逆に、Itemを検索しても、Cartが取得できます。

RelationMain.java抜粋(Item検索時)
List<Item> items = em.createQuery(query.select(r)).getResultList();
System.out.println("items length = " + items.size());
for (Item item : items) {
    System.out.println("item cartId = " + item.getCart().getId() + " id = " + item.getId() + ", name = " + item.getName());
}

RDBを使っていることを意識せずに保存、取得ができていますね!

更に詳しく見てみる。

JPAは便利すぎます!しかしながら、どういう処理をしているかとても気になるので、中を見てみます。

テーブル定義

JPAでは勝手にテーブルを作ってくれています。どのようなテーブルができているか見てみます。

テーブル定義
CREATE CACHED TABLE PUBLIC.ITEM(
    ID BIGINT NOT NULL,
    NAME VARCHAR,
    CART_ID BIGINT
);
CREATE PRIMARY KEY PUBLIC.PRIMARY_KEY_2 ON PUBLIC.ITEM(ID);
eALTER TABLE PUBLIC.ITEM ADD CONSTRAINT PUBLIC.CONSTRAINT_2 PRIMARY KEY(ID) INDEX PUBLIC.PRIMARY_KEY_2,  
JCREATE CACHED TABLE PUBLIC.CART(
    ID BIGINT NOT NULL,
    KEY VARCHAR
);
CREATE PRIMARY KEY PUBLIC.PRIMARY_KEY_1F ON PUBLIC.CART(ID);
ALTER TABLE PUBLIC.CART ADD CONSTRAINT PUBLIC.CONSTRAINT_1F PRIMARY KEY(ID) INDEX PUBLIC.PRIMARY_KEY_1;
CREATE INDEX PUBLIC.FK_ITEM_CART_ID_INDEX_2 ON PUBLIC.ITEM(CART_ID);
ALTER TABLE PUBLIC.ITEM ADD CONSTRAINT PUBLIC.FK_ITEM_CART_ID FOREIGN KEY(CART_ID) INDEX PUBLIC.FK_ITEM_CART_ID_INDEX_2 REFERENCES PUBLIC.CART(ID) NOCHECK;

ITEMテーブルにCART_IDというカラムができて、ALTER TABLEでそこに外部キーを張っているのがわかります。

Select文

EclipseLinkのログをアプリの出力と交えて見てみます。

ログ
query cart start
[EL Fine]: sql: 2014-06-27 23:09:07.716--ServerSession(253338012)--Connection(1998581014)--Thread(Thread[main,5,main])--SELECT ID, KEY FROM CART
carts length = 3
# cart id = 1
[EL Fine]: sql: 2014-06-27 23:09:07.721--ServerSession(253338012)--Connection(1998581014)--Thread(Thread[main,5,main])--SELECT ID, NAME, CART_ID FROM ITEM WHERE (CART_ID = ?)
	bind => [1]
	 item id = 2, name = ITEM1, cartId
	 item id = 3, name = ITEM2, cartId

Cartの検索はSELECT ID, KEY FROM CARTで普通に行っていますね。興味深いのは、この時はまだItemは取得しておらず、cart.getItems()の時に初めてSELECT ID, NAME, CART_ID FROM ITEM WHERE (CART_ID = ?)を行っているのがわかります。これで余分なデータ取得をすることがなくてハッピーですね。

JPAはとても簡単。使わない手はありません。

DBアクセスのような、いつもいつも同じコードを書くような実装は、できるだけ楽をして作って、UXや便利な機能などに力を入れ、ものづくりの価値を上げたいものです。
そのためにも、JPAを使ってみてはいかがでしょうか?

33
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?