JakartaEE(旧称:JavaEE)には、EJB(Enterprise JavaBeans)という、サーバ上で動作するアプリケーションをソフトウェア部品(コンポーネント)を組み合わせて開発・実行できるようにする仕組みがあり、データベースアクセス処理などでEJBを利用できる。
今回は、作成済のJSFプロジェクトで、EJBを利用してみたので、そのサンプルプログラムを共有する。
なお、EJBについては、以下のサイトを参照のこと。
https://atmarkit.itmedia.co.jp/fjava/keyword/jkey/jkey03.html
前提条件
以下の記事の実装が完了していること。
サンプルプログラムの作成
作成したサンプルプログラムの構成は、以下の通り。
なお、上記の赤枠は、前提条件のプログラムから変更したプログラムである。
JPAクラスの内容は以下の通りで、先頭にEJBのステートレスセッションBeanを示す@Statelessアノテーションを付与すると共に、トランザクション属性をREQUIREDに指定する@TransactionAttributeアノテーションを付与している。
package jpa;
import java.util.List;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
* USER_DATAテーブルへアクセスするJPA.
*/
// EJBのステートレスセッションBean(各クライアントに固有の値を保持しない)
@Stateless
public class UserDataJpa {
@PersistenceContext
private EntityManager em;
/**
* USER_DATAテーブルに引数のデータを追加する.
* @param userData
*/
// トランザクション属性をREQUIRED(トランザクションが開始していれば
// そのトランザクションで、トランザクションが開始していない場合は
// 新しいトランザクションを開始して実行)に設定
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
public void regist(UserData userData){
// UserDataテーブルのid最大値を取得
Integer maxId = em.createQuery("SELECT MAX(u.id) FROM UserData u"
, Integer.class).getSingleResult();
// id最大値がNULLの場合は、0に変換
maxId = (maxId == null) ? 0 : maxId;
// USER_DATAテーブルのIDを設定後、登録処理を実行
userData.setId(maxId + 1);
em.persist(userData);
em.flush();
}
/**
* USER_DATAテーブルの全件を取得する.
* @return USER_DATAテーブル全件のリスト
*/
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
public List<UserData> getAll(){
// UserDataテーブルのデータを全件取得し返却
List<UserData> uList = em.createQuery("FROM UserData u ORDER BY u.id ASC"
, UserData.class).getResultList();
// 取得したデータを返却
return uList;
}
/**
* 選択したIDをもつUSER_DATAテーブルの値を取得する.
* @param selectId 選択したID
* @return 選択したIDをもつUSER_DATAテーブルの値
*/
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
public UserData getById(String selectId) throws NumberFormatException{
// UserDataテーブルの選択したIDをもつデータを取得
Integer intId = Integer.parseInt(selectId);
UserData userData = em.createQuery("FROM UserData u WHERE u.id = :intId"
, UserData.class)
.setParameter("intId", intId)
.getSingleResult();
// 取得したデータを返却
return userData;
}
/**
* 引数のデータをもつUSER_DATAテーブルのデータを更新する.
* @param userData
*/
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
public void update(UserData userData){
// IDが設定されている場合、更新
if(userData.getId() != null){
em.merge(userData);
em.flush();
}
}
/**
* 引数のデータをもつUSER_DATAテーブルのデータを削除する.
* @param userData
*/
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
public void delete(UserData userData){
// IDが設定されている場合、削除
if(userData.getId() != null){
em.remove(this.getById(String.valueOf(userData.getId())));
em.flush();
}
}
}なお、EJBのステートレスセッションBeanについては、以下のサイトを参照のこと。
https://www.itmedia.co.jp/enterprise/0402/13/epn03.html
また、トランザクション属性のREQUIREDについては、以下のサイトを参照のこと。
https://itpfdoc.hitachi.co.jp/manuals/3020/30203M0360/EM030204.HTM
さらに、先ほどのJPAクラスを呼び出しているクラスの内容は以下の通りで、JPAクラスをインジェクトする箇所に@EJBアノテーションを付与している。
package faces;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.ejb.EJB;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.model.SelectItem;
import javax.inject.Named;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
import common.CommonUtil;
import jpa.UserData;
import jpa.UserDataJpa;
import lombok.Data;
import lombok.ToString;
/**
* 画面のフォーム値と画面遷移メソッドを定義.
*/
// @Namedアノテーションは、JSFのXHTMLファイルから#{inputFormAction}で
// Javaクラスを参照できるようにしている(→バッキングビーン)
// @SessionScopedアノテーションは、このバッキングビーンの生存期間を
// セッションに設定している
@Named(value="inputFormAction")
@SessionScoped
// 以下はLombokのアノテーション
//「@Data」アノテーションを付与すると、このクラス内の全フィールドに対する
// Getterメソッド・Setterメソッドにアクセスができる
@Data
//「@ToString」アノテーションを用いて、exclude属性で指定した項目以外の値を、
// toStringメソッド呼出時に出力することができる
@ToString(exclude={"birthMonthItems","birthDayItems","sexItems"})
public class InputFormAction implements Serializable {
// シリアルバージョンUID
private static final long serialVersionUID = 7283339629129432007L;
/** ID */
private String id;
/** 名前 */
@NotEmpty
@Size(min=1, max=10)
private String name;
/** 生年月日_年 */
private String birthYear;
/** 生年月日_月 */
private String birthMonth;
/** 生年月日_日 */
private String birthDay;
/** 性別 */
@NotEmpty(message="{sex.NotEmpty.message}")
private String sex;
/** 性別(ラベル) */
private String sexLabel;
/** メモ */
private String memo;
/** 確認チェック */
@AssertTrue
private Boolean checked;
/** 生年月日_月(選択リスト) */
private List<SelectItem> birthMonthItems;
/** 生年月日_日(選択リスト) */
private List<SelectItem> birthDayItems;
/** 性別(選択リスト) */
private List<SelectItem> sexItems;
/** UserDataテーブルへアクセスするJPA */
// EJBのステートレスセッションBeanをインジェクト
@EJB
private UserDataJpa userDataJpa;
/**
* コンストラクタ生成時に選択リストの値を設定.
*/
public InputFormAction(){
// 生年月日_月(選択リスト)
birthMonthItems = new ArrayList<SelectItem>();
birthMonthItems.add(new SelectItem("", ""));
for(Integer i = 1; i <= 12; i++){
birthMonthItems.add(new SelectItem(String.valueOf(i), String.valueOf(i)));
}
// 生年月日_日(選択リスト)
birthDayItems = new ArrayList<SelectItem>();
birthDayItems.add(new SelectItem("", ""));
for(Integer i = 1; i <= 31; i++){
birthDayItems.add(new SelectItem(String.valueOf(i), String.valueOf(i)));
}
// 性別(選択リスト)
sexItems = new ArrayList<SelectItem>();
sexItems.add(new SelectItem(String.valueOf(1),"男"));
sexItems.add(new SelectItem(String.valueOf(2),"女"));
}
/**
* 入力画面への遷移(更新用).
* @return 入力画面へのパス
*/
public String toMod(){
// 選択されたIDをもつユーザーデータを取得・設定
this.setSelectItem();
// 入力画面に遷移
return "toMod";
}
/**
* 確認画面への遷移.
* @return 確認画面へのパス
*/
public String confirm(){
// 性別(ラベル)を設定
if(!CommonUtil.isBlank(sex)){
this.setSexLabel(this.getSexItems().get(
Integer.parseInt(this.getSex())-1).getLabel());
}
// 確認画面に遷移
return "confirm";
}
/**
* 入力画面に戻る.
* @return 入力画面へのパス
*/
public String back(){
// 入力画面に戻る
return "back";
}
/**
* 完了画面への遷移.
* @return 完了画面へのパス
*/
public String send(){
// 画面の入力内容を登録または更新
if(id != null){
userDataJpa.update(getUserData());
}else{
userDataJpa.regist(getUserData());
}
// セッション情報の破棄
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
// 完了画面への遷移
return "send";
}
/**
* 削除確認画面への遷移(更新用).
* @return 入力画面へのパス
*/
public String toDel(){
// 選択されたIDをもつユーザーデータを取得・設定
this.setSelectItem();
// 削除確認画面に遷移
return "toDel";
}
/**
* 削除確認画面から一覧画面への遷移.
* @return 一覧画面へのパス
*/
public String del(){
// 画面の入力内容を削除
userDataJpa.delete(getUserData());
// セッション情報の破棄
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
// 一覧画面に遷移
return "del";
}
/**
* 相関チェックを実施し、エラーの場合はエラーメッセージを表示.
* @param compSysEvent JSFシステムイベント
*/
public void validate(ComponentSystemEvent compSysEvent) {
UIComponent component = compSysEvent.getComponent();
// 生年月日の年・月・日を取得する
UIInput birthYearUI = (UIInput)component.findComponent("birthYear");
UIInput birthMonthUI = (UIInput)component.findComponent("birthMonth");
UIInput birthDayUI = (UIInput)component.findComponent("birthDay");
String birthYearSt = (String)birthYearUI.getLocalValue();
String birthMonthSt = (String)birthMonthUI.getLocalValue();
String birthDaySt = (String)birthDayUI.getLocalValue();
// 年・月・日がすべて空白値の場合はエラーメッセージを返す
if(CommonUtil.isBlank(birthYearSt) && CommonUtil.isBlank(birthMonthSt)
&& CommonUtil.isBlank(birthDaySt)){
addErrorMessage("org.hibernate.validator.constraints.NotEmpty.message"
, component);
return;
}
// 生年月日が存在しない日付の場合はエラーメッセージを返す
String dateStr = birthYearSt + CommonUtil.addZero(birthMonthSt)
+ CommonUtil.addZero(birthDaySt);
if(!CommonUtil.isCorrectDate(dateStr, "uuuuMMdd")){
addErrorMessage("date.Invalid.message", component);
}
}
/**
* 引数のメッセージKeyをもつエラーメッセージを追加.
* @param messageKey メッセージKey
* @param component JSFコンポーネント
*/
private void addErrorMessage(String messageKey, UIComponent component){
FacesContext context = FacesContext.getCurrentInstance();
String message = CommonUtil.getMessage(messageKey);
FacesMessage facesMessage = new FacesMessage(message, message);
facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
context.addMessage(component.getClientId(), facesMessage);
context.renderResponse();
}
/**
* 登録時に利用するユーザー情報を生成.
* @return ユーザー情報
*/
private UserData getUserData(){
UserData userData = new UserData();
try{
if(this.id != null){
userData.setId(Integer.parseInt(this.id));
}
userData.setName(this.getName());
userData.setSex(this.getSex());
userData.setMemo(this.getMemo());
userData.setBirthYear(Integer.parseInt(this.getBirthYear()));
userData.setBirthMonth(Integer.parseInt(this.getBirthMonth()));
userData.setBirthDay(Integer.parseInt(this.getBirthDay()));
}catch(Exception ex){
System.err.println(ex);
}
return userData;
}
/**
* 選択されたIDをもつユーザーデータを取得・設定.
*/
private void setSelectItem(){
// リクエストパラメータの値を取得
FacesContext fc = FacesContext.getCurrentInstance();
Map<String,String> params = fc.getExternalContext().getRequestParameterMap();
String selectId = params.get("selectId");
// 選択したIDをもつユーザーデータを取得
UserData userData = userDataJpa.getById(selectId);
// フィールドの各値に取得した値を設定
if(userData != null){
id = String.valueOf(userData.getId());
name = userData.getName();
birthYear = String.valueOf(userData.getBirthYear());
birthMonth = String.valueOf(userData.getBirthMonth());
birthDay = String.valueOf(userData.getBirthDay());
sex = userData.getSex();
sexLabel = this.getSexItems().get(Integer.parseInt(
userData.getSex())-1).getLabel();
memo = userData.getMemo();
}
}
}package faces;
import java.io.Serializable;
import java.util.List;
import javax.ejb.EJB;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import jpa.UserData;
import jpa.UserDataJpa;
import lombok.Getter;
/**
* USER_DATAリスト取得処理を定義.
*/
//「更新」「削除」リンクを機能させるため、RequestScopedからSessionScopeに変更
@Named(value="userListAction")
@SessionScoped
public class UserListAction implements Serializable{
// シリアルバージョンUID
private static final long serialVersionUID = -8890511854883241114L;
/** USER_DATAリスト */
@Getter
private List<UserData> userDataList;
/** UserDataテーブルへアクセスするJPA */
// EJBのステートレスセッションBeanをインジェクト
@EJB
private UserDataJpa userDataJpa;
/** 一覧画面の初期表示処理 */
public void initialize(){
// USER_DATAテーブルの全件を取得する
userDataList = userDataJpa.getAll();
}
/**
* 入力画面への遷移(追加用).
* @return 入力画面へのパス
*/
public String toAdd(){
return "add";
}
/**
* 一覧画面への遷移.
* @return 一覧画面へのパス
*/
public String toList(){
// 初期表示処理を呼び出し、一覧画面に遷移する
this.initialize();
return "list";
}
}その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/javaee-jsf-ejb/demoJsf
サンプルプログラムの実行
サンプルプログラムの実行結果は、以下の記事の「サンプルプログラムの実行結果」と同じ結果となる。
要点まとめ
- JakartaEE(旧称:JavaEE)には、EJB(Enterprise JavaBeans)という、サーバ上で動作するアプリケーションをソフトウェア部品(コンポーネント)を組み合わせて開発・実行できるようにする仕組みがあり、データベースアクセス処理などでEJBを利用できる。





