現在の開発ではデータベースへの接続ではJPAを使っています。このJPAがなかなか曲者でいろいろと詰まることがありましたので解説させていただきます。
Contents
JPAとは?
定義を検索するとネットでも様々な定義がでてきますね。ほとんどが厳密な理解を求めるので理解をするのが難しいですね。(汗)
厳密な理解でなくてもいいので「とりあえず使える」というレベルにまでいくために必要な理解を書かせていただきます。まとめると下記が要点です。
- リレーショナルデータベースを扱うJavaSEおよびJavaEEのフレームワーク
- Javaのテーブルをエンティティというクラスで扱う(1テーブル、1エンティティ)
- エンティティを操作するためにエンティティマネージャーというクラスを使う
- 設定ファイルをpersistence.xmlという設定ファイルで定義する
メリット
- Javaのクラスをそのままテーブルとして使えるため、SQLを書かなくて良い(JPQLというSQLににたJava用のSQLがあり、かけないわけではありません。)
- 上記のため、DBによる仕様の差異を吸収できる
などがあげられます。
デメリット
- 学習コストが高い
(ネットをみてもそれほど情報がでてるわけではないので大変です。) - 既存のDBを書き換えるときに特に不便
(新規ならばいいですが、既存の場合、JPQLの限界などもあり、完全に移行するのが難しいと思います。)
ざっとこんな感じでしょうか。はまりポイントが結構あったりしてすすまなくなるときが怖いですね・・・
実サンプル
とりあえず1テーブルの簡単なときにどのように書くかを下記に記します。
構成ファイル&開発環境
Controller.java
ProductDB.java(これがいわゆるエンティティマネージャーになります。)
Product.java(これがエンティティです。)
Database :MySQL5.6
IDE:Netbeans8.0.2
DBの作成
まずMySQLでDBを定義しましょう。実はJPAからテーブルを作成することもできますが、こちらのほうがスムーズかと思います。
1 2 3 4 5 6 7 8 9 |
//データベース作成 CREATE DATABASE product_db DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; //テーブル作成 CREATE TABLE `product` ( `id` int(11) NOT NULL AUTO_INCREMENT, `productName` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
persistence.xmlの作成
ここでpersistence.xmlつまりDBとの接続情報を作成します。
Netbeansを使っていれば
プロジェクトフォルダで右クリック→新規ファイル→持続性ユニットを選びます。
ウィザードが開きますので
「データベース接続」で「データベースの新規接続」を選びます。(これ以外はディフォルトでいいと思います。)
ここでドライバをMySQLにし、設定情報を入力する画面になりますので
ホストやポートデータベース名、ユーザー名、パスワードをいれましょう。
この時点で「接続をテスト」のボタンで接続が確認できます。
persistence.xml ユーザー名/パスワードはroot/なしです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="com_SuperScraping_jar_1.0-SNAPSHOTPU" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/product_db?zeroDateTimeBehavior=convertToNull"/> <property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="javax.persistence.jdbc.password" value=""/> <property name="javax.persistence.schema-generation.database.action" value="create"/> </properties> </persistence-unit> </persistence> |
エンティティの作成
プロジェクトフォルダで右クリック→新規ファイル→持続性→データベースからのエンティティクラス
でウィザードの通りに選んでいけば作成できます。
最初のうちは設定は全てディフォルトでいきましょう。
Product.java
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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
package com.entity; import java.io.Serializable; import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.xml.bind.annotation.XmlRootElement; @Entity @Table(name = "product") @XmlRootElement @NamedQueries({ @NamedQuery(name = "Product.findAll", query = "SELECT p FROM Product p"), @NamedQuery(name = "Product.findById", query = "SELECT p FROM Product p WHERE p.id = :id"), @NamedQuery(name = "Product.findByProductName", query = "SELECT p FROM Product p WHERE p.productName = :productName")}) public class Product 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 = 255) @Column(name = "productName") private String productName; public Product() { } public Product(Integer id) { this.id = id; } public Product(Integer id, String productName) { this.id = id; this.productName = productName; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } @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 Product)) { return false; } Product other = (Product) 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.superscraping.app.Product[ id=" + id + " ]"; } } |
エンティティマネージャーの作成
あとは実際にエンティティを操作するエンティティマネージャーにメソッドをかいてあげればOKです。
とりあえず新規作成と全件選択だけかいてあげましょう。
ProductDB.java
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 |
package com.em; import com.superscraping.entity.Product; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; public class ProductDB { private EntityManagerFactory fac; private EntityManager em; private EntityTransaction tx; public ProductDB(){ fac = Persistence.createEntityManagerFactory("com_SuperScraping_jar_1.0-SNAPSHOTPU"); em = fac.createEntityManager(); tx = em.getTransaction(); } public void create(Product product){ tx.begin(); em.persist(product); tx.commit(); } public List<Product> getAll(){ return em.createQuery(" SELECT p FROM Product p ",Product.class).getResultList(); } } |
あとはContollerでProductDBをインスタンス生成し、createやgetAllを呼び出します。
特にcreateですが、引数がProductになっていることに注意しましょう。
エンティティをインスタンスしてその中に値をセットし、エンティティマネージャーの処理を走らせます。
自分は最初JavaEEから入りましたが、JavaEEだと
- エンティティマネージェーのインスタンス生成は不要
- トランザクションの処理自体不要
なんですが、JavaSEで使う場合にはこちらで書いてあげないといけません。ここがはまりポイントでしたね・・・