久しくさぼってたSpring Frameworkですが、NetBeans 6.7 Beta版が出たのでつっついてみたいと思います。
今日のお題はSpring Framework + JPAを試してみようと思います。
データベースの作成
とりあえず、お試しようのデータベースを作成します。
SpringとJPAの連携がメインなので、DBはシンプルに外部キーとかのない1テーブルのみのDBにします。
サーバウィンドウのデータベースの下のJavaDBで右クリックしてデータベースを作成を選択します。
DB名、ユーザ名、パスワードすべてjpaeduのデータベースを作成します。
DBが作成されると、DBの接続も出来るので右クリックして接続を選択します。
APPの下の表の右クリックメニューからコマンドを実行を選択します。
以下のSQLを打ち込んで実行してテスト用のテーブルを作成します。
create table Person (
id int primary key,
name varchar(255));
以上でDBの作成は完了です。
プロジェクトの作成
DBの準備が終わったので、Javaのプロジェクトを作成していきます。
springjpaeduという名前で、Java アプリケーションのプロジェクトを作成します。
エンティティクラスの作成
プロジェクトを作成したら、springjpaedu.entitiesというパッケージを作成して、そこに新規→データベースからのエンティティクラスを選択してエンティティクラスを作成します。
先ほど作ったjpaeduデータベースを選択して、PERSONテーブルを選択します。
次の画面で、持続性ユニットを作成を選択して持続性ユニットを作成します。
持続性ユニットを作成したら、その他の項目はデフォルトのまま作成します。
作成が完了すると、springjpaedu.entities.Personクラスが作成されます。
作成されたら、ちゃんと動くかの確認も兼ねて、テスト用データ作成プログラムを作ります。
JPAを使うこと自体が主題ではないので、プログラムだけのせます。
とりあえずDBをクリアして、1000件データを作成しているだけのプログラムです。
package springjpaedu;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import springjpaedu.entities.Person;
public class Main {
private static final String PU = "springjpaeduPU";
public static void main(String[] args) {
EntityManagerFactory emf = null;
EntityManager em = null;
try {
emf = Persistence.createEntityManagerFactory(PU);
em = emf.createEntityManager();
execute(em);
} finally {
if (em != null) {
em.close();
}
if (emf != null) {
emf.close();
}
}
}
private static void execute(EntityManager em) {
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
int deleted = em.createQuery("delete from Person").executeUpdate();
for (int i = 1; i <= 1000; i++) {
Person p = new Person();
p.setId(i);
p.setName("田中 太郎" + i);
em.persist(p);
}
tx.commit();
System.out.println(deleted + "件のデータを削除しました");
System.out.println("1000件のデータを作成しました");
} catch (RuntimeException e) {
tx.rollback();
System.out.println("失敗");
e.printStackTrace();
}
}
}
このまま実行すると、恐らくエラーがおきてしまいます。
JavaDBのJDBCドライバをライブラリについかしていないのが原因なので、JavaDBのドライバをライブラリに追加します。
最近のJavaを何も意識せずに入れていると、C:\Program Files\Sun\JavaDB\lib\derbyclient.jarにJavaDBのJDBCドライバがあるので、それをライブラリに追加します。
実行すると、↓のような文字列が出力ウィンドウに表示されるはずです。
run:
[EL Info]: 2009.05.17 18:35:44.959--ServerSession(25197736)--EclipseLink, version: Eclipse Persistence Services - 1.0.1 (Build 20080905)
[EL Info]: 2009.05.17 18:35:45.118--ServerSession(25197736)--file:/C:/Users/Kazuki/Documents/NetBeansProjects/springjpaedu/src/-springjpaeduPU login successful
0件のデータを削除しました
1000件のデータを作成しました
[EL Info]: 2009.05.17 18:35:46.252--ServerSession(25197736)--file:/C:/Users/Kazuki/Documents/NetBeansProjects/springjpaedu/src/-springjpaeduPU logout successful
構築成功 (合計時間: 2 秒)
Springの設定
ついにSpring Frameworkのほうに入ります!
まず、ライブラリからSpring Framework 2.5を選択して追加します。これには、以下の3つのjarファイルが含まれています。
- spring-2.5.jar
- cglib-2.2.jar
- commons-logging-1.1.jar
凝ったことをしないのであればこれでいいのですが、今回はちょっとこれだけだと都合が悪いので、aspectjrt.jarもライブラリに追加します。
これは、Spring Frameworkのページから一式入ったものをダウンロードした中に入っているので、それを追加しました。
つぎに、このspringjpaedu.DaoTestMainクラスを作成して、mainメソッドを作っておきます。
package springjpaedu;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DaoTestMain {
public static void main(String[] args) {
AbstractApplicationContext ctx = null;
try {
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// TODO : ここに処理を書いていく予定
} finally {
ctx.close();
}
}
}
次に、デフォルトパッケージにapplicationContext.xmlを作成します。ソースパッケージの右クリックメニューから新規→その他からSpring XML 構成ファイルを選択します。
ファイル名をapplicationContext(xmlはつけない)にして次へ
名前空間には、contextとtxを選んで完了を押してください。
DaoTestMainを実行して、エラーが出ないことを確認すれば下準備完了です。
やっとDAO
ついにSpringとJPAの連携の機能を作りこんでいきます!長かった…
Spring FrameworkとJPAの連携は、2通りの方法があります。
- Hibernateとかと同じように***DaoSupportと***Templateクラスを使う方法
- JPAのAPIをそのまま使う方法
今回は2番のJPAのAPIをそのまま使う方法をやってみます。
何故かというと、1の方法でもあまりうれしさが感じれないからです。Hibernateとかで経験がある人が、自然に使いはじめれるという利点があるくらいなのかな?
とりあえずDAOを作っていきます!
springjpaedu.daoパッケージを作成して、そこにPersonDaoインターフェースとPersonDaoImplクラスを作成します。
PersonDaoに、CRUD用のメソッドを一通り作成します。
package springjpaedu.dao;
import java.util.List;
import springjpaedu.entities.Person;
public interface PersonDao {
List<Person> findAll();
Person get(int id);
void update(Person p);
void insert(Person p);
void delete(Person p);
}
PersonDaoImplで、これを実装していきます。
package springjpaedu.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import springjpaedu.entities.Person;
// トランザクションの定義
@Transactional
// Springに自動登録してもらうための印
@Repository("personDao")
public class PersonDaoImpl implements PersonDao {
// PersistenceContextをつけておくとSpringが適切にインスタンスをセットしてくれる
@PersistenceContext
private EntityManager entityManager;
@Transactional(readOnly=true)
public List<Person> findAll() {
return entityManager.
createQuery("select p from Person p order by p.id").
getResultList();
}
// 以下のメソッドでEntityManagerを使って色々処理を書く
@Transactional(readOnly=true)
public Person get(int id) {
return entityManager.find(Person.class, id);
}
public void update(Person p) {
entityManager.merge(p);
}
public void insert(Person p) {
entityManager.persist(p);
}
public void delete(Person p) {
entityManager.remove(p);
}
}
特別なことは別にしてなくて、EJB3のコンテナでもそのまま動きそう。
これを使うようにSpringの定義ファイルを書いていきます。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- EntityManagerFactoryとTransactionManagerを定義 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="springjpaeduPU" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- JPAの例外をSpringの例外に変換してくれる -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- PersistenceContextアノテーションにEntityManagerをインジェクションしてくれる -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<!-- DAOを自動登録(今回の場合1つしかないからあまりありがたみは無いかも -->
<context:component-scan base-package="springjpaedu.dao" />
<!-- アノテーションベースのトランザクション制御 -->
<tx:annotation-driven />
</beans>
こいつが動くかどうか簡単に確認してみようと思います。
動作を確認するために、CRUDを一通りするプログラムを作成してみました。
package springjpaedu;
import java.util.List;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import springjpaedu.dao.PersonDao;
import springjpaedu.entities.Person;
public class DaoTestMain {
public static void main(String[] args) {
AbstractApplicationContext ctx = null;
try {
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
PersonDao dao = (PersonDao) ctx.getBean("personDao");
// 全件表示
List<Person> people = dao.findAll();
System.out.println("## 全データ出力");
System.out.println(people);
// 1件取得して更新
Person p = dao.get(1);
p.setName("田中 麻呂");
dao.update(p);
// 更新された値を確認
Person p2 = dao.get(1);
System.out.println("更新後の名前: " + p2.getName());
// 新しい人を登録
Person newPerson = new Person();
newPerson.setId(1001);
newPerson.setName("木村 きむきむ");
dao.insert(newPerson);
// 新しく登録された人を確認
Person newPerson2 = dao.get(1001);
System.out.println("追加した人の名前: " + newPerson2.getName());
} finally {
ctx.close();
}
}
}
実行すると
## 全データ出力
[springjpaedu.entities.Person[id=1], springjpaedu.entities.Person[id=2], springjpaedu.entities.Person[id=3], spri...省略]
更新後の名前: 田中 麻呂
追加した人の名前: 木村 きむきむ
ちゃんと更新とかもされているっぽいです。
META-INF/persistence.xmlを以下のようにすると、
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="springjpaeduPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>springjpaedu.entities.Person</class>
<properties>
<property name="eclipselink.jdbc.password" value="jpaedu"/>
<property name="eclipselink.jdbc.user" value="jpaedu"/>
<property name="eclipselink.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
<property name="eclipselink.jdbc.url" value="jdbc:derby://localhost:1527/jpaedu"/>
<!-- SQLのログを出力 -->
<property name="eclipselink.logging.level" value="FINE" />
</properties>
</persistence-unit>
</persistence>
実際に発行されているSQL文も表示されていい感じになりそうです。
データ作成用のMainを起動してDBを初期化して、Javaのファイル以外を更新したので生成物を削除して構築をしてDaoTestMainを実行すると…
[EL Fine]: 2009.05.17 23:08:16.602--ServerSession(2719739)--Connection(6586390)--Thread(Thread[main,5,main])--SELECT ID, NAME FROM PERSON ORDER BY ID ASC
## 全データ出力
[springjpaedu.entities.Person[id=1], springjpaedu.entities.Person[id=2], springjpaedu.entities.Person[id=3], springjpaedu.entities.Person[id=4], springjp...省略]]
[EL Fine]: 2009.05.17 23:08:16.802--ClientSession(20804180)--Connection(27660658)--Thread(Thread[main,5,main])--UPDATE PERSON SET NAME = ? WHERE (ID = ?)
bind => [田中 麻呂, 1]
更新後の名前: 田中 麻呂
[EL Fine]: 2009.05.17 23:08:16.809--ClientSession(24809582)--Connection(9870377)--Thread(Thread[main,5,main])--INSERT INTO PERSON (ID, NAME) VALUES (?, ?)
bind => [1001, 木村 きむきむ]
追加した人の名前: 木村 きむきむ
うん。大丈夫そう。