blob: 46fad3d9e38f7be04db535b373c7759020fac75c [file] [log] [blame]
Title: Customizing Queries
<P>Any object that implements <TT>org.apache.cayenne.query.Query</TT> interface can be executed with a DataContext. </P>
<H3><A name="CustomizingQueries-UnderstandingQueryInterface"></A>Understanding Query Interface</H3>
<P>The interface defines the following methods (ommitting irrelevant deprecated ones):</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-keyword">public</SPAN> <SPAN class="code-keyword">interface</SPAN> Query <SPAN class="code-keyword">extends</SPAN> Serializable {
<SPAN class="code-object">String</SPAN> getName();
QueryMetadata getMetaData(EntityResolver resolver);
void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery);
SQLAction createSQLAction(SQLActionVisitor visitor);
}</PRE>
</DIV></DIV>
<UL>
<LI><TT>getName</TT> returns a symbolic name of the query. The name may be used as a key to find queries stored in the DataMap. Some query implementors reuse the name as a QueryMetadata cache key. Generally the name can be null.</LI>
</UL>
<UL>
<LI><TT>getMetaData</TT> is called at various stages of the execution by Cayenne access stack to retrieve query parameters. <TT>EntityResolver</TT> instance is passed to this method, meaning that the query doesn't need to store direct references to Cayenne mapping objects and can resolve them at runtime.</LI>
</UL>
<UL>
<LI><TT>route</TT> is invoked when Cayenne decides which DataNode (database) to use when running a query. Routing decision is made by the Query istelf by calling <TT>QueryRouter.route(..)</TT>. This is an important extension point. For instance all Cayenne &quot;indirect&quot; queries implement this method to create (or somehow resolve) one or more substitute queries and route them instead of self.</LI>
</UL>
<UL>
<LI><TT>createSQLAction</TT> allows to fully customize Query execution at the JDBC level. In the simplest case an implementor calls one of the methods on <TT>SQLActionVisitor</TT> to return a standard action for a specific type of query. However it can provide its own <TT>SQLAction</TT> that accesses the database in some special way.</LI>
</UL>
<H3><A name="CustomizingQueries-IndirectQueries"></A>Indirect Queries</H3>
<P>One customization strategy is an &quot;indirect&quot; query that encapsulates some user-defined operation and in runtime resolves to one or more standard Cayenne queries. Indirect queries can be created from scratch or by extending <TT>org.apache.cayenne.query.IndirectQuery</TT>. As an example lets implement a &quot;CountQuery&quot; query that returns a number of rows in a given table:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-keyword">public</SPAN> class CountQuery <SPAN class="code-keyword">extends</SPAN> IndirectQuery {
<SPAN class="code-keyword">protected</SPAN> <SPAN class="code-object">Class</SPAN> objectClass;
<SPAN class="code-keyword">public</SPAN> CountQuery(<SPAN class="code-object">Class</SPAN> objectClass) {
<SPAN class="code-keyword">this</SPAN>.objectClass = objectClass;
}
<SPAN class="code-keyword">protected</SPAN> Query createReplacementQuery(EntityResolver resolver) {
DbEntity entity = resolver.lookupDbEntity(objectClass);
<SPAN class="code-keyword">if</SPAN> (entity == <SPAN class="code-keyword">null</SPAN>) {
<SPAN class="code-keyword">throw</SPAN> <SPAN class="code-keyword">new</SPAN> CayenneRuntimeException(
<SPAN class="code-quote">&quot;No entity is mapped <SPAN class="code-keyword">for</SPAN> java class: &quot;</SPAN>
+ objectClass.getName());
}
<SPAN class="code-object">String</SPAN> sql = <SPAN class="code-quote">&quot;SELECT #result('count(*)' '<SPAN class="code-object">int</SPAN>' 'C') FROM &quot;</SPAN>
+ entity.getName();
SQLTemplate replacement = <SPAN class="code-keyword">new</SPAN> SQLTemplate(entity, sql);
replacement.setFetchingDataRows(<SPAN class="code-keyword">true</SPAN>);
<SPAN class="code-keyword">return</SPAN> replacement;
}
}</PRE>
</DIV></DIV>
<P>Now you can run the query like that:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java">CountQuery query = <SPAN class="code-keyword">new</SPAN> CountQuery(Artist.class);
DataContext context = DataContext.createDataContext();
Map row = (Map) context.performQuery(query).get(0);
<SPAN class="code-object">System</SPAN>.out.println(<SPAN class="code-quote">&quot;Count: &quot;</SPAN> + row.get(<SPAN class="code-quote">&quot;C&quot;</SPAN>));</PRE>
</DIV></DIV>
<P>For other real-life examples of indirect queries take a look at the source code of the following Cayenne queries: <A href="querychain.html" title="QueryChain">QueryChain</A>, <A href="objectidquery.html" title="ObjectIdQuery">ObjectIdQuery</A>, <A href="relationshipquery.html" title="RelationshipQuery">RelationshipQuery</A>, <A href="namedquery.html" title="NamedQuery">NamedQuery</A>. </P>
<H3><A name="CustomizingQueries-SubclassingStandardQueries"></A>Subclassing Standard Queries</H3>
<P>All standard queries can be subclassed, overriding some of their methods. For instance overriding <TT>route</TT> and/or <TT>createSQLAction</TT> would allow to implement custom callbacks at different points of query lifecycle.</P>