Support > Repository > Business logic > Using Service/Dao class, helper, SQL
ja | en

You can use the Service/Dao class (automatically generated by Wagby) from the script. You can also use SQL. R7.6

Wagby automatically generates a "service class" corresponding to the model.This is a transaction boundary and provides methods for CRUD processing.Detail is"Service/Dao classPlease read.

Figure 1 Service class and Dao class

In the script, you can retrieve the Service object with the following code:

Var entityService = p.appctx.getBean (Service object name);

The service name is "<model ID> EntityService".The model ID isCamel notationIt expresses with.For example, the Service object name for the customer model is "CustomerEntityService".

I will show specific code examples.

var entityService = p.appctx.getBean("JuserEntityService");
var user = entityService.findById("user01", true); /* 1件のデータを取得 */
stdout.println(user); /* デバッグ用ログ出力 */
user.name = "ジャスミン太郎";
entityService.update(user); /* 更新 */
  • Since the service object is a transaction boundary, it is committed to the database at the timing of the update method invocation.
  • The second argument of the findById method indicates that locking is to be performed.For details, see "Wagby Developer Network (R7)> Customizing with Java> Service/Dao class> About lockPlease read.

This allows you to retrieve and update data of any model.

Notes on lock: In the above code, the lock obtained by findById is always released by executing the update method after that, so there is no problem.However, if you acquire the lock, but do not execute the update method, the lock will be retained even when this script exits.Details and solutions are described in "Acquiring and releasing locksPlease read.

Transaction scriptThen, the transaction is started before running this script.At this time, you can acquire and update data using Dao on any model.

In the script, you can get Dao using the following code.

Var dao = p.appctx.getBean (Dao name);

The Dao name is "<model ID> Dao".The model ID isCamel notationIt expresses with.For example, the Dao name for the customer model is "CustomerDao".

I will show specific code examples.

var dao = p.appctx.getBean("JuserDao");
var user = dao.get("admin", true); /* 1件のデータを取得 */
stdout.println(user); /* デバッグ用ログ出力 */
user.name = "ジャスミン太郎";
dao.update(user); /* 更新 */

Dao does not include transaction declaration.Therefore, the transaction must be started before the dao.update method.And you must end the transaction after the dao.update method.

Because of this constraint, you can only use Dao in transaction scripts.Transaction scriptBecause transaction start and end are automatically executed before and after the call, update processing by Dao is processed correctly.

Summary: Both Service objects and Dao are available in transaction scripts.For scripts other than transaction scripts, use the Service object.(The database is committed each time an update method is executed.)
The second argument of dao.get indicates acquisition of lock.For details, see "Wagby Developer Network (R7)> Customizing with Java> Service/Dao class> About lockPlease read.

Middleware for database operation Wagby uses internally You can execute arbitrary SQL using the session object provided by Hibernate.Within a transaction script you can use this session variable (object).

The following example executes SQL to find the number of account models (juser).

try {
    if (session != null) {
        var o = session.createSQLQuery(
           "SELECT COUNT(*) FROM \"juser\"").uniqueResult();
        stdout.println("o="+o);/* デバッグ用 */
    }
} catch (e) {
    e.printStackTrace();
}
  • The return type of createSQLQuery depends on your database.Integer, Long, BigInteger etc. possibility.[More ...]
  • You can not use "session" for model name (English).This name is reserved (as an implicit object).
  • The session used here is the class provided by Hibernate.Use of session conforms to how to use Hibernate.
  • Session is always valid in transaction scripts.Other scripts may be invalid depending on where you call.Therefore, when using it in places other than transaction scripts, please make a judgment as to whether session is null as in the example above.
  • In the example above, we enclose item names and table names in double quotes, but this rule will vary depending on your database.(When using HSQLDB which is the built-in DB, enclose it in double quotation marks.) Therefore, if different databases are used for the development machine and the production machine, it may be necessary to change the script file.Please be careful.
  • It is recommended to use SQL only in search system.In case of update system, please use Service or Dao instead of SQL.Database locking processing and cache processing are done automatically.When updating with SQL, it is necessary for the developer to write control concerning transaction management, locking, and caching.(Because the procedure becomes complicated, explanation is omitted in this manual.)
It is not necessary to close database connections (sessions) available in transaction scripts.It will automatically close internally.

Use session in places other than transaction script

Transaction scriptOther thanIf you need to use session at the location of, please check whether session can be used first.If session is null, use HibernateUtil.openSession () in the script to obtain it.Here is an example.

var HibernateUtil = Java.type("jp.jasminesoft.jfc.app.HibernateUtil");
var session = HibernateUtil.openSession();
try {
   /* session を利用した処理を実装する */
   ...
} catch (e) {
   e.printStackTrace();
} finally {
   if (session !== null) {
       session.close();/* 忘れないこと */
   }
}
Be sure to close processing connections that developers have opened independently.If you forget to close it, you will run out of available database sessions, resulting in a runtime error during operation.
> Customization using Java> Database> Monitor the database connection pooling countPlease read.

You can use helper classes (generated automatically by Wagby) in scripts.In addition to reusing the initial values ​​and expressions described in the design information, you can reuse the conversion between the store model and the presentation model using the s2p and p2s methods.

Here is an example.We convert the store model called quotation into a presentation model and store it in a variable po.

var IPresentationHelper = Java.type("jp.jasminesoft.jfc.IPresentationHelper");
var helperClass = p.appctx.getBean("QuotationPHelper");
var po = helperClass.s2p(quotation, p, IPresentationHelper.SHOW);

stdout.println(po);
  • We also get helper class with p.appctx.getBean.
  • The jp.jasminesoft.jfc.IPresentationHelper interface provides four modes: the constant SHOW, UPDATE, CSVDOWNLOAD, and CREATEOBJECT.Usually you use SHOW or UPDATE.Passing SHOW to the s2p method converts it to the presentation model for display.Passing UPDATE converts it to a presentation model that can be used on the update screen.This includes candidate choices.
Store models and presentation models are provided by Wagby.Detail is"Customization using JavaPlease read the slide prepared in ".In order to properly understand this content, we recommend that you take a technical seminar provided by Jasmine Software.

Problems when pessimism lock is used

The following code is a sample that performs update processing depending on conditions.This code has a problem with lock processing.(Here,When using pessimistic lockIt is a description of.)

var entityService = p.appctx.getBean("JuserEntityService");
var user = entityService.findById("user01", true);
if (user.kind == 1) {
  user.name = "ジャスミン太郎";
  entityService.update(user);
}

Specifically, if the condition is not met, the update method will not be called.At this time, since lock was acquired because the second argument of findById was set to true, there is a possibility that the script ends without this lock being released.

If unlocking is not done, this lock will remain on until menu display or logoff.

Wagby's lock is automatically canceled at menu screen display or logoff.

Solution

Instead of obtaining the lock with the findById method, you should get the lock just before the update method.Here is a code example.

var LockUtils = Java.type("jp.jasminesoft.jfc.core.util.LockUtils");
var userDaoHelper = p.appctx.getBean("JuserDaoHelper");
var entityService = p.appctx.getBean("JuserEntityService");

var user = entityService.findById("user01");
if (user.kind == 1) {
  try {
    LockUtils.lock(user, userDaoHelper);/*ロック取得*/
    user.name = "ジャスミン太郎";
    entityService.update(user);
  } catch (e) {
    LockUtils.release(user, userDaoHelper);/*ロック解放*/
  }
}
  • The LockUtils class is bundled with Wagby.We use lock and release methods.
  • The target object is specified as the first argument.For the second argument, specify the DaoHelper class of the target object.

When retrieving multiple items of data using Service or Dao, you can specify arbitrary search conditions using criteria.Even items that do not validate search conditions in model design can be specified as criteria (conditions).

However, if the search condition is not enabled in the model definition, the index is not created in the database.Please create indexes manually if necessary.

Prepare criteria

Use CriteriaConverter prepared for each model.Here is a code example.

var criteriaConverter = p.appctx.getBean("Model1CriteriaConverter");
var criteria = criteriaConverter.defaultCriteria();

Use a metaclass

We set search criteria in criteria using Meta class prepared for each model.Here is a code example.

var Model1Meta = Java.type("jp.jasminesoft.wagby.model.model1.Model1Meta");
var model1Meta = new Model1Meta();
criteria.eq(model1Meta.item1, 1000); /* item1 の値が 1000 のデータで絞り込む */

Eq are equal to each other.For details of search conditions, "search control> [application] customize search conditions with script> Available search conditionsPlease read.

Search for

When using the Service object, pass criteria as the first argument.The return value is a list, and multiple store models are stored.

var list = Model1Service.find(criteria);

if (list != null && list.size() > 0) {
  /* 処理 */
}

Specify sorting

You can use the Order class to specify sort criteria for criteria.Here is a code example.

var OrderClass = Java.type("org.hibernate.criterion.Order");
var order = OrderClass.desc(Model1Meta.start_date.name());
criteria.addOrder(order);

/*criteriaを使って検索する*/
  • The Order class can use the asc and desc methods.They are ascending and descending specifications, respectively.
  • The argument of the asc/desc method uses the item name of the Meta class.By setting item name. Name (), the appropriate item name will be passed.When using the Meta class, please do not use Camel notation for the item name part, please use the item ID described in the design information as it is.(In this example, it is start_date instead of startDate.)
  • In the above example, one order is specified, but you can specify multiple sort order by passing an array of criteria.addOrder to order.

Customize criteria in script

You can also customize criteria with scripts.For details, refer to "Search control> [Advanced] Customize search conditions with scriptPlease read.

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.

var Zaiko = Java.type("jp.jasminesoft.wagby.model.zaiko.Zaiko");
var key = new Zaiko.Key();
key.pkey1 = xxx;/* xxx には、主キーの値が入ります */
key.pkey2 = yyy;/* yyy には、主キーの値が入ります */
Zaiko zaiko = zaikoEntityService.findById(key,true);/* 第二引数がtrueのときロックをかける */

In a transaction script, you can roll back the process by returning an arbitrary string in the return statement.

It is not a transaction script, but you can also roll back the process with a script called within a series of transactions (eg helper script).I will explain this method.

BusinessLogicException

You can roll back this transaction by throwing a BusinessLogicException in the script.

Throw new Packages.jp.jasminesoft.jfc.core.exception.BusinessLogicException ("Error Message");

Error messages are displayed on the screen.

Target script

A script that can use this method is effective when it is a helper system and session (Hibernate's session) is not null.

Before applying,if (session != null) {...} Please check whether session is valid.

For transactions, assume the following example.

  • "Salesslip" model and "sales product (product 4s)" model.
  • The sales slip holds, as details, which items have been sold.
  • I would like to reduce the number of inventory of products sold at the time of newly registering sales slips.(transaction)
Figure 2 Sales document model and sales product model

Transaction script when new sales slip is created

"Product code" included in the item (repeating container) of the sales slip model is a reference item to the sales product model.Write a transaction crypto in this field.

The content you want to execute is to reduce the quantity (called how many purchases) included in the statement at the time of creating the sales slip from the product inventory.

Figure 3 Transaction script for new registration

The concrete transaction script is as follows.

var suryou = product4s.stock;
var syukko_num = precord.PNumber;
/*stdout.println("suryou="+suryou+",syukko_num="+syukko_num);*/
if (suryou - syukko_num < 0) {
    return precord.PName + "の在庫 "+suryou+" に対して "+syukko_num+" を出庫しようとしました。";
}
product4s.stock = suryou - syukko_num;
return null;
  • Product4s.stock refers to the item number in stock item model.
  • Precord points to a line of detail (repeating container).If there are three record lines, the script above is executed three times.
  • The quantity of sales p_number of the repeat container precord is subtracted from the corresponding (sales merchandise model) stock.
  • Camel notation is applied to the item name.P_numebr is expressed as PNumber in the script.
  • It returns null if the process is successful.

Transaction script when sales slip is updated

Update script is not the same as registration.

In the case of updating, it is necessary to recalculate the stock number by taking the difference between "value once registered" and "value changed on the update screen". Here, the value once registered refers to the saved value of the database. In other words, it is processing to find the difference between the value on the database and the value re-entered from the screen.

For comparison with data to be updatedWhen acquiring the data before update from the database it is necessary to acquire it in separate transaction.

For this correspondence, please use the mechanism to perform get processing in another transaction newTransactionEntityService.Here is a code example.

Figure 4 Transaction script at update
var entityService = p.appctx.getBean("SalesslipEntityService");
var newTransactionEntityService = entityService.newTransactionEntityService();/*別トランザクションになる*/
var o_salesslip = newTransactionEntityService.findById(salesslip.id, false);/* salesslip.idは主キー */

var o_precords = o_salesslip.precord;
var o_precord;

for (var i=0; i<o_precords.length; i++) {
    o_precord = o_precords[i];
    if (o_precord.PNo == precord.PNo) {
        break;
    }
}
if (o_precord != null) {
    var suryou = product4s.stock;
    var syukko_num = precord.PNumber - o_precord.PNumber;
    /*stdout.println("now suryou="+suryou+",syukko_num_diff="+syukko_num);*/
    if (syukko_num != 0) {
        if (suryou - syukko_num < 0) {
	        return precord.PName + "の在庫 "+suryou+" に対して "+syukko_num+" を出庫しようとしました。";
        }
        product4s.stock = suryou - syukko_num;
    }
}
return null;
  • From the service object, get the value of the database.We store this in a variable called o_salesslip.Find gets a single key by passing the primary key.
  • Services that can be retrieved via the newTransactionEntityService method areAcquisition onlyis.Registration/update/deletion processing etc. can not be performed.(*)
  • Use a for loop to find what matches the line you are currently targeting.
  • For the matched line, find the difference from the input value from the screen.This difference is subtracted from inventory count again.
In this example, we allow you to update the sales slip once we have registered, but there are scenarios where we do not allow updates in actual business.There is also a scenario in which new vouchers are created for change, not renewal.Please read this code as an example.
In the above example, we used a service object to retrieve the value of the database.Another approach is to use SQL expressions to retrieve database values.For details, see "SQL expression> Prepare an item to hold the value before savingPlease read.
I use @ Transactional (propagation = Propagation.REQUIRES_NEW) of Spring.

Continue with the explanation of the transaction script at the time of update described in Figure 4.In this script, apart from the value entered, the value currently held in the database is acquired using Dao.Using Dao will resolve all reference linking items as well.Therefore, the SQL issued to the database increases in proportion to the number of reference interlocks.

In this example, however, o_precord.PNo and o_precord.PNumber are not reference linked items.Therefore, when using Dao, I will explain how to lead to performance improvement by skipping resolution of unnecessary reference interlocking items.

DataBindingContext

DataBindingContext is a class provided by Wagby.In this class you can specify the item name to solve the reference linkage.Here is an example of how to use it.

var DataBindingContext = Java.type("jp.jasminesoft.jfc.dao.DataBindingContext");

var dao = p.appctx.getBean("SalesslipDao");
var dataBindingContext = new DataBindingContext();
var targetItemSet = new java.util.HashSet();
targetItemSet.add("customerName");/* 解決したい参照連動項目 */
dataBindingContext.setTargetItemSet(targetItemSet);
var o_salesslip = dao.get(salesslip.id, true, dataBindingContext);/* 第3引数に指定 */

In this way, pass the dataBindingContext to the third argument of the get method of dao.If this argument is unspecified, all reference linkage items are resolved.

If the reference linkage item to be solved is an item in the repeating container, the container name is not included.Specify only the item name.

Skip reference linkage processing

To skip all reference interlocking processing, pass an empty dataBindingContext.

var DataBindingContext = Java.type("jp.jasminesoft.jfc.dao.DataBindingContext");

var dao = p.appctx.getBean("SalesslipDao");
var dataBindingContext = new DataBindingContext();
var targetItemSet = new java.util.HashSet();
dataBindingContext.setTargetItemSet(targetItemSet);/* 項目を明示しない */
var o_salesslip = dao.get(salesslip.id, true, dataBindingContext);/* 第3引数に指定 */

An example

An example of adding the code to skip resolution of reference linkage items to the update transaction script in Figure 4 is shown below.

var DataBindingContext = Java.type("jp.jasminesoft.jfc.dao.DataBindingContext");

var dao = p.appctx.getBean("SalesslipDao");
var dataBindingContext = new DataBindingContext();
dataBindingContext.setTargetItemSet(new java.util.HashSet());/* 項目を明示しない */
var o_salesslip = dao.get(salesslip.id, true, dataBindingContext);/* 第3引数に指定 */

stdout.println(o_salesslip);/* コンソールに出力。確認用 */

var o_precords = o_salesslip.precord;
var o_precord;

for (var i=0; i<o_precords.length; i++) {
    o_precord = o_precords[i];
    if (o_precord.PNo == precord.PNo) {
        break;
    }
}
if (o_precord != null) {
    var suryou = product4s.stock;
    var syukko_num = precord.PNumber - o_precord.PNumber;
    /*stdout.println("now suryou="+suryou+",syukko_num_diff="+syukko_num);*/
    if (syukko_num != 0) {
        if (suryou - syukko_num < 0) {
	        return precord.PName + "の在庫 "+suryou+" に対して "+syukko_num+" を出庫しようとしました。";
        }
        product4s.stock = suryou - syukko_num;
    }
}
return null;

You can also execute SQL directly in the transaction script.

I will explain the code to retrieve the quantity of item data using SQL instead of Dao using the above subject.

try {
    if (session != null) {
        /*旧データ読み込み*/
        var sql =
           "SELECT \"p_number\" FROM \"salesslip$precord\"" +
           " WHERE \"id\"=" + salesslip.id + " AND" +
           " \"precordjshid\"=" + (precord.PNo - 1);
        /*stdout.println("sql="+sql);*/
        var o_PNumber = session.createSQLQuery(sql).uniqueResult();
        /*stdout.println("o_PNumber="+o_PNumber);*/
        var suryou = product4s.stock;
        var syukko_num = precord.PNumber - o_PNumber;
        stdout.println("now suryou="+suryou+",syukko_num_diff="+syukko_num);
        if (syukko_num != 0) {
            if (suryou - syukko_num < 0) {
	            return precord.PName + "の在庫 "+suryou+" に対して "+syukko_num+" を出庫しようとしました。";
            }
            product4s.stock = suryou - syukko_num;
        }
    }
} catch (e) {
    e.printStackTrace();
}
return null;
  • Repeat containers are prepared as separate tables.This is the table "model name $ repeating container name".
  • The physical item name of the repeating container ID item is always "repeat container name jshid".
  • The primary key of the repeating container table will be a composite key of the "primary key" of the parent model and "repeat container name jshid".
  • "Repeat container name jshid" starts from 0.So in the above code we refer to precord.PNo - 1 and the value minus 1.