Eclipse+JSF+JPAで作るアプリ(6)―JPA EntityMangerの利用

このアプリはロジックはそんなにないためほぼDAO(DataAccessObject)といわれるパターンに近い実装になります。

今回の範囲は前回触れなかった、

  • JPQLを使ったクエリ
  • アプリケーション管理するEntityManager

になります。


不作法ですが、カットするほどの分量ではないので、ソースをすべて貼り付け解説は後で行います。
簡略化のためメソッドはすべてstaticにしています。スレッドセーフにするためステートレスにしていることにも注意してください。

ちなみにあまり良い例とは言えません。
フル装備でパッケージにする際は、staticは辞めて、UserDao(Interface=DAO、DBアクセスを担当), UserDaoImpl(Class), UserService(Interface=ロジックを担当), UserServiceImpl(Class)
とし、さらにCDI(詳しくはないです)でインジェクションしてやるといったことをする必要があるでしょうか。そこまではやりすぎなので今回はしません。

package sample.yourlibrary.logic;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;

import sample.yourlibrary.entity.LendHistory;
import sample.yourlibrary.entity.User;

public class UserManager {
	
	//1.アプリケーションで管理するEntityManger
	private static EntityManager getEm()
	{
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("YourLibrary");
		EntityManager em = emf.createEntityManager();
		return em;
	}
	
	//2.persistで新規オブジェクトの永続化
	public static User createUser( String account, String name)
	{
		User user = new User();
		user.setAccount(account);
		user.setName(name);
		EntityManager em = getEm();
		EntityTransaction tx = em.getTransaction();
		tx.begin();
		em.persist(user);//newしたオブジェクトの永続化
		tx.commit();
		em.close();
		return user;
	}
	
	//3.findの使用
	public static User findById( long id )
	{
		EntityManager em = getEm();
		User user = em.find(User.class, id);
		em.clear();
		em.close();
		return user;
	}
	
	//4.もっとも簡単なJPQL
	public static List<User> findAll()
	{
		EntityManager em = getEm();
		TypedQuery<User> q = em.createQuery("select u from User u order by u.account asc", User.class);
		List<User> result = q.getResultList();
		em.close();
		return result;
	}
	
	//5.JPQLの名前付きパラメータの使用
	public static User findByAccount( String account )
	{
		EntityManager em = getEm();
		TypedQuery<User> q = em.createQuery("select u from User u where u.account=:account", User.class);
		q.setParameter("account", account);//名前付きパラメータ
		List<User> result = q.getResultList();
		em.close();
		if( result.size() > 0 )
			return result.get(0);
		return null;
	}
	
	//6.JPQLを使ったログイン処理
	public static User login( String account, String password)
	{
		EntityManager em = getEm();
		TypedQuery<User> q = em.createQuery("select u from User u where u.account=:account and u.password=:password", User.class);
		q.setParameter("account", account);
		q.setParameter("password", password);
		List<User> result = q.getResultList();
		em.close();
		if( result.size() > 0 )
			return result.get(0);
		
		if( "admin".equals(account) && "admin".equals(password))
		{
			User admin = findByAccount( account );
			if( admin == null )
			{//adminがいないときのみadminユーザを作成するバックドア。
				admin = createUser(account, password);
				admin.setPassword("admin");
				admin.setIsAdmin( true );
				updateUser(admin);//冗長だが、Insert後すぐにUpdateしている。
				return admin;
			}
		}
		
		return null;
	}

	//7.デタッチ状態から永続コンテキストへマージ
	public static User updateUser( User user)
	{
		EntityManager em = getEm();
		EntityTransaction tx = em.getTransaction();
		tx.begin();
		if(!em.contains(user) )
			user = em.merge(user);//マージ
		tx.commit();
		em.close();
		return user;
	}
	
	//8.エンティティの削除
	public static boolean removeUser( User user )
	{
		EntityManager em = getEm();
		User find = em.find(User.class, user.getId());
		if( find == null )
			return false;
		EntityTransaction tx = em.getTransaction();
		tx.begin();
		List<LendHistory> histories = find.getLendHistories();
		for (LendHistory history : histories) {
			history.setLendUser(null);//履歴からは、ユーザをnullにしてから削除する。
			em.merge(history);//履歴の更新
		}
		em.remove(find);//削除
		tx.commit();
		em.close();
		return true;
	}
}
1.アプリケーションで管理するEntityManger

EntityManagerは、永続化の中心となるオブジェクトです。
永続化コンテキストという大きなマップを持っていて、そこにEntityオブジェクトの参照を持って管理しています。
エンティティの状態には、new状態(コンテキスト外)、管理状態(コンテキスト内)、デタッチ状態(コンテキスト外)、削除状態(コンテキスト内)とあり、EntityManagerのメソッドによって状態が変わります。
はじめての Java Persistence API | 寺田 佳央 - Yoshio Teradaの「Entityのライフサイクル」がわかりやすい図です。

メモリを圧迫することもあるので注意が必要とのこと。

EntityManagerFactory emf = Persistence.createEntityManagerFactory("YourLibrary");

ここで指定するのは、persistence.xmlで記述したpersistence-unitの名前です。

<persistence-unit name="YourLibrary"></persistence-unit>
2.persistで新規オブジェクトの永続化

新規にnewしたオブジェクトは、EntityManagerのpersistを使って管理下に置きます。commitを呼び出すことでデータベースに同期(Insert)されます。

3.findの使用

@Idアノテーションを指定していた場合にfindメソッドで検索することができます。
このUserManagerでは永続化コンテキストとは分離させるため、すぐにem.clear()を呼び出しています。

4.もっとも簡単なJPQL
5.JPQLの名前付きパラメータの使用
6.JPQLを使ったログイン処理

オブジェクトを検索するためのSQLライクなクエリです。エイリアスの指定が必須となっています。
distinct, count, sumなども使えます。
パラメータには、"{0}":順番で指定、":name":パラメータ名で指定が使えます。
#6番は、ログインといいつつバックドアでユーザを作成しています。DBにレコードを作っておくのが面倒なため。

7.デタッチ状態から永続コンテキストへマージ

mergeメソッドで分離状態のオブジェクトをコンテキスト下に置き、直ちにcommitしています。

8.エンティティの削除

findで管理下に置き、removeで削除状態にしてからcommitしています。
『貸出履歴』から参照されている状態をクリアにするロジックが含まれています。

同様に、MovieManagerをsample.yourlibrary.logicパッケージ以下に実装します。