Describe how to customize Service class and Dao class for database operation.

Service class and Dao class are prepared for each model.

The Service class internally uses the Dao class.The Service class is a transaction boundary, and the actual database operation is done by the Dao class.In addition to that, the Dao class creates Criteria for cache control and inquiries.

Figure 1 Service class and Dao class
In Wagby R6, the class called "process bean" was responsible, but in R7 it was separated into the Service/Dao layer.Even with R7 some process bean remains in some places, but we will lose it as we upgrade in the future.Therefore, customization using process bean is deprecated.

It is a class using the ORM framework Hibernate.CRUD processing is realized with one Dao class. Inquiries to the database use "Criteria query API" provided by Hibernate.There is no place to generate SQL directly.

Figure 2 Interface of Dao class

Here is an example of using the Dao class.

// 主キー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);

About lock

In the above example, when calling the customerDao.get method and deleteById method, the second argument is set to true.In this case, apply pessimistic lock processing at the timing of processing.When locked,If this data is updated, the lock is released at the end of the transaction, but if it is not updated, the lock will remainIt will become.If you want to retrieve data that does not need updating with reference only processing, set the second argument of get method to false.

The second argument of the findById method of the EntityService, which will be described later, also has the same behavior.

It is a transaction boundary class.In the class that implements the interface, @Transactional annotation is given to each method.

The transaction is started automatically when the method starts.It is committed with normal termination.It rolls back automatically when an Exception occurs.

Figure 3 Interface of Service class

EntityService, Dao, Helper can be acquired outside the method by declaring instance variables as follows.

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;

The first argument of the type parameter of each class is the target class. The second argument is the type of the primary key. Specify Integer for integer type, String for string type, and so on.

Wagby realizes database search using Criteria provided by Hibernate. In addition, we realize code completion using IDE such as Eclipse and automatically generate Meta class for the purpose of preventing typing errors.

Here is a code example using Criteria.

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 class

It manages character strings of item names, table names, and column names at once.We get these values ​​through the Meta class.

Figure 4 Meta class

CriteriaConverter

Wagby automatically generates a condition model that holds search conditions.To obtain Criteria from the condition model, use CriteriaConverter.

Here is an example of using CriteriaConverter.

// CriteriaConverter インスタンスを作成
CustomerCriteriaConverter converter
        = (CustomerCriteriaConverter) p.appctx.getBean(
                "CustomerCriteriaConverter");
// Condition を Criteria に変換
DetachedCriteria criteria = converter.convert(condition, sortKey);
// Criteria を使って検索を実行
List<Customer> results = dao.findByCriteria(criteria);

You can also retrieve a CriteriaConverter instance by declaring instance variables outside the method as follows:

/** CriteriaConverter インスタンスを作成 */
@Autowired
@Qualifier("CustomerCriteriaConverter")
protected CriteriaConverter<CustomerC> converter;
CriteriaConverter's type parameter argument is a condition model.

paging

The findByCriteria method provided by the Dao class corresponds to paging processing.It also provides a FinderContext class to remember the current page.

Here is an example of using the findByCriteria method.

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);

Here is an example of using 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);

Acquisition of number of cases

By setting finderContext as an argument of the count method provided by Dao class, the number of cases can be obtained.

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

int size = dao.count(finderContext);

Cache control

Wagby actively uses cash internally to improve performance. It uses Hibernate 's Interceptor function, and processing such as clearing the cache at the timing of transaction commit is automatically performed.

A compound key class is prepared as an inner class of the store model.
If the zaiko model is a compound key, it will be described as follows.

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

In the Dao class you can get the database session using the getCurrentSession method.You can also write SQL directly using this.

Here is an example of preparing a MyDao class that customized auto-generated Dao class and calling the stored procedure of the database at the timing of getting data (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);
    }
}

The database session acquired via getCurrentSession () is under the control of Spring's TransactionManager.Therefore, developers should not close the database session.However, close processing is necessary for Statement and ResultSet prepared independently by developers.In this case you should use DbUtils.closeQuietly.

Here is an example of code that overrides the afterUpdate method of the helper class and performs stored procedure call (or some sort of SQL processing) at this timing.

   @Override
   public void afterUpdate(Model1 entity, final DbActionParameter 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 automatically closes the Hibernate Session acquired via TransactionManager, but Hibernate Session acquired using HibernateUtil.openSession () as described above must be explicitly closed by code developer .

It is the same framework as above, but also shows an example of calling SQL.(Exactly not HQL, but HQL provided by Hibernate.)

   @Override
   public void afterUpdate(Model1 entity, final DbActionParameter 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);
   }

Details will be Hibernate programming, so we will omit it.

By adding the prefix "My" you can extend the automatically generated Service class.

Here is an example of code to realize transaction processing of stock management.The image of work is "repository> business logic> Cross-model calculation (transaction)Please read.

I explained in this sentenceTransaction scriptIs written in Java code is as follows.

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,
                getDbActionParameterContainer().get());
    }
}
  • The service class can use another service class.The required service class can be obtained by Autowired annotation.At the same time, please specify the service name with Qualifier annotation.
  • When acquiring a service class or helper class other than your own model in the above method, the first parameter of the type parameter is the target class.The second argument is the type of the primary key.Specify Integer for integer type, String for string type, and so on.
  • When updating processing is done, please call the afterUpdate method of helper class of target model.You can execute postprocessing required by Wagby at once, such as deletion of cache.(Note that it is unnecessary to describe your own service, here Syukko model.This is because it is supported by automatically generated code.)
  • In the above example, the second argument of the findById method is set to true.In this case, apply pessimistic lock processing at the timing of processing.After this, the lock is released properly because the update method is called.

Transaction processing method

The transaction processing methods provided for the reference linkage item (reference destination storage) and the foreign key model are as follows.Please use these methods for customization code to realize transaction processing.

Update related data at registration processing

UpdateRelatedModelsInsertTransaction (E)

Update related data at update processing

UpdateRelatedModelsUpdateTransaction (E)

Update related data at the time of deletion processing

updateRelatedModelsDeleteTransaction(E)

Delete related data at the time of foreign key model deletion processing

cascadeDelete(E)
When customizing Service/Dao class, please consider taking "customized education course" provided by Jasmine Software.We provide appropriate advice according to customization content.Please inquire details.

When connecting to the sub database, the method of obtaining org.hibernate.Session differs. Specify the sub database as the first argument of the openSession method of the class HibernateUtil that is included in Wagby.

Sub database Designated name
Sub database 1sessionFactory2
Sub database 2sessionFactory3
Sub database 3sessionFactory4

When DbActionParameter is available

In the customization code (instance of DbActionParameter) If "p" is available, it looks like this:

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

When DbActionParameter can not be used

To embed the customization code into JSP etc., please obtain (application of Spring) applicationContext from ServletContext.

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