どうもこんにちは。シンジです。
皆さんは、ORマッピングという言葉は知ってますか?テーブルとオブジェクトをマッピングしてくれるアレです。
今回はJavaEEの仕様の1つでORマッピングの役割を担うJPAについて、お話していこうと思います。
JPAといっても、あくまでも永続化管理のためのAPI仕様なので、実際はJPAの参照実装である、Hibernate、EclipseLinkを使って行います。
正直な話、最初はとっつきにくかったです。学習コストもかかりました。でも、理解していくにつれて、JPAの設計思想について興味を持つようになりました。
あと、JPAはとーーーーっても奥が深いんです。ちゃんと理解して使わないと痛い目に合いますので、注意してください。
では、早速初めていきましょう。
説明していくにあたり、以下のテーブルを使っていきます。
準備編
※JPAを使う上で環境構築が必要になりますが、今回のメインからはずれてしまうので、割愛させていただきます。
エンティティを作成しよう
まず、上記で定義したテーブルのエンティティを作成します。
ユーザマスタのエンティティは以下のようになります。
@Table(name="m_user") @Entity public class User{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; private Integer age; ~getter/setter~ }
@ マークがついているものがいくつか存在しますが、これをアノテーションと呼びます。
そのアノテーションについて、いくつか説明していきます。
1行目: @Table(name=”テーブル名”)
エンティティ名と実テーブル名の名前が異なる場合のみ定義が必要にになります。
今回の場合、エンティティが「User」でテーブル名が「m_user」なので定義してあります。(Locationも同様)
2行目: @Entity
必ず指定が必要です。これがないと、JPAが始まりません。
4行目: @Id
エンティティクラス内のフィールドのいずれかに指定する必要があります。
5行目: @GeneratedValue(strategy = GenerationType.IDENTITY)
@Id が指定されたフィールドの値の採番方式を指定します。これを指定しておくことで、自分でIDをセットして登録という手間がなくなります。
勤務地マスタのエンティティは以下のようになります。
@Table(name="m_location") @Entity public class Location{ @Id private Integer id; private String name; private String address; @Column(name="tel_no") private String telNo; ~getter/setter~ }
11行目: @Column(name=”カラム名”)
@Table と同様、フィールド名とカラム名が異なる場合のみ定義します。
エンティティの定義方法や、アノテーションについて述べましたが、実はアノテーションを定義する上でいくつかの条件あるので、ここでまとめたいと思います。
・クラス内のフィールドのいずれかに@Idが定義されていること。
・引数なしのコンストラクタを持つこと。
* 引数ありのコンストラクタを定義した場合は、明示的に引数なしのコンストラクタも定義が必要。
・フィールドをカプセル化(privateのフィールド、getter、setter)すること。
・final修飾子をクラスやフィールドに使わないこと。
次は作成したエンティティを使って、CRUD処理についてみていきましょう。
検索編
ユーザのデータを全件取得したい
// EntityManagerのFactoryクラスをPersistence.xmlで定義したユニット名を使って生成 EntityManagerFactory emf = Persistence.createEntityManagerFactory("ユニット名"); // EntityManagerの生成 EntityManager em = emf.createEntityManager(); // クエリの生成 TypedQuery q = em.createQuery("SELECT u FROM User u",User.class); // 抽出 return q.getResultList();
SQLみたいな記述がありますが、これはSQLではなくJPQL(Java Persistence Query Language)と呼ばれるクエリ使って取得します。JPQLを使うことで、オブジェクトとして扱うことができます。もちろんSQLで記述することも可能です。
その場合はcreateNativeQueryメソッドを使いましょう。
ユーザのデータの中でキー(id)が一致するデータを取得したい
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ユニット名"); EntityManager em = emf.createEntityManager(); Users user = em.find(User.class,id);
EntityManagerのメソッドに「find」というメソッドがあるので、これを使います。
第2引数で指定できるのは、 @Id が指定されているフィールドの値のみです。
ユーザの中で年齢が30以上のデータを取得したい
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ユニット名"); EntityManager em = emf.createEntityManager(); List users = em.createQuery("SELECT u FROM User u WHERE u.age >= 30").getResultList();
@Id が指定されているフィールド以外で、条件を設定したい場合はcreateQueryを使います。
JPQLの記述でWHERE句を書いて、必要な条件を書きます。
結合編
関連テーブルを結合して取得する方法は、前に述べた検索処理とほぼ同じです。異なるのはエンティティクラスに定義するアノテーションです。アノテーションをつけることで、結合を意識することなく取得することができます。
ユーザ情報を取得する際に勤務地情報も取得したい
Userエンティティクラスに、Locationエンティティクラスのフィールドを定義します。
@Table(name="m_user") @Entity public class User{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; private Integer age; @ManyToOne private Location location; ~getter/setter~ }
関連するエンティティクラスをフィールドとして定義します。関連元テーブル(今回の場合User)から見た関連先テーブル(Location)の関連がN対1なので、アノテーションに @ManyToOne を定義します。
これで、Userの検索処理したときに、Locationも取得できます。
ユーザごとの勤務地情報を取得する場合は以下のようにします。
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ユニット名"); EntityManager em = emf.createEntityManager(); TypedQuery q = em.createQuery("SELECT u FROM User u",User.class); ListuserList = q.getResultList(); for(User user : userList){ // 勤務地情報取得 Location location = user.getLocation(); }
ある勤務地に所属するユーザを取得したい
LocationエンティティクラスにUserのエンティティクラスを定義します。
@Table(name="m_location") @Entity public class Location{ @Id private Integer id; private String name; private String address; @Column(name="tel_no") private String telNo; @OneToMany(mappedBy="location") private List userList; ~getter/setter~ }
関連元テーブル(今回の場合Location)から見た関連先テーブル(User)の関連が1対Nなので、アノテーションに @OneToMany を定義します。あと、ここで注意してほしいのが、双方向に関連アノテーションを定義した場合、 @OneToMany を定義した方に「mappedBy」を定義する必要があります。
mappedByに指定する内容は、関連先エンティティクラス内に定義している関連元エンティティクラスのフィールド名を定義します。
取得した勤務地情報からユーザ情報を取得する場合は以下のようにします。
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ユニット名"); EntityManager em = emf.createEntityManager(); Location location = em.find(Location.class,1); // ユーザ情報取得 List userList = location.getUserList();
データ更新編
ユーザのデータを新規登録したい
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ユニット名"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 勤務地情報取得 Location location = em.find(Location.class,1); // 登録するユーザ情報 User user = new User(); user.setName("デーコム"); user.setAge(30); user.setLocation(location); // 登録 em.persist(user); tx.commit();
新規で作成したエンティティを登録する場合は「persist」を使います。
あるユーザの勤務地を変更したい
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ユニット名"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 変更先の勤務地情報を取得 Location location = entityManager.find(Location.class,2); // 更新対象のユーザ情報取得 User user = em.find(User.class,1); // 変更先の勤務地をセット user.setLocation(location); // 更新 em.merge(user); tx.commit;
変更したエンティティで更新する場合は「merge」を使います。
あるユーザを削除したい
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ユニット名"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 削除対象のユーザ情報取得 User user = em.find(User.class,1); // 削除 em.remove(user); tx.commit;
対象エンティティを削除する場合は「remove」を使います。
まとめ
JPAでデータベース処理を行う際は、EntityManagerを使って行います。EntityManagerが持つ、今回利用したメソッドは以下のとおりです。
今回はEntityManagerの簡単な使い方をメインにお話しましたが、次回は永続化コンテキストを絡めたお話をしていこうと思います。
永続化コンテキストを利用してこそ、JPAを使う価値があります。(内容的には難易度はあがりますが。。。)
それでは素敵なJPAライフを。