JDOQL is defined by the JDO2 specification and is explicit about what is supported.
You can extend DataNucleus's capabilities using the plugin extension
org.datanucleus.store_expression_scalarexpression
. This is only for RDBMS
|
Plugin extension-point
|
Key
|
Description
|
Location
|
|
org.datanucleus.store_expression_scalarexpression
|
java.lang.Math
|
Use of Math functions for JDO2
|
datanucleus-rdbms
|
|
org.datanucleus.store_expression_scalarexpression
|
javax.jdo.JDOHelper
|
Use of JDOHelper function for JDO2
|
datanucleus-rdbms
|
|
org.datanucleus.store_expression_scalarexpression
|
Analysis
|
Use of Analysis functions
|
datanucleus-rdbms
|
When the JDOQL does not fulfill the querying needs, DataNucleus 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.datanucleus.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.datanucleus.store.expression.ScalarExpression
.
-
Add constructor with
org.datanucleus.store.expression.QueryExpression
argument.
-
The method to be invoked must return a
org.datanucleus.store.expression.ScalarExpression
instance.
-
If the method received arguments, it must be type of
org.datanucleus.store.expression.ScalarExpression
.
-
The method name must be postfixed with the word
Method
.
package org.datanucleus.samples.finance.expression;
import org.datanucleus.store.expression.NumericExpression;
import org.datanucleus.store.expression.QueryExpression;
import org.datanucleus.store.expression.ScalarExpression;
import org.datanucleus.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.datanucleus" name="DataNucleus plug-ins" provider-name="My Company">
<extension point="org.datanucleus.store_expression_scalarexpression">
<scalar-expression literal-class="org.datanucleus.samples.finance.Finance"
scalar-expression-class="org.datanucleus.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.datanucleus.samples.store.Payment.class);
query.setFilter("amount>Finance.pmt(100000,0.66,360)");
List results = (List)query.execute();