| 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 "indirect" 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 "indirect" 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 "CountQuery" 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">"No entity is mapped <SPAN class="code-keyword">for</SPAN> java class: "</SPAN> |
| + objectClass.getName()); |
| } |
| |
| <SPAN class="code-object">String</SPAN> sql = <SPAN class="code-quote">"SELECT #result('count(*)' '<SPAN class="code-object">int</SPAN>' 'C') FROM "</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">"Count: "</SPAN> + row.get(<SPAN class="code-quote">"C"</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> |
| |