JPOX
JPOX
 Project  |  Ver 1.1  |  Ver 1.2  |  JDO  |  JPA  |  Guides  |  Tools
1.1 | Preparation | O/R Mapping | Runtime | Extensions | Developer
Extensions
JDOQL : UDF

When the JDOQL does not fulfill the querying needs, JPOX permits the user to extend it with user defined methods. The below example shows how to extend the JDOQL language to invoke a custom operation Finance.pmt(..). This custom method calculates payment amounts at interest rate per month.

package org.jpox.samples.finance;

/**
 * Finance operations
 */
public class Finance
{
    /**
     * Calculate payment amount at interest rate per month
     * @param amount borrowed ammount
     * @param rate rate per payment basis (0.66 month = 8% year)
     * @param payments number of payments
     * @return
     */
    public static double pmt(double amount,double rate,int payments)
    {
        double r = rate / 100;
        return ( amount * rate ) * (Math.pow(1+r, payments) / (Math.pow(1+r, payments)-1));
    }
}

With the above class, we will mirror the code into expressions to be evaluted by JDOQL compiler. We have to implement a ScalarExpression class to generate the SQL statement. The rules are:

  • Extends org.jpox.store.expression.ScalarExpression.
  • Add constructor with org.jpox.store.expression.QueryExpression argument.
  • The method to be invoked must return a org.jpox.store.expression.ScalarExpression instance.
  • If the method received arguments, it must be type of org.jpox.store.expression.ScalarExpression.
  • The method name must be postfixed with the word Method.

package org.jpox.samples.finance.expression;

import org.jpox.store.expression.NumericExpression;
import org.jpox.store.expression.QueryExpression;
import org.jpox.store.expression.ScalarExpression;
import org.jpox.store.mapping.JavaTypeMapping;

/**
 * Represents expressions of Finance 
 */
public class FinanceExpression extends ScalarExpression
{

    /**
     * @param qs The query statement
     */    
    protected FinanceExpression(QueryExpression qs)
    {
        super(qs);
    }

    /**
     * Calculate payment amount at interest rate per month
     * Generates a SQL like "(( amount * rate ) * (POWER(1+(rate/100), payments) / (POWER(1+(rate/100), payments)-1)))"
     * @param amount borrowed ammount
     * @param rate rate per payment basis (0.66 month = 8% year)
     * @param payments number of payments
     * @return
     */
    public ScalarExpression pmtMethod(ScalarExpression amount, ScalarExpression rate, ScalarExpression payments)
    {    
        /*
         * double r = rate / 100;
         * double numerator = (Math.pow(1+r, payments)
         * double denominator = (Math.pow(1+r, payments)-1
         * return ( amount * rate ) * (numerator / denominator)
         */

        //define literal 100
        JavaTypeMapping m100 = qs.getStoreManager().getDatastoreAdapter().getMapping(Integer.class,qs.getStoreManager(),qs.getClassLoaderResolver());
        ScalarExpression literal100 = m100.newLiteral(qs, new Integer(100));

        //define literal 1
        JavaTypeMapping m1 = qs.getStoreManager().getDatastoreAdapter().getMapping(Integer.class,qs.getStoreManager(),qs.getClassLoaderResolver());
        ScalarExpression literal1 = m1.newLiteral(qs, new Integer(1));

        //double r = rate / 100;
        ScalarExpression r = rate.div(literal100).encloseWithInParentheses();

        //double numerator = (Math.pow(1+r, payments)
        ScalarExpression numerator = power(literal1.add(r), payments).encloseWithInParentheses();

        //double denominator =  (Math.pow(1+r, payments)-1
        ScalarExpression denominator = power(literal1.add(r), payments).sub(literal1).encloseWithInParentheses();

        // return ( amount * rate ) * (numerator / denominator)        
        return amount.mul(rate).mul(numerator.div(denominator).encloseWithInParentheses()).encloseWithInParentheses();
    }

    /**
     * Creates the expression POWER(expr1,expr2). This method assume the database supports the function POWER
     * @param expr1 the first argument
     * @param expr2 the second argument
     * @return the result
     */
    private ScalarExpression power(ScalarExpression expr1,ScalarExpression expr2)
    {
        ArrayList args = new ArrayList();
        args.add(expr1);
        args.add(expr2);
    
        return new NumericExpression("POWER", args);
    }
}

The FinanceExpression is registered by declaring it in the /plugin.xml file. The below code exemplifies it:

<?xml version="1.0"?>
<plugin id="org.jpox" name="JPOX plug-ins" provider-name="JPOX">
    <extension point="org.jpox.store_expression_scalarexpression">
        <scalar-expression literal-class="org.jpox.samples.finance.Finance" scalar-expression-class="org.jpox.samples.finance.expression.FinanceExpression"/>
    </extension>
</plugin>

After that pack all the classes and the /plugin.xml into a jar and place it in the classpath.

In JDOQL invoke it with Literal.method(args). The below exemplifies:

Query query = pm.newQuery(org.jpox.samples.store.Payment.class);
query.setFilter("amount>Finance.pmt(100000,0.66,360)");
List results = (List)query.execute();