blob: 3c84c83210dcc49add56272c64f08d1a9f194118 [file] [log] [blame]
Title: Scripting SQLTemplate
<P>SQLTemplate's internal SQL string is a dynamic script that is processed at runtime to generate PreparedStatement SQL code. Dynamic nature of SQLTemplate makes possible a few important things - it allows to bind parameters on the fly; it provides a way to pass extra information to Cayenne that is not included in the SQL text; it supports including/excluding chunks of SQL depending on runtime parameters.</P>
<P>Scripting of SQL strings is done using <A href="http://jakarta.apache.org/velocity" class="external-link" rel="nofollow">Jakarta Velocity</A>. Velocity was chosen primarily for its concise template language (no XML tags within SQL!) that doesn't conflict with the SQL syntax. When creating dynamic SQL template, all standard Velocity directives are available, including <TT>#set</TT>, <TT>#foreach</TT>, <TT>#if</TT>. However due to the nature of the SQL and the need to integrate it with Cayenne runtime, only a few Cayenne custom directives are normally used. These directives (<TT>#bind</TT>..., <TT>#result</TT>, <TT>#chain</TT>, <TT>#chunk</TT>) are described below.</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>Directive Syntax Note</B><BR>Velocity directives start with pound sign (#) and have their parameters separated by space, not comma. E.g. <TT>#bind('SOMESTRING' 'VARCHAR')</TT>.</TD></TR></TABLE></DIV>
<H3><A name="ScriptingSQLTemplate-NamedParameters"></A>Named Parameters</H3>
<P><TT>SQLTemplate.setParameters(java.util.Map)</TT> allows setting a number of named parameters that are used to build parts of the query. During template processing by Velocity all keys in the parameters map are available as variables. For example if the map contains a key &quot;name&quot;, its value can be referenced as &quot;$name&quot; in the template. Value of the parameter will be insterted in the SQL unmodified:</P>
<H5><A name="ScriptingSQLTemplate-NamedParametersExample"></A>Named Parameters Example</H5>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-comment">// build SQLTemplate
</SPAN><SPAN class="code-object">String</SPAN> sql = <SPAN class="code-quote">&quot;delete from $tableName&quot;</SPAN>;
SQLTemplate delete = <SPAN class="code-keyword">new</SPAN> SQLTemplate(Artist.class, sql, <SPAN class="code-keyword">false</SPAN>);
...
<SPAN class="code-comment">// <SPAN class="code-keyword">this</SPAN> will create a query <SPAN class="code-quote">&quot;delete from ARTIST&quot;</SPAN>
</SPAN>update.setParameters(Collections.singletonMap(<SPAN class="code-quote">&quot;tableName&quot;</SPAN>, <SPAN class="code-quote">&quot;ARTIST&quot;</SPAN>));
...
<SPAN class="code-comment">// <SPAN class="code-keyword">this</SPAN> will create a query <SPAN class="code-quote">&quot;delete from PAINTING&quot;</SPAN>
</SPAN>update.setParameters(Collections.singletonMap(<SPAN class="code-quote">&quot;tableName&quot;</SPAN>, <SPAN class="code-quote">&quot;PAINTING&quot;</SPAN>));
</PRE>
</DIV></DIV>
<H3><A name="ScriptingSQLTemplate-DescribingtheResults%23resultDirective"></A>Describing the Results - #result Directive</H3>
<P><TT>#result</TT> directive is used in selecting SQLTemplates to quickly map an arbitrary ResultSet to a DataObject (or a data row with known keys), and also to control Java types of result values. #result directive has a variable number of arguments:</P>
<UL>
<LI><TT>#result(columnName)</TT> - e.g. <TT>#result('ARTIST_NAME')</TT></LI>
<LI><TT>#result(columnName javaType)</TT> - e.g. <TT>#result('DATE_OF_BIRTH' 'java.util.Date')</TT></LI>
<LI><TT>#result(columnName javaType columnAlias)</TT> - e.g. <TT>#result('DATE_OF_BIRTH' 'java.util.Date' 'DOB')</TT> - in this case returned data row will use &quot;DOB&quot; instead of &quot;DATE_OF_BIRTH&quot; for the result value.</LI>
</UL>
<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>Generally &quot;javaType&quot; argument is a fully-qualified Java class name for a given result column. However for simplicity most common Java types used in JDBC can be specified without a package. These include all numeric types, primitives, String, SQL dates, BigDecimal and BigInteger. So &quot;#result('A' 'String')&quot;, &quot;#result('B' 'java.lang.String')&quot; and &quot;#result('C' 'int')&quot; are all valid.</TD></TR></TABLE></DIV>
<P>While &quot;select * from&quot; queries may work just fine, in most cases it is a good idea to explicitly describe results.</P>
<H5><A name="ScriptingSQLTemplate-%23resultExamples"></A>#result Examples</H5>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-comment">// build selecting SQLTemplate
</SPAN><SPAN class="code-object">String</SPAN> sql = <SPAN class="code-quote">&quot;SELECT&quot;</SPAN>
+ <SPAN class="code-quote">&quot; #result('ARTIST_ID' '<SPAN class="code-object">int</SPAN>'),&quot;</SPAN>
+ <SPAN class="code-quote">&quot; #result('ARTIST_NAME' '<SPAN class="code-object">String</SPAN>'),&quot;</SPAN>
+ <SPAN class="code-quote">&quot; #result('DATE_OF_BIRTH' 'java.util.Date')&quot;</SPAN>
+ <SPAN class="code-quote">&quot; FROM ARTIST&quot;</SPAN>;
SQLTemplate select = <SPAN class="code-keyword">new</SPAN> SQLTemplate(Artist.class, sql, <SPAN class="code-keyword">true</SPAN>);
...
DataContext context...;
List artists = context.performQuery(select);
</PRE>
</DIV></DIV>
<H3><A name="ScriptingSQLTemplate-BindingParameters%23bindDirective"></A>Binding Parameters - #bind Directive</H3>
<P>SQLTemplate uses <TT>#bind</TT> directive to indicate value binding. It has the same meaning as PreparedStatement question mark (&quot;?&quot;), however it also tells Cayenne about the nature of the bound value, so it should be used for all bindings. <TT>#bind()</TT> directive can have a variable number of arguments. The following are the valid invocation formats:</P>
<UL>
<LI><TT>#bind(value)</TT> - e.g. <TT>#bind($xyz)</TT> or <TT>#bind('somestring')</TT></LI>
<LI><TT>#bind(value jdbcTypeName)</TT> - e.g. <TT>#bind($xyz 'VARCHAR')</TT>. Second argument is the name of JDBC type for this binding. Valid JDBC types are defined in java.sql.Types class. This form is the the most common and useful. It is generally preferred to the single argument form, as it explicitly tells what type of JDBC value this binding is.</LI>
<LI><TT>#bind(value jdbcTypeName precision)</TT> - e.g. <TT>#bind($xyz 'DECIMAL' 2)</TT></LI>
</UL>
<H5><A name="ScriptingSQLTemplate-%23binddirectiveexample"></A>#bind directive example</H5>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-comment">// build SQLTemplate
</SPAN><SPAN class="code-object">String</SPAN> sql = <SPAN class="code-quote">&quot;update ARTIST set ARTIST_NAME = #bind($name) where ARTIST_ID = #bind($id)&quot;</SPAN>;
SQLTemplate update = <SPAN class="code-keyword">new</SPAN> SQLTemplate(Artist.class, sql, <SPAN class="code-keyword">false</SPAN>);
...
<SPAN class="code-comment">// set parameters and run it...
</SPAN>Map parameters = <SPAN class="code-keyword">new</SPAN> HashMap();
parameters.put(<SPAN class="code-quote">&quot;name&quot;</SPAN>, <SPAN class="code-quote">&quot;Publo Picasso&quot;</SPAN>);
parameters.put(<SPAN class="code-quote">&quot;id&quot;</SPAN>, <SPAN class="code-keyword">new</SPAN> <SPAN class="code-object">Integer</SPAN>(1001));
update.setParameters(parameters);
DataContext context...;
context.performNonSelectingQuery(update);
</PRE>
</DIV></DIV>
<P>SQLTemplate also supports binding Collections for building <TT>IN ( .. )</TT> queries. In any of the <TT>#bind</TT> invocation formats above, you may specify a Collection of values in place of <TT>value</TT>, and Cayenne will automatically expand it.</P>
<H5><A name="ScriptingSQLTemplate-Collectionexample"></A>Collection example</H5>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-comment">// build SQLTemplate
</SPAN><SPAN class="code-object">String</SPAN> sql = &quot;SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST WHERE ARTIST_NAME IN (#bind($names))
SQLTemplate select = <SPAN class="code-keyword">new</SPAN> SQLTemplate(Artist.class, sql, <SPAN class="code-keyword">false</SPAN>);
...
<SPAN class="code-comment">// set parameters and run it...
</SPAN>Map parameters = <SPAN class="code-keyword">new</SPAN> HashMap();
parameters.put(<SPAN class="code-quote">&quot;names&quot;</SPAN>, Arrays.asList(<SPAN class="code-keyword">new</SPAN> <SPAN class="code-object">String</SPAN>[] { <SPAN class="code-quote">&quot;Monet&quot;</SPAN>, <SPAN class="code-quote">&quot;Publo Picasso&quot;</SPAN>}));
select.setParameters(parameters);
DataContext context...;
List artists = context.performQuery(select);
</PRE>
</DIV></DIV>
<H3><A name="ScriptingSQLTemplate-NullValuesinBindings%23bindEqualand%23bindNotEqualDirectives"></A>Null Values in Bindings - #bindEqual and #bindNotEqual Directives</H3>
<P>Sometimes when a parameter is NULL, SQL code has to be changed. For example, instead of &quot;WHERE COLUMN = ?&quot;, PreparedStatement should be rewritten as &quot;WHERE COLUMN IS NULL&quot;, and instead of &quot;WHERE COLUMN &lt;&gt; ?&quot; - as &quot;WHERE COLUMN IS NOT NULL&quot;. <TT>#bindEqual</TT> and <TT>#bindNotEqual</TT> directives are used to dynamically generate correct SQL string in this case. Their semantics is the same as #bind directive above, except that they do not require &quot;=&quot;, &quot;!=&quot; or &quot;&lt;&gt;&quot; in front of them:</P>
<UL>
<LI><TT>#bindEqual(value)</TT>, <TT>#bindNotEqual(value)</TT></LI>
<LI><TT>#bindEqual(value jdbcTypeName)</TT>, <TT>#bindNotEqual(value jdbcTypeName)</TT></LI>
<LI><TT>#bindEqual(value jdbcTypeName precision)</TT>, <TT>#bindNotEqual(value jdbcTypeName precision)</TT></LI>
</UL>
<H5><A name="ScriptingSQLTemplate-NullValueExamples"></A>Null Value Examples</H5>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-comment">// build SQLTemplate
</SPAN><SPAN class="code-comment">// note that <SPAN class="code-quote">&quot;=&quot;</SPAN> is ommitted <SPAN class="code-keyword">for</SPAN> the second binding, since it is a part of the directive
</SPAN><SPAN class="code-object">String</SPAN> sql = &quot;update ARTIST set ARTIST_NAME = #bind($name) where ARTIST_ID #bindEqual($id);
SQLTemplate update = <SPAN class="code-keyword">new</SPAN> SQLTemplate(Artist.class, sql, <SPAN class="code-keyword">false</SPAN>);
...
<SPAN class="code-comment">// set parameters and run it...
</SPAN>Map parameters = <SPAN class="code-keyword">new</SPAN> HashMap();
parameters.put(<SPAN class="code-quote">&quot;name&quot;</SPAN>, <SPAN class="code-quote">&quot;Publo Picasso&quot;</SPAN>);
parameters.put(<SPAN class="code-quote">&quot;id&quot;</SPAN>, <SPAN class="code-keyword">new</SPAN> <SPAN class="code-object">Integer</SPAN>(1001));
update.setParameters(parameters);
DataContext context...;
<SPAN class="code-comment">// after binding processing PrepapredStatement SQL will look like
</SPAN><SPAN class="code-comment">// <SPAN class="code-quote">&quot;update ARTIST set ARTIST_NAME = ? where ARTIST_ID = ?&quot;</SPAN>
</SPAN>context.performNonSelectingQuery(update);
</PRE>
</DIV></DIV>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-comment">// build SQLTemplate
</SPAN><SPAN class="code-comment">// note that <SPAN class="code-quote">&quot;!=&quot;</SPAN> is ommitted <SPAN class="code-keyword">for</SPAN> the second binding, since it is a part of the directive
</SPAN><SPAN class="code-object">String</SPAN> sql = <SPAN class="code-quote">&quot;update ARTIST set ARTIST_NAME = #bind($name) where ARTIST_ID #bindNotEqual($id)&quot;</SPAN>;
SQLTemplate update = <SPAN class="code-keyword">new</SPAN> SQLTemplate(Artist.class, sql, <SPAN class="code-keyword">false</SPAN>);
...
<SPAN class="code-comment">// set parameters and run it...
</SPAN>Map parameters = <SPAN class="code-keyword">new</SPAN> HashMap();
parameters.put(<SPAN class="code-quote">&quot;name&quot;</SPAN>, <SPAN class="code-quote">&quot;Publo Picasso&quot;</SPAN>);
parameters.put(<SPAN class="code-quote">&quot;id&quot;</SPAN>, <SPAN class="code-keyword">null</SPAN>);
update.setParameters(parameters);
DataContext context...;
<SPAN class="code-comment">// after binding processing PrepapredStatement SQL will look like
</SPAN><SPAN class="code-comment">// <SPAN class="code-quote">&quot;update ARTIST set ARTIST_NAME = ? where ARTIST_ID IS NOT NULL&quot;</SPAN>
</SPAN>context.performNonSelectingQuery(update);
</PRE>
</DIV></DIV>
<H3><A name="ScriptingSQLTemplate-BuildingDynamicSQL%23chainand%23chunkDirectives"></A>Building Dynamic SQL - #chain and #chunk Directives</H3>
<P>Often it is desirable to exclude parts of the WHERE clause if some parameters are null or not set. This task is not trivial considering the semantics of a SQL statement. Consider this fairly simple example:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-object">String</SPAN> sql = <SPAN class="code-quote">&quot;SELECT DISTINCT&quot;</SPAN>
+ <SPAN class="code-quote">&quot; #result('ARTIST_ID' '<SPAN class="code-object">int</SPAN>'),&quot;</SPAN>
+ <SPAN class="code-quote">&quot; #result('ARTIST_NAME' '<SPAN class="code-object">String</SPAN>'),&quot;</SPAN>
+ <SPAN class="code-quote">&quot; #result('DATE_OF_BIRTH' 'java.util.Date')&quot;</SPAN>
+ <SPAN class="code-quote">&quot; FROM ARTIST t0&quot;</SPAN>
+ <SPAN class="code-quote">&quot; WHERE ARTIST_NAME LIKE #bind($name)&quot;</SPAN>
+ <SPAN class="code-quote">&quot; OR ARTIST_ID &gt; #bind($id)&quot;</SPAN>;
SQLTemplate select = <SPAN class="code-keyword">new</SPAN> SQLTemplate(Artist.class, sql, <SPAN class="code-keyword">true</SPAN>);
</PRE>
</DIV></DIV>
<P>It would be nice to exclude ARTIST_NAME matching if &quot;name&quot; parameter is null, exclude ARTIST_ID matching if &quot;id&quot; is null, and exclude the whole WHERE clause if both are null. <TT>#chain</TT> and <TT>#chunk</TT> directives are used for this purpose. Each logical piece is wrapped in a conditional &quot;chunk&quot;, and a number of chunks are grouped in a chain. If chain contains no chunks it doesn't render anything enclosed in it.</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-object">String</SPAN> sql = <SPAN class="code-quote">&quot;SELECT DISTINCT&quot;</SPAN>
+ <SPAN class="code-quote">&quot; #result('ARTIST_ID' '<SPAN class="code-object">int</SPAN>'),&quot;</SPAN>
+ <SPAN class="code-quote">&quot; #result('ARTIST_NAME' '<SPAN class="code-object">String</SPAN>'),&quot;</SPAN>
+ <SPAN class="code-quote">&quot; #result('DATE_OF_BIRTH' 'java.util.Date')&quot;</SPAN>
+ <SPAN class="code-quote">&quot; FROM ARTIST t0&quot;</SPAN>
+ &quot; #chain('OR' 'WHERE') <SPAN class="code-comment">// start chain prefixed by WHERE,
</SPAN> <SPAN class="code-comment">// and joined by OR
</SPAN> + <SPAN class="code-quote">&quot; #chunk($name) ARTIST_NAME LIKE #bind($name) #end&quot;</SPAN> <SPAN class="code-comment">// ARTIST_NAME <SPAN class="code-quote">&quot;chunk&quot;</SPAN>
</SPAN> + <SPAN class="code-quote">&quot; #chunk($id) ARTIST_ID &gt; #bind($id) #end&quot;</SPAN> <SPAN class="code-comment">// ARTIST_ID <SPAN class="code-quote">&quot;chunk&quot;</SPAN>
</SPAN> + <SPAN class="code-quote">&quot; #end&quot;</SPAN>; <SPAN class="code-comment">// end of chain
</SPAN>SQLTemplate select = <SPAN class="code-keyword">new</SPAN> SQLTemplate(Artist.class, sql, <SPAN class="code-keyword">true</SPAN>);
</PRE>
</DIV></DIV>
<H3><A name="ScriptingSQLTemplate-BindingObjectIdValues."></A>Binding ObjectId Values.</H3>
<P>TO BE DONE</P>