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を利用できる。