Service/Daoクラス

最終更新日: 2021年3月8日
R8 | R9

ServiceクラスとDaoクラス

ServiceクラスとDaoクラスはモデル毎に用意されます。

Serviceクラスは内部でDaoクラスを利用します。Serviceクラスはトランザクション境界となっており、実際のデータベース操作はDaoクラスが行います。Daoクラスはそれ以外にキャッシュの制御や問合せのためのCriteriaの作成を行います。

図1 ServiceクラスとDaoクラス
Wagby R6 では「プロセスビーン」というクラスが担っていましたが、R9 では Service/Dao 層に分離されています。
なお R9 でも一部でプロセスビーンが残っているところがありますが、今後のバージョンアップで廃止予定です。
そのため、プロセスビーンを用いたカスタマイズは非推奨です。

Dao (Data Access Object)

ORMフレームワークHibernateを利用したクラスとなっています。CRUD処理は一つのDaoクラスで実現します。 またデータベースへの問合せはHibernateが提供する「CriteriaクエリAPI」を使います。SQLを直接、生成しているところはありません。

図2 Daoクラスのインタフェース

Daoクラスの利用例を示します。

// 主キー1000のデータを取得
Customer customer = customerDao.get(1000, true);
// データを更新
customer.setName("ジャスミン太郎");
customerDao.update(customer);
// データを登録
Customer customer2 = new Customer(customer);
customer2.setCustomerid(1001);
customerDao.save(customer2);
// 主キー2000のデータを削除
customerDao.deleteById(2000, true);

ロックについて

上の例では、customerDao.get メソッド、ならびにdeleteById メソッド呼び出し時に、第二引数を true としています。この場合、処理のタイミングで悲観ロック処理を適用します。ロックをかけた場合、同データが更新されれば、トランザクション終了のタイミングでロックは解除されますが、更新しない場合はロックが残ったままとなってしまいます。参照のみの処理で、更新する必要がないデータを取得する場合は、get メソッドの第二引数を false としてください。

後述する、EntityService の findById メソッドの第二引数も、同じ動作となります。

EntityServiceにおけるトランザクション管理

トランザクション境界となるクラスです。インタフェースを実装したクラスでは、各メソッドに @Transactional アノテーションが付与されています。

メソッド開始時に自動的にトランザクションが開始されます。正常終了でコミットされます。Exception発生時には自動的にロールバックされます。

図3 Serviceクラスのインタフェース

"リポジトリ > スクリプト > Service/Daoクラス、ヘルパ、SQLを利用する" では、スクリプトを使った場合の Service の利用例を説明しています。

EntityService,Dao,Helperインスタンスの取得

EntityService, Dao, Helper はメソッドの外側で以下のようにインスタンス変数を宣言することで取得することができます。

EntityService

@Autowired
@Qualifier("CustomerEntityService")
protected JFCEntityService<Customer, Integer> customerEntityService;

Dao

@Autowired
@Qualifier("CustomerDao")
protected JFCHibernateDao<Customer, Integer> customerDao;

Helper

@Autowired
@Qualifier("CustomerHelper")
protected EntityHelper<Customer, Integer> customerHelper;

各クラスの型パラメータの第一引数は対象クラスになります。 第二引数は主キーの型になります。 整数型の場合は Integer、文字列型の場合は String などと指定します。

ApplicationContextから取得する

SpringのApplicationContextインスタンスは、p.appctx で参照することができます。プログラム中では次のように扱うことができます。

import jp.jasminesoft.jfc.service.JFCEntityService;
...
JFCEntityService<Customer, Integer> entityService =
  (JFCEntityService<Customer, Integer>)p.appctx.getBean("CustomerEntityService");
  • 左辺の entityService の型は JFCEntityService としてください。そうしないと実行時に ClassCastException 例外が発生します。これは EntityService クラスが Spring によって Proxy に置き換えられるようになっているためです。
  • 更新用メソッド (update) の第一引数にはストアモデルを指定します。第二引数は更新対象モデルがロック済みかどうかを指定します。trueの場合はロック済みです。falseの場合はロックされていない状態のモデルを(強制的に)保存します。

ワンポイント

EntityService は SpringのDIコンテナから取得します。これによって EntityService の動作で必要となる Dao や EntityHelper、ActoinParamterContainer などのインスタンスが自動的にセットされた状態で取得できます。

EntityServiceのサンプルコード

EntityService を用いたデータ操作のサンプルコードを紹介します。

コンディションモデルを使ったデータの検索

検索条件を格納した「コンディションモデル」を finderContext にセットします。finderContext には CriteriaConverter が必要です。Criteria の詳細は次節で説明します。

import jp.jasminesoft.jfc.service.JFCEntityService;
...
Customer[] customer_ary = null;
CustomerC cond = new jp.jasminesoft.wagby.model.customer_c.CustomerC();
((CustomerCHelper)p.appctx.getBean("CustomerCHelper")).initialize(cond, p);
cond.setName(name);// ここでは顧客名を検索条件に設定した。
JFCEntityService<Customer, String> entityService = 
    (JFCEntityService<Customer, String>)p.appctx.getBean("CustomerEntityService");
FinderContext<CustomerC> finderContext = new FinderContext<CustomerC>();
finderContext.setCondition(cond);
finderContext.setPageSize(-1);// 無制限
finderContext.setCriteriaConverter((CustomerCriteriaConverter)
    p.appctx.getBean("CustomerCriteriaConverter"));
List<Customer> coll = entityService.find(finderContext);
if (coll != null && coll.size() > 0) {
    customer_ary = (Customer[])coll.toArray(new Customer[0]);
}

データ1件の取得

主キーを指定してデータを1件取得することができます。

import jp.jasminesoft.jfc.service.JFCEntityService;
...
Customer customer = null;
try {
    JFCEntityService<Customer, Integer> entityService =
        (JFCEntityService<Customer, Integer>)p.appctx.getBean("CustomerEntityService");
    customer = entityService.findById(customerid, true);//主キーを指定。ロックを取得する。
} catch (Exception e) {
    String errmsg = 
        DbBaseController.convertErrorMessage(e, 
            new String[][] {
                { "customerid", JFCUtils.getRValue("customer.customerid", p.locale) }
            });
    if (errmsg == null) {
        errmsg = "";
    }
    Object[] ep = { errmsg };
    p.errors.addJfcerror
         (errorManager.getJfcerror("customer.error.dbaccess", ep, p.locale));
}
// このあと customer を更新することを想定。更新時に取得したロックは解放される。
// (省略)
更新や削除を行わない場合は、findById メソッドの第二引数に false を指定し、ロックをかけないようにすることもできます。また LockUtils クラスを使って、ロックの取得をあとで行うこともできます。[詳細...]

更新

import jp.jasminesoft.jfc.service.JFCEntityService;
...
JFCEntityService<Customer, Integer> entityService =
  (JFCEntityService<Customer, Integer>)p.appctx.getBean("CustomerEntityService");
// findById メソッドを経由してオブジェクトを取得、そのときにロックを取得した。
Customer customer = entityService.findById(customerid, true);//主キー
// customer オブジェクトの内容を変更
// (省略)
// 更新
entityService.update(customer);
//entityService.update(customer, false); 未ロックのオブジェクトを更新する場合。非推奨。

更新用メソッド (update) の第一引数にはストアモデルを指定します。第二引数は更新対象モデルがロック済みかどうかを指定します。第二引数にfalseを指定すると他ユーザによるロック取得を無視して更新しますが、この方法は推奨できません。 上にあるように更新対象となるストアモデルを取得するときにロックをかけ、ロック済みのストアモデルをupdateメソッドの第一引数に指定するようにしてください。

クライテリアを利用する

Wagbyが提供する検索条件を格納するための「コンディションモデル」は、内部でHibernateが提供する「クライテリア」(Criteria)に変換されています。開発者はこのクライテリアを直接、操作したデータベース検索を行うことができます。この方法のメリットは、IDE (Eclipse) を使ったコード補完の実現と、Meta クラスの利用によるタイプミスの防止です。

クライテリアを利用したコード例を示します。

// この方式で取得した criteria は、モデル定義で設定した暗黙条件も適用されない。
// (後述する CriteriaConverter を使う方式では、暗黙条件が適用される。)
CustomerMeta meta = new CustomerMeta();
DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class);
// customerid を検索するための Criteria を組み立てる(数値の範囲検索)。
criteria.between(meta.customerid,
        condition.getCustomerid1jshparamAsInteger(),
        condition.getCustomerid2jshparamAsInteger());
// deptname を検索するための Criteria を組み立てる(部分一致検索)。
criteria.like(meta.deptname, condition.getDeptname());
// 繰返し項目 email を検索するための Criteria を組み立てる。
criteria.like(meta.email, condition.getEmail());
// 繰り返しコンテナ内の項目 rdate を検索するための Criteria を組み立てる。
criteria.between(meta.rdate, condition.getRdate1jshparam(),
        condition.getRdate2jshparam());
// 会社名(降順)、顧客ID(昇順) でソートする。
// SQL : ORDER BY companyname DESC, customerid ASC
// Order はhibernateが提供するクラス。(org.hibernate.criterion.Order)
criteria.addOrder(
       new Order[] {Order.desc(meta.companyname.name()),
               Order.asc(meta.customerid.name())});
Criteria executableCriteria
        = criteria.getExecutableCriteria(getCurrentSession());
@SuppressWarnings("unchecked")
List<Customer> results = executableCriteria.list();

Metaクラス

項目名、テーブル名、列名の文字列を一括で管理します。Metaクラスを通してこれらの値を取得します。

図5 Metaクラス

CriteriaConverter

Wagbyは検索条件を保持するコンディションモデルを自動生成しています。コンディションモデルからCriteriaを取得するにはCriteriaConverterを使います。

CriteriaConverterの利用例を示します。

// CriteriaConverter インスタンスを作成
CustomerCriteriaConverter converter
        = (CustomerCriteriaConverter) p.appctx.getBean(
                "CustomerCriteriaConverter");
// Condition を Criteria に変換
DetachedCriteria criteria = converter.convert(condition, sortKey);
// Criteria を使って検索を実行する。(daoオブジェクトは事前に用意されたものとする)
List<Customer> results = dao.findByCriteria(criteria);
// Service を利用することもできる。(serviceオブジェクトは事前に用意されたものとする)
//List<Customer> results = service.find(criteria);

メソッドの外側で以下のようにインスタンス変数を宣言することで CriteriaConverter インスタンスの取得することもできます。

/** CriteriaConverter インスタンスを作成 */
@Autowired
@Qualifier("CustomerCriteriaConverter")
protected CriteriaConverter<CustomerC> converter;
CriteriaConverterの型パラメータの引数はコンディションモデルになります。
CriteriaConverterを使うと、モデル定義で指定された暗黙条件が適用されます。

ページング

Daoクラスが提供するfindByCriteriaメソッドはページング処理に対応しています。さらに現在のページを記憶する FinderContext クラスも提供しています。

findByCriteriaメソッドの利用例を示します。

CustomerCriteriaConverter converter
        = (CustomerCriteriaConverter) p.appctx.getBean(
                "CustomerCriteriaConverter");
DetachedCriteria criteria = converter.convert(condition, sortKey);
// 先頭の 10 件を取得
List<Customer> results = dao.findByCriteria(criteria, 0, 10);
// ...
// 次の 10 件を取得
results = dao.findByCriteria(criteria, 10, 10);

finderContextの利用例を示します。

// FinderContext インスタンスを作成
FinderContext<CustomerC> finderContext = new FinderContext<CustomerC>();
// Condition をセット
finderContext.setCondition(condition);
// ページサイズをセット( 0 をセットすると無制限となります)
finderContext.setPageSize(10);
// CriteriaConverter をセット
finderContext.setCriteriaConverter(
        (CustomerCriteriaConverter) p.appctx.getBean(
                "CustomerCriteriaConverter"));
// 先頭の 10 件を取得
List<Customer> results = dao.find(finderContext);
// 次の 10 件を取得
finderContext.next();
results = dao.find(finderContext);
// 最後のページのデータを取得
finderContext.last();
results = dao.find(finderContext);

件数の取得

Daoクラスが提供するcountメソッドの引数にfinderContextをセットすることで、件数を取得することができます。

// FinderContext インスタンスを作成
FinderContext<CustomerC> finderContext = new FinderContext<CustomerC>();
// Condition をセット
finderContext.setCondition(condition);
// CriteriaConverter をセット
finderContext.setCriteriaConverter(
        (CustomerCriteriaConverter) p.appctx.getBean(
                "CustomerCriteriaConverter"));
int size = dao.count(finderContext);

キャッシュ制御

Wagbyではパフォーマンス向上のために内部で積極的にキャッシュを用いています。 HibernateのInterceptor機能を利用しており、トランザクションコミットのタイミングでキャッシュをクリアするといった処理が自動的に行われます。

複合キーの場合

複合キークラスをストアモデルのインナークラスとして用意しています。
zaiko モデルが複合キーの場合は次のような記述となります。

Zaiko.Key key = new Zaiko.Key();
key.setPkey1(xxx);/* xxx には、主キーの値が入ります */
key.setPkey2(yyy);/* yyy には、主キーの値が入ります */
Zaiko zaiko = zaikoEntityService.findById(key,true);/* 第二引数がtrueのときロックをかける */

Daoからストアドプロシージャを呼び出す

Daoクラスでは getCurrentSession メソッドを使ってデータベースセッションを取得することができます。これを使ってSQLを直接、記述することもできます。

自動生成されたDaoクラスをカスタマイズしたMyDaoクラスを用意し、データを取得(get)したタイミングでデータベースのストアドプロシージャを呼び出す例を紹介します。

public class MyCustomerDao extends CustomerDao {
    /** {@inheritDoc} */
    @Override
    public Customer get(Integer pkey) {
        getCurrentSession().doWork(new Work() {
            public void execute(Connection connection) throws SQLException {
                CallableStatement st = null;
                ResultSet rs = null;
                try {
                    // ストアドプロシージャ呼び出し
                    st = connection.prepareCall("{?= CALL POWER(2, 3)}");
                    st.execute();
                    rs = st.getResultSet();
                    if (rs.next())  {
                      System.out.println(rs.getInt(1)); // 結果の出力。
                    }
                } finally {
                    DbUtils.closeQuietly(st);
                    DbUtils.closeQuietly(rs);
                }
            }
        });
        return get(pkey, true);
    }
}
  • 上のコードの Work クラスは、org.hibernate.jdbc.Work を使っています。
  • getCurrentSession() 経由で取得したデータベースセッションは、Spring の TransactionManager の制御下にあります。そのため開発者はデータベースセッションのクローズ処理を行わないようにしてください。しかし開発者が独自に用意した Statement や ResultSet についてはクローズ処理が必要です。この場合は DbUtils.closeQuietly を使うとよいでしょう。

ヘルパからストアドプロシージャを呼び出す

ヘルパクラスの afterUpdate メソッドをオーバーライドして、このタイミングでストアドプロシージャ呼び出し(または何らかのSQL処理)を行うコードの例を紹介します。

   @Override
   public void afterUpdate(Model1 entity, final ActionParameter p) {
       org.hibernate.Session sess = jp.jasminesoft.jfc.app.HibernateUtil.openSession();
       try {
           sess.doWork(new Work() {
               (ストアドプロシージャ呼び出し、またはSQL処理。)
           });
       } finally {
           sess.close();
       }
       super.afterUpdate(entity, p);
   }

Wagby では TransactionManager 経由で取得された Hibernate Session は自動的にクローズされますが、上記のように HibernateUtil.openSession() を使って取得した Hibernate Session はコード開発者が明示的にクローズ処理を行う必要があります。

ヘルパからSQLを呼び出す

上と同じ枠組みですが、SQLを呼び出す例も示します。(正確には SQL ではなく、Hibernate が提供する HQL です。)

   @Override
   public void afterUpdate(Model1 entity, final ActionParameter p) {
       org.hibernate.Session sess = jp.jasminesoft.jfc.app.HibernateUtil.openSession();
       try {
           Object o = sess.createSQLQuery(
               "SELECT COUNT(*) FROM \"juser\"").uniqueResult();
           System.out.println(o);
       } finally {
           sess.close();
       }
       super.afterUpdate(entity, p);
   }

詳細は Hibernate プログラミングとなるため、割愛します。

プログラムによるトランザクション管理

Wagbyは標準で Spring framework が提供する宣言的トランザクションを利用することができますが、これとは別に開発者が直接、トランザクション管理を行うことも可能です。(プログラマティックトランザクション)

具体的には Spring が提供する TransactionTemplate クラスを使って、トランザクション境界の中で動作するコードを記述することができます。

例 ヘルパクラスの拡張

Desinger のスクリプト設定で「ヘルパ > 実行のタイミング : 登録(初期データ作成)」が用意されています。これは登録画面を開く前処理としてスクリプトを記述できますが、トランザクション外の処理となっています。

そこでヘルパクラスを拡張し、次のように TransactionTemplate クラスを利用することができます。モデル model1 を例にしています。

public class MyModel1Helper extends Model1Helper {
   @Autowired
   @Qualifier("RequiredTransactionTemplate")
   protected TransactionTemplate requiredTransactionTemplate;
   public void initialize(Model1 model1, ActionParameter p)
       requiredTransactionTemplate.execute(
               new TransactionCallbackWithoutResult() {
                   @Override
                   protected void doInTransactionWithoutResult(
                           TransactionStatus status) {
                       // コードを記述する。model1 というオブジェクト(インスタンス)を利用できる。
                       // ActionParameter のインスタンス p も利用できる。
                   }
               });
   }

TransactionTemplate クラスは Spring が提供する、プログラムコードによるトランザクション管理を行うための仕組みです。一般的に、TransactionCallback を実装した無名クラスを作成します。

非検査例外(RuntimeExceptionの派生クラス)が発生することにより doInTransactionWithoutResult() メソッドの処理が終了した場合は、トランザクションは自動的にロールバックされます。

例 コントローラクラスの拡張(独自ボタン)

customer モデルの詳細画面に独自ボタン(イベント名:Original1)を配置し、ボタン押下時に実行される処理として作成しています。

package jp.jasminesoft.wagby.controller.customer;
import java.io.IOException;
import javax.servlet.ServletException;
import jp.jasminesoft.jfc.ActionParameter;
import jp.jasminesoft.jfc.core.exception.BusinessLogicException;
import jp.jasminesoft.jfc.error.Jfcerror;
import jp.jasminesoft.jfc.service.JFCEntityService;
import jp.jasminesoft.util.ExcelFunction;
import jp.jasminesoft.wagby.app.jnews.JnewsHelper;
import jp.jasminesoft.wagby.model.jnews.Jnews;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
/**
 * ShowCustomerController のカスタマイズクラスです。
 *
 * @author JasmineSoft
 * @version $Revision$ $Date$
 */
@Controller
public class MyShowCustomerController extends ShowCustomerController {
    /** {@inheritDoc} */
    @Override
    public String do_original(ActionParameter p)
        throws IOException, ServletException, SecurityException {
        if ("Original1".equals(p.action)) {
            return do_myprocess1(p);
        }
        return null;
     }
    /**
     * オリジナル処理を実行します。
     * @param p {@link ActionParameter}
     * @return view
     */
    public String do_myprocess1(final ActionParameter p)
            throws IOException, ServletException {
        try {
            // Spring が提供する TransactionTemplate を使って、
            // @Transactional アノテーションを用いずにプログラム実装による
            // トランザクション機能を実現します。
            TransactionTemplate transactionTemplate = p.appctx.getBean(
                    "RequiredTransactionTemplate", TransactionTemplate.class);
            // トランザクション処理結果の戻り値が必要な場合は
            // TransactionCallback クラスを利用します。
            // 今回は戻り値が不要なので TransactionCallbackWithoutResult クラス
            // を使っています。
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                /** {@inheritDoc} */
                @Override
                protected void doInTransactionWithoutResult(
                        TransactionStatus status) {
                    // トランザクションが開始された状態で本メソッドが実行され、
                    // メソッド内で Exception が発生すると自動的にロールバック
                    // されます。Exception が発生せずに本メソッドが正常終了する
                    // と自動的にコミットされます。
                    JnewsHelper helper = p.appctx.getBean(
                            "JnewsHelper", JnewsHelper.class);
                    @SuppressWarnings("unchecked")
                    JFCEntityService<Jnews, Integer> service
                            = (JFCEntityService<Jnews, Integer>) p.appctx
                                    .getBean("JnewsEntityService");
                    // 1件目のデータ登録
                    Jnews jnews = new Jnews();
                    helper.initialize(jnews, p);
                    jnews.setLimitdate(ExcelFunction.TODAY());
                    jnews.setTitle("お知らせ1");
                    service.insert(jnews);
                    // 2件目のデータ登録
                    jnews = new Jnews();
                    helper.initialize(jnews, p);
                    jnews.setLimitdate(ExcelFunction.TODAY());
                    jnews.setTitle("お知らせ2");
                    service.insert(jnews);
                }
            });
        } catch (Exception e) {
            Jfcerror error = new Jfcerror();
            error.setContent("エラーが発生しました。" + e.getMessage());
            p.errors.addJfcerror(error);
        }
        // 元の画面へ遷移する。
        String id = p.request.getParameter("customerid");
        return "redirect:showCustomer.do?customerid=" + id;
    }
}
  • このサンプルコードは、開発者が用意した customer モデルの独自ボタン押下時に、システムが標準で提供する「お知らせ (jnews) モデル」への2件のデータ登録を行なうものです。
  • Spring が提供する TransactionTemplate を使って、@Transactional アノテーションを用いずにプログラム実装によるトランザクション機能を実現しています。
  • トランザクション処理結果の戻り値が必要な場合は TransactionCallback クラスを利用します。今回は戻り値が不要とし、TransactionCallbackWithoutResult クラスを使っています。
  • トランザクションが開始された状態で本メソッドが実行され、メソッド内で Exception が発生すると自動的にロールバックされます。Exception が発生せずに本メソッドが正常終了すると自動的にコミットされます。

TransactionCallback内でHibernateのセッションを取得する

TransactionCallbackWithoutResult や TransactionCallback 内部で Hibernate のセッションオブジェクトを取得する場合は、次のようにしてください。

org.hibernate.Session session = p.appctx.getBean("sessionFactory").getCurrentSession();

自動生成されたServiceを拡張する

接頭語 "My" を付与することで、自動生成されたServiceクラスを拡張することができます。

ここでは、在庫管理のトランザクション処理を実現するためのコード例を示します。業務のイメージは「リポジトリ > 業務ロジック > モデル間の計算(トランザクション)」をお読みください。

この文中で説明したトランザクションスクリプトを Java コードで記述した例は次のとおりです。

package jp.jasminesoft.wagby.app.syukko;
import jp.jasminesoft.jfc.app.EntityHelper;
import jp.jasminesoft.jfc.core.exception.BusinessLogicException;
import jp.jasminesoft.jfc.service.JFCEntityService;
import jp.jasminesoft.wagby.model.syukko.Syukko;
import jp.jasminesoft.wagby.model.zaiko.Zaiko;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
/**
 * MySyukkoEntityService.
 *
 * @author JasmineSoft
 * @version $Revision$ $Date$
 */
public class MySyukkoEntityService extends SyukkoEntityService {
    /** ZaikoEntityService */
    @Autowired
    @Qualifier("ZaikoEntityService")
    protected JFCEntityService<Zaiko, Integer> zaikoEntityService;
    /** ZaikoHelper */
    @Autowired
    @Qualifier("ZaikoHelper")
    protected EntityHelper<Zaiko, Integer> zaikoHelper;
    /** {@inheritDoc} **/
    @Override
   protected void updateRelatedModelsInsertTransaction(Syukko syukko) {
       super.updateRelatedModelsInsertTransaction(syukko); // 既存処理の呼び出し
        // 在庫データの取得(第二引数をtrueとすることで悲観ロックを同時に行う)
        Zaiko zaiko = zaikoEntityService.findById(syukko.getShohinId(), true);
        // 業務処理 在庫チェック
        int suryou = zaiko.getSuryou();
        int syukkoNum = syukko.getSyukkoNum();
        if (suryou - syukkoNum < 0) {
            throw new BusinessLogicException("在庫 " + suryou + " に対して "
                    + syukkoNum + " を出庫しようとしました。");
        }
        // 業務処理 在庫数の調整処理
        zaiko.setSuryou(suryou - syukkoNum);
        zaikoEntityService.update(zaiko);
        // 更新処理の後処理(キャッシュの削除等)
        zaikoHelper.afterUpdate(zaiko,
                getActionParameterContainer().get());
    }
}
  • サービスクラスが、別のサービスクラスを利用することができます。必要なサービスクラスは Autowired アノテーションによって取得できます。このとき、同時にサービス名を Qualifier アノテーションで指定してください。
  • 上述の方法で自モデル以外のサービスクラスやヘルパクラスを取得する場合、型パラメータの第一引数は対象クラスになります。第二引数は主キーの型になります。整数型の場合は Integer、文字列型の場合は String などと指定します。
  • 更新系の処理を行った場合は、対象モデルのヘルパクラスの afterUpdate メソッドを呼び出してください。キャッシュの削除等、Wagbyが必要とする後処理をまとめて実行できます。(なお、自分のサービス、ここでは Syukko モデルについては記述不要です。自動生成されたコードで対応されているためです。)
  • 上の例では、findById メソッドの第二引数を true としています。この場合、処理のタイミングで悲観ロック処理を適用します。このあと、update メソッドを呼び出しているためロックは適切に解除されます。

トランザクション処理用メソッド

参照連動項目(参照先保存)および、外部キーモデルのために提供しているトランザクション処理用メソッドは次のとおりです。トランザクション処理を実現するためのカスタマイズコードはこれらのメソッドを利用してください。

登録処理時に関連データの更新を行う

updateRelatedModelsInsertTransaction(E)

更新処理時に関連データの更新を行う

updateRelatedModelsUpdateTransaction(E)

削除処理時に関連データの更新を行う

updateRelatedModelsDeleteTransaction(E)

外部キーモデル削除処理時に関連データの削除を行う

cascadeDelete(E)

なお、このタイミングで呼び出されるスクリプトを記述することもできます。登録/更新/削除時の関連データの処理は Java を使わなくともスクリプトで実現可能です。こちらの利用もご検討ください。[詳細...]

サブデータベースへの Hibernate Session を取得する

サブデータベースへ接続を行う場合は、org.hibernate.Session の取得方法が異なります。 Wagby に同梱されているクラス HibernateUtil の openSession メソッドの第一引数に、サブデータベースを指定します。

サブデータベース 指定名
サブデータベース1sessionFactory2
サブデータベース2sessionFactory3
サブデータベース3sessionFactory4

ActionParameterが利用できる場合

カスタマイズコードで (ActionParameter のインスタンス) "p" が利用できる場合は次のようになります。

org.hibernate.Session sess
   = jp.jasminesoft.jfc.app.HibernateUtil.openSession("sessionFactory2", p.appctx);

ActionParameterが利用できない場合

JSP などへカスタマイズコードを埋め込む場合は、ServletContext から (Springの) applicationContext を取得してください。

ServletContext sc = session.getServletContext();
WebApplicationContext appctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
org.hibernate.Session sess
   = jp.jasminesoft.jfc.app.HibernateUtil.openSession("sessionFactory2", appctx);