blob: c5f0d9644f79b1b594d208c3a88f2ddbbdab9316 [file] [log] [blame]
Title: Caching Query Results
<P>Cayenne provides a way to cache query results, avoiding unneeded database trips for the frequently used queries. Caching policy is configured per query. Policy can be set via the API or in CayenneModeler.</P>
<DIV class="panelMacro"><TABLE class="noteMacro"><COLGROUP><COL width="24"><COL></COLGROUP><TR><TD valign="top"><IMG src="http://cayenne.apache.org/docs/1.2/images/icons/emoticons/warning.gif" width="16" height="16" align="absmiddle" alt="" border="0"></TD><TD><B>Upgrading to Cayenne 1.2 and Newer</B><BR><TT>org.objectstyle.cayenne.query.GenericSelectQuery</TT> interface that defined cache policy types is deprecated. Cache policies are now a part of the new <TT>org.objectstyle.cayenne.query.QueryMetadata</TT> interface.</TD></TR></TABLE></DIV>
<P>The following cache policies are supported:</P>
<DIV class="table-wrap">
<TABLE class="confluenceTable"><TBODY>
<TR>
<TH class="confluenceTh">Policy</TH>
<TH class="confluenceTh">Cache Scope</TH>
<TH class="confluenceTh">Cache Behavior</TH>
</TR>
<TR>
<TD class="confluenceTd"><EM>(default policy)</EM> <TT>QueryMetadata.NO_CACHE</TT> </TD>
<TD class="confluenceTd">N/A</TD>
<TD class="confluenceTd">Always fetch, never use cache, never save to cache</TD>
</TR>
<TR>
<TD class="confluenceTd"><TT>QueryMetadata.LOCAL_CACHE</TT></TD>
<TD class="confluenceTd">DataContext</TD>
<TD class="confluenceTd">If result is previously cached, use it, otherwise do a fetch and store result in cache for future use</TD>
</TR>
<TR>
<TD class="confluenceTd"><TT>QueryMetadata.LOCAL_CACHE_REFRESH</TT></TD>
<TD class="confluenceTd">DataContext</TD>
<TD class="confluenceTd">Never use cache, alwyas do a fetch and store result in cache for future use</TD>
</TR>
<TR>
<TD class="confluenceTd"><TT>QueryMetadata.SHARED_CACHE</TT></TD>
<TD class="confluenceTd">DataDomain (usually shared by all contexts in the same JVM)</TD>
<TD class="confluenceTd">If result is previously cached, use it, otherwise do a fetch and store result in cache for future use</TD>
</TR>
<TR>
<TD class="confluenceTd"><TT>QueryMetadata.SHARED_CACHE_REFRESH</TT></TD>
<TD class="confluenceTd">DataDomain (usually shared by all contexts in the same JVM)</TD>
<TD class="confluenceTd">Never use cache, alwyas do a fetch and store result in cache for future use</TD>
</TR>
</TBODY></TABLE>
</DIV>
<P>It is important to understand that caching of <B>result lists</B> is done independently from caching of <B>individual DataObjects and DataRows</B>. Therefore the API is different as well. Also cached results lists are not synchronized across VMs (even the shared cache).</P>
<H3><A name="CachingQueryResults-APIforResultCaching"></A>API for Result Caching</H3>
<P>Users must set two Query parameters to configure caching - query <B>name</B> that is used as a key to result cache and query <B>cache policy</B> (one of the policies above). Note that if two unrelated queries have the same name, they will hit the same cache entry. This is not a bug, this is a feature that should be taken into consideration when naming queries.</P>
<P>Below we will create a query and set its caching policy to LOCAL_CACHE:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java">SelectQuery query = <SPAN class="code-keyword">new</SPAN> SelectQuery(Artist.class);
<SPAN class="code-comment">// set query name that will be used as a unique key to perform result caching
</SPAN>query.setName(<SPAN class="code-quote">&quot;MySelect&quot;</SPAN>);
<SPAN class="code-comment">// set local cache policy, meaning the cache will be stored in the DataContext
</SPAN><SPAN class="code-comment">// and not shared between different contexts
</SPAN>query.setCachePolicy(GenericSelectQuery.LOCAL_CACHE);
DataContext context = ... <SPAN class="code-comment">// assume <SPAN class="code-keyword">this</SPAN> exists
</SPAN>
<SPAN class="code-comment">// there is probably no cache at <SPAN class="code-keyword">this</SPAN> point, so the query will hit the database
</SPAN>List objects = context.performQuery(query);
</PRE>
</DIV></DIV>
<P>Reruning the query in the same DataContext at a later time will be much faster as it will be hitting the cache:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java">List objects1 = context.performQuery(query);
</PRE>
</DIV></DIV>
<P>Here we want to refresh the cache, but still keep caching the fresh result:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java">query.setCachePolicy(GenericSelectQuery.LOCAL_CACHE_REFRESH);
List objects2 = context.performQuery(query);
</PRE>
</DIV></DIV>
<P>The example above shows caching with <TT>SelectQuery</TT>, but it works exactly the same way for <TT>SQLTemplate</TT> and <TT>ProcedureQuery</TT>. Similarly <TT>SHARED_CACHE</TT> and <TT>SHARED_CACHE_REFRESH</TT> cache policies create cache shared by all DataDontexts that work with a given DataDomain. </P>
<DIV class="panelMacro"><TABLE class="noteMacro"><COLGROUP><COL width="24"><COL></COLGROUP><TR><TD valign="top"><IMG src="http://cayenne.apache.org/docs/1.2/images/icons/emoticons/warning.gif" width="16" height="16" align="absmiddle" alt="" border="0"></TD><TD><B>Upgrading to Cayenne 1.2 and Newer</B><BR>Cache refreshing API has changed in 1.2. Cayenne 1.1 relied on the use of <TT>SelectQuery.setRefreshingObjects(..)</TT> to determine whether to expire cached result lists. This is no longer the case (setting this flag only refreshes <B>individual objects</B> as it should, and has no effect whatsoever on list caching). Instead caching and cache refreshing is controlled by the cache policy as described above.</TD></TR></TABLE></DIV>
<H3><A name="CachingQueryResults-QueriesMappedinCayenneModeler"></A>Queries Mapped in CayenneModeler</H3>
<P>The easiest way to set up caching is by creating a named query in CayenneModeler with the appropriate caching type.</P>
<P><SPAN class="image-wrap" style=""><IMG src="caching-query-results.data/caching.jpg" style="border: 0px solid black"></SPAN></P>
<P>Then it can be executed via DataContext:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java">List objects1 = context.performQuery(<SPAN class="code-quote">&quot;MyQuery&quot;</SPAN>, <SPAN class="code-keyword">false</SPAN>);
</PRE>
</DIV></DIV>
<P>The second &quot;false&quot; parameter above indicated that if possible, cached result should be used. Now if we want to force refresh, it can be changed to true (for just this invocation - this does not affect the underlying saved query)</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java">List objects2 = context.performQuery(<SPAN class="code-quote">&quot;MyQuery&quot;</SPAN>, <SPAN class="code-keyword">true</SPAN>);
</PRE>
</DIV></DIV>
<P>Note that parameterized named queries will still work correctly with the cache. We've already mentioned that the users must ensure that two queries must have different names if they fetch logically different data. This is NOT the case with queries stored in the DataMap. If you run the same named query with different sets of parameters, Cayenne will internally generate unique cache keys for each distinct parameter set.</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java">Map parameters = Collections.singletonMap(<SPAN class="code-quote">&quot;key&quot;</SPAN>, <SPAN class="code-quote">&quot;value1&quot;</SPAN>);
List objects1 = context.performQuery(<SPAN class="code-quote">&quot;MyQuery&quot;</SPAN>, parameters, <SPAN class="code-keyword">false</SPAN>);
</PRE>
</DIV></DIV>
<P>Now if we run the same query with a different set of parameters, Cayenne will do the right thing and create a separate entry in the cache:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java">Map parameters = Collections.singletonMap(<SPAN class="code-quote">&quot;key&quot;</SPAN>, <SPAN class="code-quote">&quot;value2&quot;</SPAN>);
List objects2 = context.performQuery(<SPAN class="code-quote">&quot;MyQuery&quot;</SPAN>, parameters, <SPAN class="code-keyword">false</SPAN>);
</PRE>
</DIV></DIV>