JPAわけわかんねーと思い、触りはじめてから3か月ぐらいたちます。
いまだに細かい部分はよくわかっていないことは多いのですが、全体像というか何となくどういうものかはわかり始めてきたいのでまとめたいと思います。
JPAとは
一言でいうとO/Rマッピングツールであり、下記のような特徴があります。
- RDB(リレーショナルデータベース)とJavaのPOJO(普通のクラス)の自動変換を行う
- 上記の仕様からJavaのクラスがそのまま使える
- SQL文を書く必要がない
- データベースの規格に依存しない
通常のプログラムではプログラムにSQLを書いてデータベースとアクセスすることが一般的でした。
ところがJPAでは1テーブルをエンティティというPOJOで管理し、これがそのままテーブルに対応します。
エンティティ
下記がエンティティとテーブルの対応例です。
MySQLのテーブルを作成するSQL
1 2 3 4 5 |
CREATE TABLE `person` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, PRIMARY KEY (`id`) ) |
上記テーブルから生成したエンティティ(IDEから自動生成することが可能です。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
@Entity @Table(name = "person") @XmlRootElement @NamedQueries({ @NamedQuery(name = "Person.findAll", query = "SELECT p FROM Person p"), @NamedQuery(name = "Person.findById", query = "SELECT p FROM Person p WHERE p.id = :id"), @NamedQuery(name = "Person.findByName", query = "SELECT p FROM Person p WHERE p.name = :name")}) public class Person implements Serializable { private static final long serialVersionUID = 1L; @Id //プライマリーキー @GeneratedValue(strategy = GenerationType.IDENTITY) //自動生成 @Basic(optional = false) @Column(name = "id") private Integer id; @Basic(optional = false) @NotNull @Size(min = 1, max = 20) @Column(name = "name") private String name; public Person() { } public Person(Integer id) { this.id = id; } public Person(Integer id, String name) { this.id = id; this.name = name; } 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; } @Override public int hashCode() { int hash = 0; hash += (id != null ? id.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof Person)) { return false; } Person other = (Person) object; if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { return false; } return true; } @Override public String toString() { return "com.member.ejb.Person[ id=" + id + " ]"; } } |
複雑なSQLをかかずともエンティティを操作するだけで、SELECT,INSERT,UPDATE,DELETEに相当する作業ができます。
プライマリーキーの性質などもエンティティにアノテーションを付けるだけ表せますし、IDEを使えば、データベースからエンティティを自動生成できます。
(SQLに似たJPQLを使用できますので従来の通り、SQL文チックなものを書くことは可能です。SQLでできることは大抵JPQLでもできますがJPQLではlimit句が使えません・・・(涙))
エンティティマネージャー
エンティティがテーブルだとするとそのエンティティを管理し、SELECT,INSERT,UPDATE,DELETEに相当する指示をだすのがエンティティマネージャーです。
通常のプログラムではデータベースの接続やCRUD系の処理を1つのクラスに書いておくことが一般的ですが、それに相当します。(実際のDBとのアクセスはJPAそのものが行います。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@Stateless public class PersonFacade { //ここがデータベースとの接続を表します。 @PersistenceContext(unitName = "com_person_war_1.0-SNAPSHOTPU") private EntityManager em;//エンティティマネージャーです。 //新規追加 public void create() { em.persist(person); } //全件取得 public List<Person> findAll() { Query query = em.createNamedQuery("Person.findAll", Person.class); List<Person> personList = query.getResultList(); return personList; } //idで取得 public Person find(int id) { return em.find(Person.class, id); } } |
上記のように追加と取得だけ書いてみましたが非常に簡単にかけるのが特徴です。
いろいろカスタマイズするとわからんことが多くて大変ですが・・・
ちなみにエンティティマネージャーの命令ですが、下記のようなものが一般的です。
persist
管理対象としてエンティティに追加
merge
管理対象となっているエンティティとマージ
find
エンティティの取得
remove
管理対象の中から指定したエンティティを削除
detach
管理対象から除外
よくあるミスがpersiste=insert,merge=updateですね。
persistというのはエンティティマネージャーの管理下に入っただけでこの瞬間にinsertはされていないです。
たとえば
idとname,ageのテーブルがあったとして
1 2 3 |
Person person = new Person("hoge",33) //name=hoge,age=33です。 entityManage.persist(person); person.setName("foo"); |
というコードを書いた場合、新規にinsertされるレコードのnameはhogeではなくfooです。
実際にコミットされた時点で初めてinsertが行われます。あくまで管理対象となる、ということだけ覚えておきましょう。
ちなみにJavaEEとして動かす場合は、コミット、ロールバックなどはすべてアプリケーションサーバーが自動で行います。
また永続化という言葉の意味がよくわからなかったのですが、これはメモリなど揮発性の高い記録媒体への記録ではなく、ファイルやDBなど,プログラム終了後もデータが失われないような場所へのデータ保存を指します。
下記のリンクなどでは詳細な説明やエンティティマネージャーが行うことなどを図解入りで紹介しており、理解に役立ちました。