The Query<T>
notifier provides
notification for queries initiated by an application using the
KeyQuery<T>
class. The methods in this notifier
are called on all nodes that are part of a distributed query on which
Query<T>
notifiers are installed. If a notifier
is not installed on a node participating in a distributed query, the query
succeeds without calling a notifier on that node.
Query notifier methods are always called, even if an application query would have been satisified by managed objcts in memory. The query notifier methods are responsible for determining whether a query should be passed to a secondary store or not based on the current contents of memory.
These methods are executed synchronously before the
KeyQuery<T>
API calls return to the application.
The methods in this notifier control the query result set by creating and
deleting managed objects as required.
The methods supported are:
query
- called for
KeyQuery<T>.getOrCreateSingleResult(...)
,
KeyQuery<T>.getResults(...)
, and
KeyQuery<T>.getSingleResult(...)
.
queryMaximum
- called for
KeyQuery<T>.getMaximumResult(...)
.
queryMinimum
- called for
KeyQuery<T>.getMinimumResult(...)
.
notifiers are
installed for a specific key. They must be installed on the type that
defines the key. They are inherited by all children of the type on which
they are installed. The Query<T>
klass
parameter to the methods
can be used to determine the actual type on which the query is being
performed.
The queryData
parameter provides details on the
application specified query information. It is used to reconstruct the
query in the notifier.
The application requested LockMode
is in the
lockMode
parameter. The notifier should attempt to
honor the requested lockMode
, but there is no
enforcement.
Result set ordering is the responsibility of the TIBCO ActiveSpaces® Transactions runtime, not the query notifiers. Any ordering specification on the application query is not available to the query notifiers as that information is not needed.
The Example 10.5, “Query notifier” shows how the application specified query is reconstructed in the query notifier methods.
Example 10.5. Query notifier
// $Revision: 1.1.2.4 $ package com.kabira.snippets.store; import com.kabira.platform.KeyComparisonOperator; import com.kabira.platform.KeyFieldValueList; import com.kabira.platform.KeyFieldValueRangeList; import com.kabira.platform.KeyManager; import com.kabira.platform.KeyOrderedBy; import com.kabira.platform.KeyQuery; import com.kabira.platform.LockMode; import com.kabira.platform.Transaction; import com.kabira.platform.annotation.Key; import com.kabira.platform.annotation.Managed; import com.kabira.store.KeyField; import com.kabira.store.Query; import java.util.ArrayList; /** * Secondary store query notifier * <p> * <h2> Target Nodes</h2> * <ul> * <li> <b>domainnode</b> = A * </ul> */ public class QueryNotifier { @Managed @Key(name = "ByName", unique = true, ordered = true, fields = { "m_name" }) private static class A { private A(final String name) { m_name = name; } private final String m_name; }; private static class B extends A { public B(final String name) { super(name); } }; // // Query notifier // private static class Notifier extends Query<A> { public Notifier(final String keyName) { super(A.class, keyName); } @Override public void query( Class<? extends A> klass, LockMode lockMode, ArrayList<KeyField> queryData) { System.out.println("INFO:\t" + prettyPrintQuery( "object", klass.getSimpleName(), getKeyName(), lockMode, queryData)); } @Override public void queryMinimum( Class<? extends A> klass, LockMode lockMode, ArrayList<KeyField> queryData) { System.out.println("INFO:\t" + prettyPrintQuery( "minimum", klass.getSimpleName(), getKeyName(), lockMode, queryData)); } @Override public void queryMaximum( Class<? extends A> klass, LockMode lockMode, ArrayList<KeyField> queryData) { System.out.println("INFO:\t" + prettyPrintQuery( "maximum", klass.getSimpleName(), getKeyName(), lockMode, queryData)); } } /** * Main entry point * @param args Not used */ public static void main(final String[] args) { initialize(); uniqueQuery(); minimumMaximumQuery(); rangeQuery(); } // // Initialize notifier and create some test data // private static void initialize() { new Transaction("Initialize") { @Override protected void run() throws Transaction.Rollback { new Notifier("ByName"); for (int i = 0; i < 20; i++) { new A(Integer.toString(i)); } for (int i = 20; i < 30; i++) { new B(Integer.toString(i)); } } }.execute(); } // // Perform a unique query // private static void uniqueQuery() { new Transaction("Unique Query") { @Override protected void run() throws Transaction.Rollback { KeyManager<B> km = new KeyManager<>(); KeyQuery<B> kq = km.createKeyQuery(B.class, "ByName"); KeyFieldValueList values = new KeyFieldValueList(); values.add("m_name", "22"); kq.defineQuery(values); System.out.println("INFO: getSingleResult " + kq); B b = kq.getSingleResult(LockMode.NOLOCK); } }.execute(); } // // Perform a range query // private static void rangeQuery() { new Transaction("Range Query") { @Override protected void run() throws Transaction.Rollback { KeyManager<B> km = new KeyManager<>(); KeyQuery<B> kq = km.createKeyQuery(B.class, "ByName"); KeyFieldValueRangeList values = new KeyFieldValueRangeList(); values.add("m_name", "22", KeyComparisonOperator.GT); values.add("m_name", "28", KeyComparisonOperator.LTE); kq.defineQuery(values); System.out.println("INFO: Range Query " + kq); for (B b : kq.getResults(KeyOrderedBy.ASCENDING, LockMode.NOLOCK)) { break; } } }.execute(); } // // Perform minimum and maximum queries // private static void minimumMaximumQuery() { new Transaction("Minimum/Maximum Query") { @Override protected void run() throws Transaction.Rollback { KeyManager<A> km = new KeyManager<>(); KeyQuery<A> kq = km.createKeyQuery(A.class, "ByName"); System.out.println("INFO: Get Maximum Result"); A a = kq.getMaximumResult(LockMode.READLOCK); KeyFieldValueRangeList values = new KeyFieldValueRangeList(); values.add("m_name", "8", KeyComparisonOperator.GTE); kq.defineQuery(values); System.out.println("INFO: Get Minimum Result " + kq); a = kq.getMinimumResult(LockMode.NOLOCK); } }.execute(); } // // Pretty print query // private static String prettyPrintQuery( final String queryType, final String className, final String keyName, LockMode lockMode, ArrayList<KeyField> queryData) { StringBuilder builder = new StringBuilder(); builder.append("select "); builder.append(queryType); builder.append(" from "); builder.append(className); builder.append(" using "); builder.append(keyName); builder.append(prettyPrintQueryData(queryData)); builder.append(" with "); builder.append(lockMode); return builder.toString(); } // // Pretty print query data // private static String prettyPrintQueryData(final ArrayList<KeyField> queryData) { if (queryData.isEmpty() == true) { return ""; } StringBuilder builder = new StringBuilder(); boolean first = true; builder.append(" where ("); for (KeyField f : queryData) { if (first == true) { first = false; } else { builder.append(" && "); } builder.append(f.name); builder.append(" "); builder.append(f.comparisonOperator); builder.append(" "); builder.append(f.value); } builder.append(")"); return builder.toString(); } }
When Example 10.5, “Query notifier” is run it outputs these messages (annotations added):
# # Unique query on managed object B # INFO: getSingleResult select obj from com.kabira.snippets.store.QueryNotifier$B using ByName where (m_name == 22); Java constructor: (none) INFO: select object from B using ByName where (m_name == 22) with NOLOCK # # Maximum result query on managed object A # INFO: Get Maximum Result INFO: select maximum from A using ByName with READLOCK # # Minimum result query with a start range on managed object A # INFO: Get Minimum Result for obj in com.kabira.snippets.store.QueryNotifier$A using ByName where (m_name >= 8) { } INFO: select minimum from A using ByName where (m_name >= 8) with NOLOCK # # Range query on managed object B # INFO: Range Query for obj in com.kabira.snippets.store.QueryNotifier$B using ByName where (m_name > 22 && m_name <= 28) { } INFO: select object from B using ByName where (m_name > 22 && m_name <= 28) with NOLOCK